Facebook Ad Library API — Search Ads by Keyword
Documentation menu
FetchLayer FetchLayer API

Search ads by keyword

Public Facebook and Instagram ads matching a keyword or phrase, filtered by country, status, media type, platform, language and impression date. Up to 30 ads a page, with Facebook’s estimated total and a cursor to continue.

POST /search-ads Ads
https://api.fetchlayer.dev/facebook-ad-library/search-ads
OpenAPI JSON

Headers

Authorization string required

Bearer token — your FetchLayer API key.

Parameters

query string required

Keyword or phrase to search the Ad Library for.

searchType string optional

keyword_unordered or keyword_exact_phrase. (default: keyword_unordered)

pageIds string[] optional

Restrict the keyword search to these advertiser page ids (max 50).

country string optional

Two-letter country code, or ALL. (default: ALL)

countries string[] optional

Several countries at once (max 20). Overrides country.

activeStatus string optional

active, inactive, or all. (default: active)

adType string optional

ALL, POLITICAL_AND_ISSUE_ADS (POLITICAL is accepted for it), or a special ad category: HOUSING_ADS, EMPLOYMENT_ADS or FINANCIAL_PRODUCTS_AND_SERVICES_ADS (CREDIT_ADS is its earlier name and filters the same). Case-insensitive. (default: ALL)

mediaType string optional

all, image, meme, image_and_meme, video, or none.

publisherPlatforms string[] optional

FACEBOOK, INSTAGRAM, MESSENGER, AUDIENCE_NETWORK, WHATSAPP, THREADS, OCULUS, UNKNOWN.

contentLanguages string[] optional

Two-letter ad language codes, for example en or es.

bylines string[] optional

Political-ad disclaimers ("paid for by").

startDateFrom string optional

Only ads whose impressions start on or after this YYYY-MM-DD date.

startDateTo string optional

Only ads whose impressions start on or before this YYYY-MM-DD date.

isTargetedCountry boolean optional

Restrict to ads targeting the country rather than merely reaching it.

limit integer optional

Maximum ads returned (max 2000), or -1 for everything available. A request returns up to limit or pages × 30 ads, whichever is smaller. (default: 30)

pages integer optional

Pages of up to 30 ads to collect in one request (max 100), or -1 for the maximum allowed. A very large request stops early, with hasNextPage true, a nextCursor and a note, rather than running past the response time limit. (default: 1)

cursor string optional

Resume from a previous response's nextCursor. Send it with the same route, advertiser or query, country and filters; limit, pages, sortBy and format may change. A cursor from a different route, advertiser, query, country or set of filters, or one that was altered, is a 400 saying which. Cursors issued before October 2026 are still accepted, with a note, and the response carries a current nextCursor. Any other string is a 400: pass nextCursor back exactly as received.

delayMs integer optional

Milliseconds to wait between pages (max 30000).

sortBy string optional

start_date, end_date, total_active_time, or none. Orders the ads in this response, not the whole listing; collect more pages for a wider ordering.

sortDirection string optional

asc or desc.

format string optional

json or markdown, lowercase; any other value is a 400. Over MCP, markdown returns the result rendered as text; the REST API accepts it and returns JSON. (default: json)

timeoutMs integer optional

Time limit for the whole request, in milliseconds (1000–120000), counted from its arrival, time spent waiting in the queue included; a value outside that range is a 400. When it runs out, a paged request returns what it has with nextCursor and a note, billed for the pages it delivered; a request with nothing to return yet fails with a 503 you can retry, marked with the response header X-Timeout-Source: caller. Left out, the request runs as long as it needs.

Response Fields

search-ads

Search ads by keyword

filters object

The filters this search actually ran with, after defaults were applied.

ads object[]

The matching ads, newest data first unless sortBy says otherwise. A field Facebook does not provide is null, or [] for a list — never left out; false and 0 are real values. An ad with odd or missing fields is always returned, with those fields null.

skippedAds object[]

Records in the listing that cannot be an ad at all — an empty entry, or one with no ad archive id — each with what is still known about it, and counted in notes; [] when none. Only that record is left out: the rest of the page and nextCursor are unaffected.

skippedAds[].reason string

not-an-object (an empty entry where an ad belongs), missing-ad-archive-id, or unreadable (a record with an id whose contents could not be used).

skippedAds[].adArchiveId string | null

The ad archive id, when the record had one.

skippedAds[].adId string | null

The ad’s own id, when the record had one.

skippedAds[].pageId string | null

The advertiser’s page id, when the record named one.

skippedAds[].fieldsPresent string[]

The ad fields the record carried values for, by their names on an ad, for example startDate, advertiser or creative.

notes string[]

Caveats about this result, such as an ad left out or a list that may be incomplete.

ads[].adArchiveId string

Ad Library archive id — the handle for ad-detail and ad-media.

ads[].adLibraryUrl string

Public Ad Library permalink for this ad.

ads[].isActive boolean

Whether the ad was still running at the time of the request.

ads[].startDate string | null

The day the ad started running, as a UTC date (YYYY-MM-DD); null when not published.

ads[].endDate string | null

The day the ad stopped running, as a UTC date; for a running ad Facebook reports the current day. null when not published.

ads[].startDateUnix integer | null

startDate as Unix seconds (UTC); null when not published.

ads[].endDateUnix integer | null

endDate as Unix seconds (UTC); for a running ad, the current day. null when not published.

ads[].totalActiveTimeSeconds integer | null

How long the ad has been live — the usual proxy for what is working. null when not published.

ads[].publisherPlatforms string[]

Surfaces the ad ran on (Facebook, Instagram, Messenger, Threads…).

ads[].advertiser object

The account the ad appears under: page id, name, URL, categories, like count, political byline, pageAlias, instagramUsername and instagramUsernameStatus. On a partnership ad this is partnership.postedBy.

ads[].advertiser.pageAlias string | null

The account’s Facebook username; null when it publishes none.

ads[].advertiser.instagramUsername string | null

The Instagram username; null when the page links none or it is not known — instagramUsernameStatus says which. Without includeProfiles most Facebook Pages on these listings are not-looked-up.

ads[].advertiser.instagramUsernameStatus string

found: instagramUsername is the account’s username. not-published: the page’s own details link no Instagram account. not-looked-up: not known here — an ad names an Instagram account only when it is shown under one, so null says nothing about whether there is one; includeProfiles fills it in, as do ad-detail for the advertiser and advertiser-profile (as igUsername). With includeProfiles: lookup-failed (no answer for this account this time; asking again may fill it), skipped-cap (past the 30 accounts filled per request) or skipped-time (not filled in the time available). A username that cannot be filled never fails the request. sponsor and both partnership accounts carry it too.

ads[].sponsor object

The account that ran the ad: pageId, pageName, pageUrl, pageIsDeleted, pageAlias, instagramUsername and instagramUsernameStatus. The same account as advertiser except on some partnership ads.

ads[].isPartnershipAd boolean

Whether the ad carries Facebook’s “Paid partnership with” label (branded content).

ads[].partnership object | null

On a paid-partnership or collaboration ad, the two accounts by position, each with pageId, pageName, pageUrl, pageIsDeleted, pageAlias, instagramUsername and instagramUsernameStatus: postedBy (A in “A with B”) and taggedPartner (B), plus runBy, brand, creator and relationship. null on every other ad.

ads[].partnership.postedBy object

The account the ad appears under: A in “A with B”. To show “A with B” for any partnership ad, use postedBy as A and taggedPartner as B.

ads[].partnership.taggedPartner object

The account the ad tags: B in “A with B”.

ads[].partnership.runBy string

Which of the two ran (paid for) the ad: creator (postedBy), brand (taggedPartner) or other (neither). It says who ran the ad, not which party is the business.

ads[].partnership.brand object

The same account as taggedPartner, named after Meta’s branded-content convention that the tagged account is the brand. That does not always hold: when a brand posts a collaboration and tags a creator, brand is the creator. Example, ad 2076260499666267: Red Bull Argentina posts and runs it, tagging the Instagram creator martibenza — postedBy and creator are Red Bull Argentina, taggedPartner and brand are martibenza, runBy is creator.

ads[].partnership.creator object

The same account as postedBy; see brand on why these names are positions.

ads[].partnership.relationship string

paid_partnership, for paid partnerships and Instagram collaborations alike: Facebook publishes no difference between them.

ads[].collationId string | null

The version group Facebook files the ad under, or null when it files it under none (which also means a single version). ad-detail lists the versions in variations.

ads[].collationCount integer | null

How many versions Facebook counts in that group for your filters; 1 means the ad runs alone in its group. null when there is no group.

ads[].creative object

Title, body, caption, link, CTA, display format, plus videos, images and carousel cards. Media URLs here are Facebook’s own signed links, which expire within hours; for a download that keeps working, use the downloadUrl from ad-media.

ads[].transparency object

Spend, currency, impressions and reach bands, published for political and issue ads. categories lists the special ad categories the ad declared — POLITICAL, HOUSING, EMPLOYMENT, CREDIT, possibly more than one — or [] for other ads.

adCount integer

Number of ads in this response.

pagesScraped integer

How many pages of up to 30 ads this response delivered — a stable count that depends only on how many ads came back. Billing follows it: 1 credit per page of up to 30 ads, and an empty result still costs the 1 credit every request pays.

pagesFetched integer

A diagnostic that can vary between identical requests, so do not rely on it. It is never billed.

totalCount integer | null

Facebook’s own estimate of how many ads match the whole listing. Usually present on the first page of a listing for a single country or ALL, and exact when the response reached the end of the listing. null means not known — including on some first pages — not zero.

nextCursor string | null

Pass back as cursor to continue from where this response stopped — at the first ad not yet returned, also when limit ended the response partway through a page.

hasNextPage boolean

Whether more results are available beyond this response.

Request

curl -X POST "https://api.fetchlayer.dev/facebook-ad-library/search-ads" \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"query":"project management software","country":"US","activeStatus":"active","mediaType":"video","limit":30}'

Response

{
  "adCount": 30,
  "pagesScraped": 1,
  "pagesFetched": 1,
  "totalCount": 40599,
  "hasNextPage": true,
  "nextCursor": "fbal1.eyJyIjoic2VhcmNoLWFkcyIs…",
  "ads": [
    {
      "adArchiveId": "1008774295390738",
      "isActive": true,
      "startDate": "2026-07-14",
      "publisherPlatforms": [
        "FACEBOOK",
        "INSTAGRAM",
        "MESSENGER",
        "THREADS"
      ],
      "advertiser": {
        "pageId": "314197722000030",
        "pageName": "monday.com"
      },
      "creative": {
        "title": "Meet Your monday AI Agent",
        "ctaText": "Sign up",
        "linkUrl": "https://monday.com/ap/agents-for-work"
      }
    }
  ]
}