Facebook Ad Library Scraper - Competitor Ads by Keyword or Page avatar

Facebook Ad Library Scraper - Competitor Ads by Keyword or Page

Pricing

from $0.40 / 1,000 ads

Go to Apify Store
Facebook Ad Library Scraper - Competitor Ads by Keyword or Page

Facebook Ad Library Scraper - Competitor Ads by Keyword or Page

Find Facebook and Instagram ads by keyword or Facebook Page. Export Library ID, advertiser, start date, platforms, copy, CTA, creative links and landing URL. Failed searches are free.

Pricing

from $0.40 / 1,000 ads

Rating

0.0

(0)

Developer

Yukai Lin

Yukai Lin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Facebook Ad Library Scraper

What it does

Search Meta's public Ad Library by keyword, Facebook Page URL, numeric Page ID or a copied Ad Library search URL and export one row per ad: advertiser, start and end dates, days running, copy, headline, display domain, landing link, CTA, creatives and platforms. The browser is rendered through the TidyTools Chrome pool with WARP egress requested. No Meta login is used. This Actor is available on the Apify Store.

How to use: input example

{"searchTerms":["nike"],"pageUrls":["https://www.facebook.com/profile.php?id=15087023444"],"country":"US","activeStatus":"all","adType":"all","mediaType":"all","startDateFrom":"2026-01-01","maxAdsPerQuery":50}

searchTerms, pageUrls and adLibraryUrls are lists; provide at least one. pageUrls accepts numeric IDs and public Facebook Page URLs, including vanity URLs when the page exposes its numeric ID in rendered HTML. adLibraryUrls takes search URLs copied from facebook.com/ads/library (with q=, view_all_page_id= or id=); each is rendered as-is and keeps its own country, status, ad type and media filters. An Ad Library URL pasted into pageUrls is handled the same way. country is a two-letter country code or ALL (default US); activeStatus is active, inactive or all; adType is all or political; mediaType is all, image, video, meme, image_and_meme or none; startDateFrom and startDateTo (YYYY-MM-DD) keep only ads that started running in that range; maxAdsPerQuery is 1–1,000 (default 50). Up to 100 queries per run.

API example

Use an Apify API token from your own account. This example requests up to three ads and returns the dataset items:

curl -X POST 'https://api.apify.com/v2/acts/tidytools~facebook-ads-library-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"searchTerms":["nike"],"country":"US","activeStatus":"active","maxAdsPerQuery":3}'

Output example

{"input":"nike","searchType":"keyword","country":"US","libraryId":"100000000000000","advertiserName":"Nike","pageUrl":"https://www.facebook.com/nike/","pageId":null,"pageProfilePictureUrl":"https://cdn.example.com/nike-avatar.jpg","startedAt":"2026-06-20","endedAt":null,"activeDays":123,"active":true,"platforms":["Facebook","Instagram"],"adText":"Get the gear that goes hard on and off the field.","title":"Nike Tempo Shorts","linkDescription":null,"linkCaption":"NIKE.COM","linkUrl":"https://www.nike.com/running","cta":"Shop Now","displayFormat":"image","imageUrls":["https://cdn.example.com/ad.jpg"],"videoUrls":[],"variationCount":null,"hasMultipleVersions":true,"euTransparency":false,"spend":null,"impressions":null,"adUrl":"https://www.facebook.com/ads/library/?id=100000000000000","success":true,"errorType":null,"charged":true,"chargedEvents":["ad"],"scrapedAt":"2026-10-21T00:00:00.000Z"}

Each complete ad row has input, searchType (keyword, page or url), country, libraryId, advertiserName, pageUrl, pageId, pageProfilePictureUrl, startedAt, endedAt, activeDays, active, platforms, adText, title, linkDescription, linkCaption, linkUrl, cta, displayFormat, imageUrls, videoUrls, variationCount, hasMultipleVersions, euTransparency, spend, impressions, adUrl, success, errorType, charged, chargedEvents and scrapedAt.

  • endedAt is set for stopped ads, which the Ad Library shows as a date range; activeDays counts days from the start to the end date, or to the run date for running ads.
  • title, linkDescription and linkCaption (the display domain, e.g. NIKE.COM) come from the landing-page link box. adText is the ad copy outside that box, with line breaks kept.
  • pageId is filled when the advertiser link or the Page query has a numeric ID. pageProfilePictureUrl is the advertiser avatar; it is not repeated in imageUrls.
  • displayFormat is video or image when a media URL is visible, otherwise null. hasMultipleVersions is true when the card says the ad has multiple versions. euTransparency is true when the card shows an EU transparency section.
  • spend and impressions are the ranges printed on political and issue ad cards (for example <$100), otherwise null.

Fields not visible in the Ad Library can be null, false or empty arrays. SUMMARY reports query, saved ad, charged event and free error totals, ads skipped by the start-date range (outsideDateRange), the run's cost plan and any queries left after a charge limit. A result is complete only if its Library ID, advertiser and start date are present.

Pricing

The current ad event price is $0.0005 per saved ad on FREE and BRONZE, $0.00045 on SILVER, and $0.0004 on GOLD, PLATINUM and DIAMOND; there is no Actor start fee. The Actor saves a complete ad before charging one ad event. Duplicates across queries are free, and ads outside startDateFrom/startDateTo are skipped without a charge. Empty results (no_results), Facebook verification or login (blocked), rendering service overload (service_busy), other backend failures (backend_error), Page URLs without a resolvable numeric ID (not_found), and invalid input (invalid_input) each produce a free explanation row with charged: false. A 503 browser_busy response is retried once after a few seconds at no extra charge. If a later render fails after ads were saved, those ads keep their charges and the Actor adds a free error row; SUMMARY.failed counts it. The Actor never solves a verification challenge.

Control your cost

maxAdsPerQuery caps returned ads per query. The start status and SUMMARY.costPlan show the most ads and their estimated price. Apify's per-run maximum charge can stop the Actor early; SUMMARY.notProcessed lists untouched queries. Saved rows carry the planned charge marker, while SUMMARY.charged records the actual accepted ad events if the charge call fails after saving.

Limitations

The pool scrolls to the bottom on each render. It can render again with a longer delay until the limit or no new Library IDs; renders have separate browser contexts, so a large result set may stop below maxAdsPerQuery when the site does not reveal more ads in one render. A returned WARP fallback may use the pool's home egress. Live Ad Library HTML changes can affect extraction. Rendering takes browser time even for free rows.

On 2026-10-21, three separate local runs used the live rendering backend and maxAdsPerQuery: 3. The local runner simulated Apify FREE-tier charging; these are measured samples, not a guarantee of future yield or speed:

InputDatasetSUMMARYSimulated ad eventsElapsed
{"searchTerms":["nike"],"country":"US","maxAdsPerQuery":3}3 complete adsSUCCEEDED; ads 3, charged 3, failed 03 ($0.0015)5.0 s
{"pageUrls":["15087023444"],"country":"US","maxAdsPerQuery":3}3 complete adsSUCCEEDED; ads 3, charged 3, failed 03 ($0.0015)4.6 s
{"searchTerms":["nike"],"country":"GB","maxAdsPerQuery":3}3 complete adsSUCCEEDED; ads 3, charged 3, failed 03 ($0.0015)4.9 s

All nine saved rows had a Library ID, advertiser, start date and charge marker; the listed output fields matched the rows. variationCount was null and videoUrls empty in all nine. No query was blocked (0/3), but this small, capped sample does not establish a general blocked rate or how many ads each query has in total. Keyword matches can include advertisers other than Nike. A 2026-10-20 Nike/US fixture contains 29 sanitized cards; its source render yielded 29 complete rows, but that earlier render was not part of these three runs.

On that fixture (Chinese-locale render, all 29 ads active and non-political), the parser fills: headline title 22/29, linkCaption 17/29, linkDescription 14/29, hasMultipleVersions 18/29, euTransparency 4/29, pageProfilePictureUrl 29/29, numeric pageId 5/29 (the others link to a vanity Page URL), activeDays 29/29. Cards without a headline or display domain had none on the page. endedAt, spend and impressions were 0/29 because no card there was stopped or political; their date-range and spend formats are covered by tests on constructed cards, not yet by a live render.

FAQ

Do blocked, empty, or failed searches cost an ad event? No. blocked means Facebook asked for verification or login; no_results means no complete ads were found; service_busy means the rendering service was overloaded; backend_error means the service was unavailable or returned an error; not_found means a Page URL did not expose a numeric ID; and invalid_input means the input was invalid. Each is a free explanation row. Ads saved before a later failure retain their own charge.

Can I paste a search URL from the Ad Library website? Yes, in adLibraryUrls (or pageUrls). The URL is rendered unchanged, so its own country, status, ad type and media filters apply; country, activeStatus, adType and mediaType from the input are not added to it. The rows' country is the URL's country= value, or null when the URL has none. startDateFrom/startDateTo still filter the returned rows.

Can I pass a vanity Page URL? Yes when its public rendered page exposes a numeric Page ID. Otherwise the free not_found row asks you to enter the numeric ID directly.

Local tests

npm test checks parsing, input URL construction and local charge/error behavior. The three measurements above used webcapture/tools/local_run.mjs; local charge events are simulated and do not verify Apify billing or a hosted run.