Skip to content
truescrape

Facebook API

The TrueScrape Facebook API exposes 22 public endpoints covering Ad details, Ad video transcript and Find advertisers running ads. Every call is a GET against public, logged-out pages and returns the same unified schema as every other platform here. Calls cost 1–2 credits each. A cache hit is free, and a failed or empty response is never charged.

Endpoints

EndpointReturnsCreditsCacheableBatchable
/v1/facebook/ad-library/adAd details1yesyes
/v1/facebook/ad-library/ad-transcriptAd video transcript1yesyes
/v1/facebook/ad-library/advertisersFind advertisers running ads1yesyes
/v1/facebook/ad-library/page-adsAll ads for one advertiser2yesyes
/v1/facebook/ad-library/searchSearch the Meta Ad Library2yesyes
/v1/facebook/city-eventsGet the events of a city1yesyes
/v1/facebook/comment-repliesReplies to a comment1yesyes
/v1/facebook/eventEvent details1yesyes
/v1/facebook/events/searchSearch public events1yesyes
/v1/facebook/groupPublic group info1yesyes
/v1/facebook/group-postsPublic group posts1yesyes
/v1/facebook/marketplace/itemMarketplace listing details1yesyes
/v1/facebook/marketplace/locationsFind a Marketplace location1yesyes
/v1/facebook/marketplace/searchSearch Marketplace listings1yesyes
/v1/facebook/page-postsPublic Page posts1yesyes
/v1/facebook/page-reelsPublic Page videos and reels1yesyes
/v1/facebook/postSingle post, video, or reel1yesyes
/v1/facebook/post-commentsComments on a post1yesyes
/v1/facebook/post-transcriptVideo transcript1yesyes
/v1/facebook/profilePublic Page profile1yesyes
/v1/facebook/profile-eventsEvents on a Page1yesyes
/v1/facebook/profile-photosPhotos on a Page1yesyes

Reference

GET/v1/facebook/ad-library/ad1 creditcacheablebatchable

Ad details

One ad from the Meta Ad Library by its archive id. Meta does not expose the archive as an addressable node for every ad; pass page_id as well and we find it by scanning that advertiser's archive instead.

ParameterTypeRequiredDescription
idstringyesAd archive id, or an Ad Library URL containing ?id=
page_idstringnoThe advertiser's page id. Enables the archive-scan fallback.
countrystringnoReached country used by the fallback scandefaults to US
urlstringnoAd archive id, or an Ad Library URL containing ?id=

Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).

Returns one Ad.

Fields
FieldTypePresentDescription
platformone of 47 stringsalways
idstringalways
advertiserIdstring | nullalways
advertiserNamestring | nullalways
urlstring | nullalwaysLink to the ad's page in the platform's ad library.
headlinestring | nullalways
bodystring | nullalways
ctaTextstring | nullalwaysText of the call-to-action button, such as "Shop now".
linkUrlstring | nullalwaysWhere the ad links to. Some ad libraries expose only the destination's domain.
creativeTypestring | nullalwaysFormat of the ad creative, such as `video` or `image`, as the ad library labels it.
imageUrlsstring[]always
videoUrlsstring[]always
platformsstring[]alwaysPlatforms the ad was shown on, in lowercase, such as `facebook` or `instagram`.
countriesstring[]alwaysCountries the ad ran in or targeted, usually as two-letter country codes.
languagesstring[]always
startedAtstring | nullalways
endedAtstring | nullalwaysWhen the ad stopped running. Null while it is still running, or when the ad library reports no end date.
isActiveboolean | nullalways
impressionsLowernumber | nullalwaysLower bound of the impressions range the ad library reports. Libraries publish a range, not an exact figure.
impressionsUppernumber | nullalwaysUpper bound of the impressions range the ad library reports.
spendLowernumber | nullalwaysLower bound of the reported spend range, in `currency`.
spendUppernumber | nullalwaysUpper bound of the reported spend range, in `currency`.
currencystring | nullalwaysCurrency of the spend range, as a code such as `USD`.
fetchedAtstringalwaysWhen this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch.
curl
curl "$API/v1/facebook/ad-library/ad?id=%3Cid%3E&cache_max_age=7d" \
  -H "x-api-key: $KEY"
TypeScript
const query = new URLSearchParams({
  id: '<id>',
  cache_max_age: '7d',
});

const response = await fetch(`${API}/v1/facebook/ad-library/ad?${query}`, {
  headers: { 'x-api-key': KEY },
});

const body = await response.json();
if (!body.success) throw new Error(body.error.code);

// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);
Python
Python
import os, httpx

r = httpx.get(
    f"{os.environ['API']}/v1/facebook/ad-library/ad",
    params={
        "id": "<id>",
        "cache_max_age": "7d",
    },
    headers={"x-api-key": os.environ["KEY"]},
    timeout=30,
)

body = r.json()
if not body["success"]:
    raise RuntimeError(body["error"]["code"])

print(body["data"], body["meta"]["creditsCharged"])
Response codes
200Success
400invalid_request. Not charged
401missing_api_key / invalid_api_key / revoked_api_key. Never a billing error
402insufficient_credits. The key is valid, the balance is not
429daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged
501not_configured. This deployment is not set up to serve this endpoint. Not charged.
502upstream_schema_drift. Not charged
503upstream_unavailable. We could not get this from the platform. Not charged
504upstream_timeout. Not charged
Try it here
GET/v1/facebook/ad-library/ad
Open the full playground
GET/v1/facebook/ad-library/ad-transcript1 creditcacheablebatchable

Ad video transcript

Retrieves a transcript for a single Facebook Ad Library video ad by ID or URL, read from the caption track Facebook publishes for the ad's video (source: "captions"). Transcription of ad videos without captions is coming soon; until then those answer not_configured (501) and are not charged. Credits are only deducted when a transcript is actually returned. **Required:** Pass exactly one of id or url.

ParameterTypeRequiredDescription
idstringnoAd archive id, or an Ad Library URL containing ?id=
urlstringnoAn Ad Library URL containing ?id=
languagestringnoPreferred caption locale

Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).

Returns one Transcript.

Fields
FieldTypePresentDescription
platformone of 47 stringsalways
postIdstringalways
urlstring | nullalways
languagestring | nullalwaysThe transcript's language as the platform labels it, usually a language code such as `en`.
isAutoGeneratedboolean | nullalwaysWhether the platform generated the captions automatically, by speech recognition or machine translation, rather than a person writing them.
textstringalwaysThe full transcript as a single block of text.
cuesobject[]alwaysTimed segments of the transcript.
cues[].startnumberalwaysSeconds from the start of the media.
cues[].endnumber | nullalwaysSeconds from the start of the media. Null when the platform gives no end time.
cues[].textstringalways
durationSecondsnumber | nullalways
sourcecaptions | asr | nullalwaysWhere the transcript came from. `captions`: the platform's own caption track, whether a person wrote it or the platform generated it (see `isAutoGenerated`). `asr`: produced by speech recognition on the audio. Null when this cannot be told.
fetchedAtstringalwaysWhen this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch.
curl
curl "$API/v1/facebook/ad-library/ad-transcript?cache_max_age=7d" \
  -H "x-api-key: $KEY"
TypeScript
const query = new URLSearchParams({
  cache_max_age: '7d',
});

const response = await fetch(`${API}/v1/facebook/ad-library/ad-transcript?${query}`, {
  headers: { 'x-api-key': KEY },
});

const body = await response.json();
if (!body.success) throw new Error(body.error.code);

// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);
Python
Python
import os, httpx

r = httpx.get(
    f"{os.environ['API']}/v1/facebook/ad-library/ad-transcript",
    params={
        "cache_max_age": "7d",
    },
    headers={"x-api-key": os.environ["KEY"]},
    timeout=30,
)

body = r.json()
if not body["success"]:
    raise RuntimeError(body["error"]["code"])

print(body["data"], body["meta"]["creditsCharged"])
Response codes
200Success
400invalid_request. Not charged
401missing_api_key / invalid_api_key / revoked_api_key. Never a billing error
402insufficient_credits. The key is valid, the balance is not
429daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged
501not_configured. This deployment is not set up to serve this endpoint. Not charged.
502upstream_schema_drift. Not charged
503upstream_unavailable. We could not get this from the platform. Not charged
504upstream_timeout. Not charged
Try it here
GET/v1/facebook/ad-library/ad-transcript
Open the full playground
GET/v1/facebook/group1 creditcacheablebatchable

Public group info

Name, member count, privacy setting and description for a public Facebook group. Member count lands in followerCount (a group's equivalent of an audience size) and the privacy setting in category. Facebook rounds that count on large groups ("12K members"); raw.memberCountIsApproximate says whether the number you got was rounded.

ParameterTypeRequiredDescription
urlstringyesGroup URL, group id, or group slug

Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).

Returns one Creator.

Fields
FieldTypePresentDescription
platformone of 47 stringsalways
idstringalwaysThe platform's own id for the account, such as a channel id or a numeric user id, always as a string.
handlestring | nullalwaysThe account handle without the leading @. Null when the platform has no handles.
displayNamestring | nullalways
biostring | nullalways
avatarUrlstring | nullalways
bannerUrlstring | nullalways
urlstring | nullalways
verifiedboolean | nullalwaysWhether the platform shows a verification badge on the account. Null where the platform has no public badge or does not expose it.
followerCountnumber | nullalwaysFollowers, subscribers, or the platform's nearest equivalent. Null when the account hides the count or the platform does not expose it.
followingCountnumber | nullalways
postCountnumber | nullalwaysPosts, videos, tracks, repositories, or the platform's nearest equivalent that the account has published.
viewCountnumber | nullalwaysLifetime views across the account, where the platform exposes a total.
likeCountnumber | nullmay be absentTotal likes received across all of the creator's posts. Distinct from `viewCount`. Absent when the platform does not expose it.
isPrivateboolean | nullalways
isBusinessboolean | nullalwaysWhether the platform classifies the account as a business or organisation rather than a person or creator. Null where the platform draws no such line or does not expose it. On Facebook every Page counts, including Pages for public figures.
categorystring | nullalwaysA label the platform attaches to the account, such as a business category, a genre or an account type. What it holds varies by platform.
locationstring | nullalwaysLocation as the platform shows it: a city, a country, a region code or an address, depending on the platform.
externalLinksstring[]alwaysLinks the account lists on its profile, such as a website or other social accounts.
createdAtstring | nullalwaysWhen the account was created. Usually an ISO 8601 timestamp; some platforms expose only the date as shown on the profile.
fetchedAtstringalwaysWhen this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch.
curl
curl "$API/v1/facebook/group?url=8XkPqR2nLvE&cache_max_age=7d" \
  -H "x-api-key: $KEY"
TypeScript
const query = new URLSearchParams({
  url: '8XkPqR2nLvE',
  cache_max_age: '7d',
});

const response = await fetch(`${API}/v1/facebook/group?${query}`, {
  headers: { 'x-api-key': KEY },
});

const body = await response.json();
if (!body.success) throw new Error(body.error.code);

// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);
Python
Python
import os, httpx

r = httpx.get(
    f"{os.environ['API']}/v1/facebook/group",
    params={
        "url": "8XkPqR2nLvE",
        "cache_max_age": "7d",
    },
    headers={"x-api-key": os.environ["KEY"]},
    timeout=30,
)

body = r.json()
if not body["success"]:
    raise RuntimeError(body["error"]["code"])

print(body["data"], body["meta"]["creditsCharged"])
Response codes
200Success
400invalid_request. Not charged
401missing_api_key / invalid_api_key / revoked_api_key. Never a billing error
402insufficient_credits. The key is valid, the balance is not
429daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged
501not_configured. This deployment is not set up to serve this endpoint. Not charged.
502upstream_schema_drift. Not charged
503upstream_unavailable. We could not get this from the platform. Not charged
504upstream_timeout. Not charged
Try it here
GET/v1/facebook/group
Open the full playground
GET/v1/facebook/group-posts1 creditcacheablebatchable

Public group posts

Recent posts from a public Facebook group, with reaction, comment and share counts where Facebook renders them. Only the newest few stories render to a logged-out visitor, the same limit /v1/facebook/page-posts has. Private groups return an empty result; their posts are not public and we do not log in.

ParameterTypeRequiredDescription
urlstringyesGroup URL, group id, or group slug

Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).

Returns a list of Post in data.items.

Fields
FieldTypePresentDescription
platformone of 47 stringsalways
idstringalways
typevideo | short | image | carousel | text | live | story | reel | audio | album | episode | unknownalwaysWhat kind of post this is. `short` and `reel` are short-form video formats, `live` is a live stream or a recording of one, `carousel` holds several images or videos, `audio` is a music track, `album` is an album, `episode` is a podcast episode, and `unknown` covers anything else.
urlstring | nullalways
titlestring | nullalways
textstring | nullalwaysThe post's text: a caption, a description or the message body.
authorIdstring | nullalways
authorHandlestring | nullalways
authorNamestring | nullalways
thumbnailUrlstring | nullalways
mediaUrlsstring[]always
durationSecondsnumber | nullalways
viewCountnumber | nullalwaysViews. Null means the platform did not expose a count, which is not the same as zero.
likeCountnumber | nullalwaysLikes, or the platform's nearest equivalent. Null means not exposed, not zero.
commentCountnumber | nullalwaysComments. Null means not exposed, not zero.
shareCountnumber | nullalwaysShares, reposts, or the platform's nearest equivalent. Null means not exposed, not zero.
hashtagsstring[]alwaysHashtags or the platform's own topic tags, without the leading #.
mentionsstring[]alwaysHandles mentioned in the post text, without the leading @.
taggedUsersstring[]may be absentUsernames tagged in the media itself, as distinct from `mentions`, which come from the caption text. Absent when the platform does not report tags; an empty array means it reports none.
isSponsoredboolean | nullalwaysWhether the platform labels the post as an ad, promoted content or a paid partnership.
publishedAtstring | nullalwaysWhen the post was published. Usually an ISO 8601 timestamp; some platforms expose only a date or relative text such as "3 days ago".
fetchedAtstringalwaysWhen this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch.
curl
curl "$API/v1/facebook/group-posts?url=8XkPqR2nLvE&cache_max_age=7d" \
  -H "x-api-key: $KEY"
TypeScript
const query = new URLSearchParams({
  url: '8XkPqR2nLvE',
  cache_max_age: '7d',
});

const response = await fetch(`${API}/v1/facebook/group-posts?${query}`, {
  headers: { 'x-api-key': KEY },
});

const body = await response.json();
if (!body.success) throw new Error(body.error.code);

// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);
Python
Python
import os, httpx

r = httpx.get(
    f"{os.environ['API']}/v1/facebook/group-posts",
    params={
        "url": "8XkPqR2nLvE",
        "cache_max_age": "7d",
    },
    headers={"x-api-key": os.environ["KEY"]},
    timeout=30,
)

body = r.json()
if not body["success"]:
    raise RuntimeError(body["error"]["code"])

print(body["data"], body["meta"]["creditsCharged"])
Response codes
200Success
400invalid_request. Not charged
401missing_api_key / invalid_api_key / revoked_api_key. Never a billing error
402insufficient_credits. The key is valid, the balance is not
429daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged
501not_configured. This deployment is not set up to serve this endpoint. Not charged.
502upstream_schema_drift. Not charged
503upstream_unavailable. We could not get this from the platform. Not charged
504upstream_timeout. Not charged
Try it here
GET/v1/facebook/group-posts
Open the full playground
GET/v1/facebook/marketplace/item1 creditcacheablebatchable

Marketplace listing details

One public Facebook Marketplace listing: title, full description, price, location, delivery options and every photo. Facebook does not render the seller to a logged-out visitor, so seller identity is not returned.

ParameterTypeRequiredDescription
urlstringyesMarketplace item URL or numeric listing id

Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).

Returns an object with platform, id, title, url, description, priceText, priceAmount, originalPriceText, isSold, isPending, isLive, locationText, city, state, categoryId, deliveryTypes, thumbnailUrl, imageUrls, listedAt, fetchedAt.

Fields
FieldTypePresentDescription
platform"facebook"always
idstringalways
titlestring | nullalways
urlstringalways
descriptionstring | nullalways
priceTextstring | nullalwaysAs Facebook formatted it, e.g. "$150", carrying the currency symbol.
priceAmountnumber | nullalwaysNumeric amount in the listing's own currency.
originalPriceTextstring | nullalwaysThe pre-discount price, when the seller marked one down.
isSoldboolean | nullalways
isPendingboolean | nullalways
isLiveboolean | nullalways
locationTextstring | nullalways
citystring | nullalways
statestring | nullalways
categoryIdstring | nullalways
deliveryTypesstring[]always
thumbnailUrlstring | nullalways
imageUrlsstring[]alwaysUp to ten, the first of which is `thumbnailUrl`.
listedAtstring | nullalways
fetchedAtstringalways
curl
curl "$API/v1/facebook/marketplace/item?url=8XkPqR2nLvE&cache_max_age=7d" \
  -H "x-api-key: $KEY"
TypeScript
const query = new URLSearchParams({
  url: '8XkPqR2nLvE',
  cache_max_age: '7d',
});

const response = await fetch(`${API}/v1/facebook/marketplace/item?${query}`, {
  headers: { 'x-api-key': KEY },
});

const body = await response.json();
if (!body.success) throw new Error(body.error.code);

// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);
Python
Python
import os, httpx

r = httpx.get(
    f"{os.environ['API']}/v1/facebook/marketplace/item",
    params={
        "url": "8XkPqR2nLvE",
        "cache_max_age": "7d",
    },
    headers={"x-api-key": os.environ["KEY"]},
    timeout=30,
)

body = r.json()
if not body["success"]:
    raise RuntimeError(body["error"]["code"])

print(body["data"], body["meta"]["creditsCharged"])
Response codes
200Success
400invalid_request. Not charged
401missing_api_key / invalid_api_key / revoked_api_key. Never a billing error
402insufficient_credits. The key is valid, the balance is not
429daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged
501not_configured. This deployment is not set up to serve this endpoint. Not charged.
502upstream_schema_drift. Not charged
503upstream_unavailable. We could not get this from the platform. Not charged
504upstream_timeout. Not charged
Try it here
GET/v1/facebook/marketplace/item
Open the full playground
GET/v1/facebook/marketplace/search1 creditcacheablebatchable

Search Marketplace listings

Public Facebook Marketplace listings matching a search term, with price, condition flags, location and photos. location takes a city slug or a location id from /v1/facebook/marketplace/locations; without one, Facebook picks the city it places the request in, which you do not control. Pass location for results you can repeat.

ParameterTypeRequiredDescription
querystringyesWhat to search for, e.g. "mountain bike"
locationstringnoCity slug ("nyc", "london") or a location id from /v1/facebook/marketplace/locations
min_pricenumbernoLowest price, in the location's currency
max_pricenumbernoHighest price, in the location's currency
days_since_listednumbernoOnly listings posted in the last N days
radius_kmnumbernoSearch radius around the location, in kilometres
sort_bybest_match | creation_time_descend | price_ascend | price_descend | distance_ascendnoFacebook's own sort keys
delivery_methodlocal_pick_up | shippingnoRestrict to collection-only or shipped listings

Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).

Returns a list of object in data.items.

Fields
FieldTypePresentDescription
platform"facebook"always
idstringalways
titlestring | nullalways
urlstringalways
descriptionstring | nullalways
priceTextstring | nullalwaysAs Facebook formatted it, e.g. "$150", carrying the currency symbol.
priceAmountnumber | nullalwaysNumeric amount in the listing's own currency.
originalPriceTextstring | nullalwaysThe pre-discount price, when the seller marked one down.
isSoldboolean | nullalways
isPendingboolean | nullalways
isLiveboolean | nullalways
locationTextstring | nullalways
citystring | nullalways
statestring | nullalways
categoryIdstring | nullalways
deliveryTypesstring[]always
thumbnailUrlstring | nullalways
imageUrlsstring[]alwaysUp to ten, the first of which is `thumbnailUrl`.
listedAtstring | nullalways
fetchedAtstringalways
curl
curl "$API/v1/facebook/marketplace/search?query=ai%20agents&cache_max_age=7d" \
  -H "x-api-key: $KEY"
TypeScript
const query = new URLSearchParams({
  query: 'ai agents',
  cache_max_age: '7d',
});

const response = await fetch(`${API}/v1/facebook/marketplace/search?${query}`, {
  headers: { 'x-api-key': KEY },
});

const body = await response.json();
if (!body.success) throw new Error(body.error.code);

// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);
Python
Python
import os, httpx

r = httpx.get(
    f"{os.environ['API']}/v1/facebook/marketplace/search",
    params={
        "query": "ai agents",
        "cache_max_age": "7d",
    },
    headers={"x-api-key": os.environ["KEY"]},
    timeout=30,
)

body = r.json()
if not body["success"]:
    raise RuntimeError(body["error"]["code"])

print(body["data"], body["meta"]["creditsCharged"])
Response codes
200Success
400invalid_request. Not charged
401missing_api_key / invalid_api_key / revoked_api_key. Never a billing error
402insufficient_credits. The key is valid, the balance is not
429daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged
501not_configured. This deployment is not set up to serve this endpoint. Not charged.
502upstream_schema_drift. Not charged
503upstream_unavailable. We could not get this from the platform. Not charged
504upstream_timeout. Not charged
Try it here
GET/v1/facebook/marketplace/search
Open the full playground

Questions

How much does the Facebook API cost?
Calls cost 1–2 credits each. Every endpoint is cacheable, and a cache hit costs nothing. A failed request and an empty result are both free, on every endpoint.
Does the Facebook API need a login or cookies?
No. Every endpoint reads public, logged-out pages only — no account, no cookies, no session. An API key identifies your own TrueScrape account and nothing else.
Can I fetch many Facebook targets in one request?
Yes. 22 of 22 are batchable: one POST to /v1/jobs/batch takes many targets and returns a job id to poll.
Can I watch Facebook endpoints for changes?
Yes, 15 of 22. A subscription polls on your schedule and fires your webhook only when the content changed; the rest carry x-subscribable: false in the spec.