Meta Ad Library Checker: Is a Brand Running Facebook Ads? avatar

Meta Ad Library Checker: Is a Brand Running Facebook Ads?

Pricing

from $1.50 / 1,000 page found running ads

Go to Apify Store
Meta Ad Library Checker: Is a Brand Running Facebook Ads?

Meta Ad Library Checker: Is a Brand Running Facebook Ads?

Check if a company is running Facebook or Instagram ads: give it a list of Facebook Pages, Page IDs or domains and get one row per brand from Meta's official Ad Library API. A Clay enrichment column. Full coverage for EU/UK-delivered ads. Bring your Meta token. Never charged for a miss.

Pricing

from $1.50 / 1,000 page found running ads

Rating

0.0

(0)

Developer

Adrian Voss

Adrian Voss

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Share

Meta Ad Library Presence Lookup: Facebook Ads Library Checker

Check whether a Facebook Page is running ads, using only Meta's official Ad Library API (graph.facebook.com/ads_archive) — no scraping, no proxy fights, no reverse-engineered GraphQL that breaks the moment Meta rotates it. You bring your own Meta access token; this actor never ships or stores one. A miss never costs you anything.

Coverage — read this before you buy

Meta's official API does not return the same worldwide, every-ad coverage the public Ad Library website's own scraper-fed clones claim. It returns:

  • Full ad coverage — any category, any advertiser — for ads delivered in the EU or UK.
  • Political and issue ads only, worldwide, for everything outside the EU/UK.

An ordinary commercial ad running only in, say, the US or Brazil will not show up here. This is a genuine Meta platform restriction, not a limitation of this actor — see Meta's own Ad Library API reference. If your use case is "does this US-only retailer run any ads at all," this actor will honestly report no result, not a fabricated one.

Features

  • Official API only. Every request goes straight to graph.facebook.com/ads_archive with your own access token — nothing here scrapes the Ad Library website.
  • Page ID, Page name, or domain input. A numeric Page ID is the most reliable match; a domain is matched as a best-effort text search (Meta has no domain-to-Page lookup), and is reported as such when it misses.
  • Presence check, or one row per ad. By default one row per brand from up to 50 active ads — enough to answer "is this page actively advertising, and since when." Tick Also return one row per ad for each ad's Ad Library ID and link, running-since date, platforms, body text, headline, link caption and description, and languages (up to 500 ads per brand).
  • Competitor ad alerts. Schedule a watchlist and get only the ads that are new since the last run, plus the ads that stopped — see Competitor ad alerts.
  • Rate-limit aware. Meta caps the API at roughly 200 calls/hour per token, and every 50 ads read is one call. Max concurrency defaults to 1, and a rate-limit response comes back as a clear, actionable row instead of a silent miss.
  • Never charged for a miss. A page with no ads, an invalid token, or a search that finds nothing costs nothing — see Pricing.

Competitor ad alerts

Looking for a Facebook Ad Library scraper that tells you when a competitor launches new ads? This actor does it through Meta's official API instead of scraping, so it doesn't break when the Ad Library website changes.

  1. Put your competitors' Page IDs (most reliable) or Page names in the list, add your Meta token.
  2. Tick New ads alert: return only ads not seen before. That turns on monitoring and ad rows.
  3. Save it as a Task and add a daily or weekly Schedule.

The first run returns every active ad it reads as the starting list. Each later run returns one row per ad that this watchlist has never seen, flagged isNewAd: true, and each brand's summary row gets newAdCount plus stoppedAds: the IDs of ads that were active last run and aren't now. Open any ID at https://www.facebook.com/ads/library/?id=<ID>.

A few rules keep the new ads alert honest:

  • stoppedAds is only filled when both runs read the brand's whole active-ad list. If the list was cut off at Max ads per brand, or a Meta request failed half way, it stays null rather than guessing. Set the cap above the brand's ad count for stop alerts.
  • The first run of a watchlist never reports stops (there's nothing to compare against).
  • A watched brand whose ads all stopped still gets its summary row, with activeAdCount: 0.
  • Name the watchlist (Watchlist name) to keep separate memories for separate schedules. The memory lives in a named key-value store on your own Apify account.

To send the new ads to Google Sheets, add Apify's Google Sheets integration to the Task (or an n8n / Make step on the run's dataset) and filter on rowType = "ad". Each run then appends only the new ads to Google Sheets.

How to use Meta Ad Library Presence Lookup — Official API

  1. In the Apify Console. Open the actor page and click Start — the pageQueries field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
  2. Via the API. Call it directly with a POST request — no Console needed once you have an API token:
    curl "https://api.apify.com/v2/acts/accountable_eel~meta-ads-presence-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{"pageQueries":["nike.com","156800974367001"]}'
  3. On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.

Don't have a Meta access token yet? Leave that field blank and run it anyway: every row comes back with status NEEDS_TOKEN so you can see the output shape before you apply for one, and it only costs the start fee.

Bulk example. A 27-Page watchlist of well-known advertisers, run with a real access token:

{
"pageQueries": [
"nike.com", "adidas.com", "target.com", "walmart.com", "bestbuy.com",
"starbucks.com", "mcdonalds.com", "coca-cola.com", "pepsi.com", "nestle.com",
"airbnb.com", "booking.com", "expedia.com", "uber.com", "doordash.com",
"spotify.com", "netflix.com", "hulu.com", "samsung.com", "sony.com",
"hm.com", "zara.com", "sephora.com", "ulta.com", "chase.com",
"americanexpress.com", "visa.com"
]
}

27 Pages/domains ≈ $0.081 if every one is found ($0.003 per found row at the FREE tier, plus the $0.00005 actor-start charge) — less for any that come back BLOCKED, NOT_FOUND, or NEEDS_TOKEN, since misses are never billed.

Input

{
"pageQueries": [
"nike.com",
"156800974367001"
]
}

One per line. A numeric Meta Ad Library Page ID, a Facebook Page name, or a company domain (domains are matched as best-effort text search, not a guaranteed domain-to-page mapping). Accepted formats: 156800974367001 (numeric Page ID — most reliable), Nike (Page name), nike.com (domain — best-effort).

Fill in Your Meta Ad Library API access token to get real results, apply for one at developers.facebook.com/docs/graph-api/reference/ads_archive. The field is marked secret: masked in the Console and encrypted at rest. It's optional: leave it blank to get NEEDS_TOKEN rows instead of real data.

Output

queryfoundstatuspageIdrowTypepageNameactiveAdCountearliestActiveSinceplatformssampleAdLibraryUrlseuReachadListCompletenewAdCountstoppedAdCountstoppedAdsadArchiveIdadLibraryUrladStartedAtadStopsAtadCreatedAtadPlatformsadBodyTextadTitleadLinkCaptionadLinkDescriptionadVersionCountadLanguagesadEuReachisNewAdscrapedAt
nike.comfalseBLOCKED<row type (brand = summary row, ad = one ad)><active ads found (this lookup: up to 50, or up to max ads per brand with ad rows or monitoring on)><platforms (facebook / instagram / audience network / messenger)><sample ad library links (public, no token)><eu total reach (eu-targeted political/issue ads only)><whole active-ad list read (false = cut off at the cap)><new ads since the last run (monitoring)><ads stopped since the last run (monitoring)><stopped ad ids since the last run (monitoring)><ad library id (ad rows)><ad library link (ad rows)><ad running since (ad rows)><ad scheduled end, if set (ad rows)><ad created (ad rows)><ad platforms (ad rows)><ad body text (ad rows)><ad link title / headline (ad rows)><ad link caption, usually the display domain (ad rows)><ad link description (ad rows)><creative versions in this ad (ad rows)><ad languages (ad rows)><ad eu total reach, where meta shows it (ad rows)><new since the last run (ad rows, monitoring)>2026-09-22T06:10:15.361Z
{
"query": "nike.com",
"found": true,
"status": "OK",
"pageId": "15087023444",
"pageName": "Nike",
"activeAdCount": 50,
"earliestActiveSince": "2026-05-12T00:00:00+0000",
"platforms": ["facebook", "instagram"],
"sampleAdLibraryUrls": ["https://www.facebook.com/ads/library/?id=..."],
"euReach": null,
"scrapedAt": "2026-08-24T00:00:00.000Z"
}

activeAdCount is capped at 50 per lookup (or at Max ads per brand once ad rows or monitoring are on) — a page running more shows the cap, not its true total, and adListComplete is false. euReach is Meta's EU ad-transparency reach figure, present only on EU-targeted political/issue ads; null (not 0) when it doesn't apply.

With Also return one row per ad on, each brand's ads come as one row per ad next to its brand row (rowType tells them apart):

{
"query": "15087023444",
"found": true,
"status": "OK",
"rowType": "ad",
"pageId": "15087023444",
"pageName": "Nike",
"adArchiveId": "1202",
"adLibraryUrl": "https://www.facebook.com/ads/library/?id=1202",
"adStartedAt": "2026-09-12",
"adStopsAt": null,
"adPlatforms": ["instagram"],
"adBodyText": "Made for the long run.",
"adTitle": "Run your best",
"adLinkCaption": "nike.com",
"adLinkDescription": null,
"adVersionCount": 2,
"adLanguages": ["en"],
"adEuReach": null,
"isNewAd": true,
"scrapedAt": "2026-09-25T06:00:00.000Z"
}

Every link in the output is the public Ad Library page, which needs no token. Meta's own ad_snapshot_url embeds your access token, so this actor never writes it to the dataset.

Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

curl "https://api.apify.com/v2/acts/accountable_eel~meta-ads-presence-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
-X POST \
-H "Content-Type: application/json" \
-d '{"pageQueries":["nike.com","156800974367001"]}'

n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~meta-ads-presence-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"pageQueries":["nike.com","156800974367001"]} (swap in an expression from an earlier node for a real value).

Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~meta-ads-presence-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"pageQueries":["{{Page or domain}}"]}, mapping the row's Page or domain into the pageQueries array.

MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Meta Ad Library Checker: Facebook & Instagram Ads" — the agent will find and run this actor.

Pricing

  • Page found running ads: $3 per 1,000 Pages or domains
  • Ad row returned: $4 per 1,000 Pages or domains

Plus a $0.00005 start fee per run. Each event above is billed independently, only when it actually returns data — misses (found:false) are never charged.

Ad rows are billed per ad as a separate ad event, only when you tick Also return one row per ad or New ads alert (see the Pricing tab). Max ads per brand caps them per brand.

FAQ

Why are there no image, video or landing-page URL columns? Meta's official Ad Library API doesn't return them. It returns the ad text, headline, link caption (usually the display domain) and description, plus a rendered-ad link that only works with your token. The adLibraryUrl link shows the full creative, images and video included.

Why doesn't this find ads for a US/non-EU/UK brand I know is advertising? Meta's official API restricts non-EU/UK results to political and issue ads only — this is documented Meta platform behavior (see Coverage above), not a bug here. The only way to see ordinary commercial ads outside the EU/UK is Meta's own Ad Library website, which this actor deliberately does not scrape.

Why does a domain sometimes miss when the Page clearly exists? Meta's API has no domain lookup — a domain input is turned into a best-effort text search (e.g. nike.com → nike). Try the exact Facebook Page name, or better, its numeric Page ID.

Do I need my own Meta access token? To get real data, yes. This actor never ships or shares one. Apply at developers.facebook.com/docs/graph-api/reference/ads_archive. Running it without a token is allowed too: every row comes back as a NEEDS_TOKEN miss (never charged beyond the start fee) so you can see the shape of the output first.

What happens if I hit Meta's rate limit? Affected rows come back "status": "BLOCKED" with Meta's own error message and guidance — lower concurrency, submit fewer pages per run, or wait before retrying. See Features above.

If this saved you a scrape, a rating on the Store page helps other buyers find it.