Facebook Ad Library Scraper — Meta Ads, Winners & Monitor
Pricing
from $0.60 / 1,000 ad scrapeds
Facebook Ad Library Scraper — Meta Ads, Winners & Monitor
Scrape the Meta (Facebook & Instagram) Ad Library by keyword, advertiser name, page or URL: copy, creatives, landing pages, days running, winning score, EU/UK reach & targeting included. $0.60/1,000 ads.
Pricing
from $0.60 / 1,000 ad scrapeds
Rating
0.0
(0)
Developer
Mr Zack
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
8 days ago
Last modified
Categories
Share
Scrape the Meta Ad Library (Facebook, Instagram, Messenger, Threads, Audience Network) by keyword, by advertiser name (just type "gymshark" or paste facebook.com/gymshark — no page-ID hunting), by page ID, by single ad ID, or by pasting any Ad Library URL. Every ad comes back as one flat row: ad copy, headline, CTA, landing page, images, videos, carousel cards, start date, days running, versions, rank in Meta's impressions order and a transparent winning score.
Every ad also comes with what Meta publishes under "See ad details" — included in the price: reach (EU/UK) broken down by country, age and gender, targeting (age, gender, locations), payer & beneficiary, and the advertiser's Instagram handle, followers and verification.
$0.60 per 1,000 ads, details included. Duplicates, filtered-out ads and ads you already received in monitor mode are never charged. No login, no cookies, no API key, no browser. Deep paging runs through Apify residential proxy automatically (≈ $0.035 of proxy traffic per 1,000 ads on your Apify bill — measured: 450 ads with details in 88 s for $0.016 total platform usage).
Why this one
| This Actor | |
|---|---|
| Find an advertiser by name / @handle / page URL | ✅ resolved to the page ID automatically (exact match first, then verified / most-liked, alternatives in RUN_SUMMARY) |
| Winning-ad signals | ✅ daysActive, versionsCount, rankInQuery (Meta's impressions order) and a 0–100 winningScore on every row |
| Sort | ✅ impressions (Meta's default) or newest first for monitoring |
| EU/UK audience data | ✅ includeAdDetails (on by default, no extra charge): reach by country/age/gender, targeting, payer/beneficiary |
| Flat output | ✅ one clean row per ad, same columns every time — opens straight in Sheets/Excel, no nested snapshot to unpack |
| Deep paging | ✅ automatic: page 1 direct, deeper pages via residential proxy with IP rotation; parallel queries |
| Monitor mode | ✅ onlyNewAds — pay only for ads you haven't seen |
| Dynamic/catalog ads | ✅ {{product.name}} placeholders replaced by the real card copy |
| Honest empties | ✅ an empty result page is re-checked with a fresh session before we report "0 ads" |
| Price | $0.60 / 1,000 ads, details included |
Who this is for
- Media buyers & performance marketers — find the ads that are actually working: an ad that has been running for 60+ days and has several versions is being paid for because it converts. Filter with
minDaysActive. - E-commerce & dropshipping teams — see which products competitors push, with the exact landing pages (
landingDomain,linkUrl) and creatives. - Agencies — pull a prospect's full ad footprint by page ID for a pitch deck, or schedule weekly competitor reports.
- Creative strategists & copywriters — build swipe files of hooks, headlines and CTAs by niche keyword.
- AI agents (MCP) — "what ads is brand X running on Instagram right now, and which have run the longest?" is one tool call with a predictable price.
What you get per ad
| Field | Example | Notes |
|---|---|---|
adArchiveId, libraryUrl | 925321173274919 | Open the ad in Meta's library |
pageName, pageId, pageUrl, pageLikes, pageCategories | Nike, 15087023444 | The advertiser |
isActive, startDate, endDate, lastSeenDate | true, 2026-08-03 | Run window |
daysActive | 53 | Days the ad has been running (to today, or to its end date) |
isNew | false | Started within newWithinDays (default 7) |
versionsCount | 4 | How many near-identical versions Meta grouped together — a scaling signal |
platforms | facebook, instagram, threads | Where it is shown |
displayFormat | video / image / carousel / dpa / dco | Creative type (dpa/dco = catalog/dynamic ads) |
bodyText, title, linkDescription, caption | Ad copy (dynamic {{product.name}} placeholders are replaced by the real card copy) | |
ctaText, ctaType | Shop Now, SHOP_NOW | Call to action |
linkUrl, landingDomain | https://www.footlocker.com/…, footlocker.com | Destination (Meta's l.facebook.com redirect is unwrapped) |
primaryImageUrl, primaryVideoUrl, imageUrls, videos | Creative media (HD + SD video, preview image) | |
cards, cardCount | Carousel / catalog cards: title, body, link, image, video | |
spend, impressions, currency, disclaimer | Filled for political & issue ads (Meta publishes these only there). spend = {lower, upper, text}, e.g. {40000, 45000, "£40K-£45K"} | |
reachEstimate | Political & issue ads: Meta's reach bucket, e.g. >1M | |
containsAiGeneratedMedia | false | Meta's AI-media label |
rankInQuery | 3 | Position in Meta's result order (impressions high → low by default) |
winningScore | 78 | 0–100: long run (≤40) + versions (≤20) + top impressions rank (≤25) + multi-placement (≤10) + still active (5) |
reachTotal, reachRegion, reachByCountry, reachByAgeGender | 14565, EU, {"DE":14235} | With includeAdDetails, for ads shown in the EU/UK |
targetingAge, targetingGender, targetingLocations | 18-65, All, Germany | With includeAdDetails |
payer, beneficiary | ALLBIRDS, INC. | With includeAdDetails (EU DSA disclosure) |
advertiserIgUsername, advertiserIgFollowers, advertiserVerification, advertiserAbout | allbirds, 511773, BLUE_VERIFIED | Free for advertisers inputs; for every ad with includeAdDetails |
searchQuery, searchCountry, scrapedAt | Which of your inputs produced the row |
Every field is always present — null when Meta does not publish it, never a made-up value.
A free RUN_SUMMARY record (key-value store) adds per query: Meta's reported total, ads delivered, pages fetched, why a query stopped; and per advertiser: ads, active ads, new ads, format mix, platforms, top landing domains, oldest start date and longest-running ad.
Input
| Field | Default | What it does |
|---|---|---|
searchTerms | ["nike"] | Keywords / brands searched in all ad copy. Ad Library URLs pasted here work too |
advertisers | [] | Every ad of an advertiser by name, @handle, facebook.com/… or instagram.com/… URL |
pageIds | [] | Same, by numeric page ID (view_all_page_id=…) — pins the exact page |
adIds | [] | Specific ads by Ad Library ID or ?id= link |
adLibraryUrls | [] | Paste a search URL from facebook.com/ads/library — all its filters are applied |
country | ALL | ISO-2 country (US, GB, DE, BR, ID…) or ALL |
activeStatus | active | active, inactive or all |
adType | all | or political_and_issue_ads, housing_ads, employment_ads, financial_products_and_services_ads |
mediaType | all | image, video, meme, image_and_meme, none |
platforms | all | any of facebook, instagram, messenger, audience_network, threads, whatsapp |
languages | all | ad language codes, e.g. ["en","es"] |
exactPhrase | false | match the search term as an exact phrase |
sortBy | impressions | impressions (high → low, Meta's default) or newest |
includeAdDetails | true | EU/UK reach by country/age/gender, targeting, payer/beneficiary, advertiser Instagram (included in the price) |
useResidentialForPaging | true | Page past the first 30 ads per query through Apify residential proxy (≈ $0.035 / 1,000 ads proxy traffic) |
activeFrom / activeTo | — | Meta's date filter: ads shown in this window (they may have started earlier) |
startedAfter | — | only ads launched on/after this date (new campaigns) — filtered ads are free |
maxAdsPerQuery | 100 | cap per search / page / URL |
minDaysActive | 0 | only ads running at least N days (winning ads) — filtered ads are free |
newWithinDays | 7 | window for the isNew flag |
onlyNewAds | false | monitor mode — see below |
proxyConfiguration | Apify Proxy | see "Depth & proxies" |
Example — winning ads in a niche
{ "searchTerms": ["protein powder"], "country": "US", "minDaysActive": 30, "maxAdsPerQuery": 200 }
Example — everything two competitors run, with EU audience data
{ "advertisers": ["gymshark", "https://www.instagram.com/allbirds/"], "country": "DE", "includeAdDetails": true }
Example — newest ads in a niche (daily monitor)
{ "searchTerms": ["ai note taker"], "country": "US", "sortBy": "newest", "onlyNewAds": true }
Schedule it: competitor monitor
Turn on onlyNewAds and schedule the Actor daily or weekly (Console → Schedules). The Actor remembers every ad ID it has seen per query (in a named key-value store in your account) and delivers — and charges — only ads that are new since the last run. Connect the dataset to Slack, email, Google Sheets or a webhook and you have a free-running "competitor launched a new ad" alert.
With sortBy: "newest" (recommended for monitoring) the Actor stops each query as soon as it reaches the ads it already delivered last time — so a quiet day costs almost nothing, and older ads that the previous run simply did not reach are never sold to you as "new".
Tip: with the default impressions sort, set maxAdsPerQuery at least as high as the number of ads you want to watch (e.g. an advertiser's full active count), so every run covers the whole set.
For AI agents & MCP
- Minimal input:
{"advertisers": ["<brand name>"]}or{"searchTerms": ["<keyword>"]}. Everything else has a sensible default. - Output: one row per ad, flat JSON, stable field names (table above).
daysActive+versionsCountanswer "which ads work";landingDomainanswers "where does the traffic go". - Price is predictable: $0.0006 per ad delivered, details included. Cap spend with
maxAdsPerQueryor the run's max charge. - Empty is not an error: a keyword with no ads ends SUCCEEDED with 0 rows and a clear status message. Short hiccups are retried inside the run (fresh sessions after 20 s and 45 s); only a Meta outage that survives every retry ends FAILED so your pipeline can alert. Short run timeouts end SUCCEEDED with whatever was collected.
Depth & proxies (honest limits)
Meta serves the first page of every search (up to 30 ads) to anyone, but rate-limits deeper paging from datacenter IPs. The Actor handles this for you: page 1 and ad details go direct (free), deeper pages go through Apify residential proxy with automatic IP rotation when an exit IP gets limited. That traffic is small (≈ 0.4 MB per 100 ads) and appears on your Apify bill as residential proxy usage (≈ $0.035 per 1,000 ads). If your plan has no residential proxy, you still get the first page of every query and the status message says so. You can also plug in your own proxy in Proxy configuration.
Other limits: Meta shows spend/impressions only for political & issue ads, and reach/targeting data only for ads delivered in the EU or UK. Deleted ads and ads Meta hides from the library cannot be returned.
FAQ
Is this legal? The Actor reads only the public Ad Library that Meta publishes for transparency, without logging in. You are responsible for how you use the data (e.g. GDPR if you process personal data of EU persons).
Why is bodyText empty on a few ads? Some image-only or dynamic ads have no copy text. Their cards (cards) usually still carry title and link.
Why do I get fewer ads than Meta's reported total? maxAdsPerQuery, filters (minDaysActive, startedAfter) and datacenter paging limits all reduce the count; RUN_SUMMARY.queries[].limitedBy tells you which one applied.
Related Actors
- Brand Ads Spy — Google + LinkedIn (
tactful_anvil/brand-ads-cross-network-spy) — the same brand across Google and LinkedIn ad libraries. - Ad Budget Signals (
tactful_anvil/ad-budget-signals) — does a list of domains run ads at all? One row per domain. - Google Ads Transparency Scraper (
tactful_anvil/google-ads-transparency-scraper).
Changelog
-
0.2.1 (2026-09-28) — hardening from a 24-case cloud test matrix: runs with short timeouts now deliver (reserves scale with the run), 256 MB runs no longer run out of memory, one ad-details session per route (much less proxy traffic), one ad without details no longer switches details off for the rest of the run, monitor mode stops at last run's ads (newest sort), political
spendandreachEstimatefilled again (Meta switched them to text), deadtargetedCountriescolumn removed, patient retries before any failure, watchdog finishes the run before a platform timeout. -
0.2.0 (2026-09-25) — advertiser name/URL lookup, single ads by ID,
sortBynewest,rankInQuery+winningScore, ad details included (EU/UK reach, targeting, payer/beneficiary, advertiser Instagram), automatic residential paging with IP rotation, 3 queries in parallel, empty-page re-checks. -
0.1.0 (2026-09-25) — first release: keyword / page / URL search, SSR + GraphQL paging with automatic route fallback, dynamic-ad placeholder fix, winning-ad filter,
startedAfter, monitor mode, per-advertiser summary.