Search the Facebook Ad Library

A keyword and a country return a page of public ads from the Meta Ad Library.

A Meta Ad Library API, for someone building a tool, is a keyword search over ads the library already shows the public. GET /v1/facebook/ad-library/search does that. It is a SocialScrape route in beta. It is not Meta's official API, and it does not create or edit ads.

Send the key in x-api-key. See authentication.

Parameters#

query is required. keyword and q are aliases. region is the one country to search. Use a two-letter code, or ALL for every country. The recorded search uses query=nike and region=US.

sort is relevance, impressions or newest. impressions is most impressions first. newest is most recent. Changing sort restarts the cursor chain. match is any_words or exact_phrase. ad_type is all or political (political and issue ads only). status is active, inactive or all. active_status is an alias of status. media_type is all, image, video, meme, image_and_meme or none. The reference says meme is Meta's word for text on an image. language is a 2-letter code such as EN, ES or FR. start_date and end_date are the impressions window, YYYY-MM-DD.

cursor pages. The route is also available as POST with a JSON body, for cursors too long for a URL. The recorded GET below is a later page: it includes a cursor. For the first page, send query and region only.

A first page for the recorded query:

curl -G "https://api.socialscrape.io/v1/facebook/ad-library/search" \
  -H "x-api-key: $SOCIALSCRAPE_API_KEY" \
  --data-urlencode "query=nike" \
  --data-urlencode "region=US"

The recorded request, cursor included, in three languages:

curl
curl -G "https://api.socialscrape.io/v1/facebook/ad-library/search" \
  -H "x-api-key: $SOCIALSCRAPE_API_KEY" \
  --data-urlencode "query=nike" \
  --data-urlencode "region=US" \
  --data-urlencode "cursor=eyJrIjoiZmJhZHMiLCJjIjoiQVFIVHR6TWN1R1ZGekcxSF85UzBpNjN5VTVMYjFzZHFDZmZaRjRYeHRHS205dXlVNG1BTUpkT2F3VEg1YUM4VWdvV2EifQ"
Python
import os
import requests

params = {
    "query": "nike",
    "region": "US",
    "cursor": "eyJrIjoiZmJhZHMiLCJjIjoiQVFIVHR6TWN1R1ZGekcxSF85UzBpNjN5VTVMYjFzZHFDZmZaRjRYeHRHS205dXlVNG1BTUpkT2F3VEg1YUM4VWdvV2EifQ",
}
res = requests.get(
    "https://api.socialscrape.io/v1/facebook/ad-library/search",
    headers={"x-api-key": os.environ["SOCIALSCRAPE_API_KEY"]},
    params=params,
)
body = res.json()

if body["ok"]:
    print(body["data"])
else:
    print(body["error"]["code"], body["error"]["message"])
TypeScript
const url = new URL("https://api.socialscrape.io/v1/facebook/ad-library/search");
url.search = new URLSearchParams({
  "query": "nike",
  "region": "US",
  "cursor": "eyJrIjoiZmJhZHMiLCJjIjoiQVFIVHR6TWN1R1ZGekcxSF85UzBpNjN5VTVMYjFzZHFDZmZaRjRYeHRHS205dXlVNG1BTUpkT2F3VEg1YUM4VWdvV2EifQ"
}).toString();
const res = await fetch(url, {
  headers: { "x-api-key": process.env.SOCIALSCRAPE_API_KEY! },
});
const body = await res.json();

if (body.ok) {
  console.log(body.data);
} else {
  console.error(body.error.code, body.error.message);
}

Check ok. invalid_params and not_found are not retryable. rate_limited is.

What an ad contains#

data.items are ads. data.object is list. Each ad has:

  • object ad, platform, id, url (an Ad Library link).
  • advertiser, with its own id, name and url when the library sends them.
  • status, started_at, ended_at.
  • title, body, cta_text, landing_url, format.
  • media: thumbnail_url, video_url, audio_url, image_urls, width, height. Long CDN links expire. extra.media_expires_at is the time on the recording.
  • regions, publisher_platforms.
  • impressions and spend.

In the recorded sample, impressions and spend are null on the ads shown. The fields exist so a number can be carried when the library publishes one. Null is not a zero. Political and regulated ads add transparency data under extra, including impressions_text, page_spend and reach_estimate on the single-ad endpoint. Do not assume a keyword search of ordinary ads fills spend.

extra on a search hit also includes caption, cards (carousel bodies, titles, cta_text, link_url), categories, collation_count, collation_id, link_description, contains_digital_created_media, contains_sensitive_content and video_urls. publisher_platforms in the sample lists where the ad ran, such as facebook, instagram and messenger, when the library says so.

Sample. The recorded payload is shortened. Ad copy and landing URLs in it are from that recording. Page with cursor while has_more is true. Each page is 1 credit. See pagination.

One ad#

GET /v1/facebook/ad-library/ad takes id or url. One of them. url is facebook.com/ads/library/?id=.... The response is one ad: page, status, dates and the snapshot (text, images, videos, format, link, call to action). Regulated ads put transparency data in extra.

get_transcript is accepted and does not transcribe the video. The reference says a video ad's transcript is its own call, ad/transcript, billed as AI transcription, and this flag only adds a pointer in extra.transcript_hint. Leave the flag off unless you want that pointer.

The recorded ad request uses an id. It is 1 credit on success, cacheable for 30 days like the search.

Cost#

Search page: 1 credit. One ad: 1 credit. A transcript, if you add it later, follows the transcription rule in AI transcription: 1 credit up to 2 minutes, then 1 per started minute. That is a different price from the search. Do not assume the transcript is inside the 1 credit search.

Failures cost 0. max_age_days makes a cached repeat free. Results are kept 30 days. Signup is 100 credits, once, no card. See pricing and credits.

Limits#

You get what the public library returns for that query, country and filters. You do not get an advertiser's private account, their unpublished drafts, or a guaranteed spend figure. ALL is wider and not a substitute for reading regions on each ad. Sort by impressions still returns null impression counts when Meta hides the number. The order can change without a filled impressions field.

not_found means nothing matched or the id is unknown. Stop. Rate limits are per team, 30 a minute on free credits. See rate limits and errors.

The API is in early access. The routes are beta.

Next#

Search: Facebook Ad Library API, tutorial, reference. One ad: ad details API and reference. The hub is the Facebook API. LinkedIn has a separate ad library route if that is the network you meant: LinkedIn Ad Library search.

There is also a TikTok ad library under the TikTok API. It is not this endpoint.

Questions

Is this the official Meta Ad Library API?

No. It is a SocialScrape endpoint that searches the public Meta Ad Library and returns ads as JSON. You send a SocialScrape key.

Why is the recorded request sending a cursor?

That recording is a later page. For the first page, send query and region and omit cursor. Pass the cursor you received when has_more is true.

Why are impressions and spend null?

Those fields are on the ad object. In the recorded search sample they are null, because the library did not publish a number for that ad. Null means unknown, not zero.

Can I search every country at once?

region takes one country code, or ALL for every country. language is a 2-letter code such as EN, ES or FR.

How do I read one ad?

Pass the ad id, or the library URL, to GET /v1/facebook/ad-library/ad. A video transcript is a separate call and is not included by default.

Build on social data early.

Developers and agent builders get the first keys.

Get early access