Meta Ads Library Scraper (Facebook Ads)
Pricing
from $0.60 / 1,000 ad results
Meta Ads Library Scraper (Facebook Ads)
For creative strategists, copywriters, media buyers, marketers, and agencies. Scrape the Meta Ad Library by exact phrase, keyword, page, or Library URL. Control country, status, media type, platforms, dates, and impressions sort. $0.60 per 1,000 ads. No Facebook token.
Pricing
from $0.60 / 1,000 ad results
Rating
0.0
(0)
Developer
Arkis
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Security
Limited permissionsShare
This Actor is for creative strategists, copywriters, media buyers, marketers, agencies, and anyone watching competitors in the Meta Ad Library (Facebook Ad Library, covering Facebook, Instagram, Messenger, Audience Network and Threads).
When the job is Meta ads, this is the scraper you reach for. You search by keyword (any word or exact phrase), paste an Ad Library URL, or point it at a Facebook page. You pick the country, keep active ads or include inactive ones, filter by media type and publisher platform, bound a delivery window, and sort by impressions or recency. Turn on ad details when you need payer, beneficiary, and EU reach. The inputs stay stable enough to schedule as a monitor.
You get the ad copy, headline, CTA, landing page, image and video URLs, carousel cards, platforms, start and end dates, days running, and, where Meta discloses them, payer, beneficiary and EU reach. Output is clean JSON, CSV or Excel, with no Facebook login, access token or app review.
It works as a Facebook Ad Library API alternative. The official Ad Library API only covers political/issue ads and EU-delivered ads, and it needs identity verification. This Actor reads the same public pages you see at facebook.com/ads/library.
What can it do?
- 🔎 Keyword search: any-word or exact-phrase, in one country or
ALLcountries. - 🔗 Paste Ad Library URLs straight from your browser. Their filters (country, status, media type, sort, dates, language, platform) are kept.
- 🏷️ All ads from a page: Facebook page URL,
profile.php?id=…, numeric page ID orview_all_page_id=URL. - 🎯 Single ad by
…/ads/library/?id=<adArchiveId>. - 📅 Delivery window and platforms: first-class
startDateMin/startDateMax(Meta's delivered-impressions window) andpublisherPlatforms(Facebook, Instagram, Messenger, Audience Network, Threads). - 🧾 Ad details (on by default): payer, beneficiary, EU total reach and EU audience (age, gender, locations), Instagram handle and followers, page verification.
- 🖼️ Creatives: image URLs, HD and SD video URLs, video thumbnails, and every carousel / dynamic-product card in
snapshot.cards. - 💾 Durable media save (
saveMedia, off by default): download images and videos into the run key-value store and keep Apify URLs that outlast signed Meta CDN links. - 📡 Monitor mode: schedule the same query and only write ads not seen for that fingerprint. Optional
emitAdChangeswritesitemType: "ad-change"when copy, CTA, landing URL, media, status, platforms or dates shift. - ♻️ Deduplicated by
adArchiveIdacross all your queries. You never pay twice for the same ad in a run. - 🛟 Resilient: solves Meta's JS challenge, retries with exponential backoff, rotates proxy sessions on blocks, and resumes after migrations. Every run writes a summary to the
OUTPUTrecord.
How to scrape the Meta Ad Library
See the input tab for every field. Four starting points:
1. Competitor page watch
Paste the competitor's Facebook page (or an Ad Library view_all_page_id= URL). Sort by most recent, keep ads active, and schedule the run. Use mode: "monitor" if you only want ads that were not seen for this query before.
{"urls": ["https://www.facebook.com/BachaCoffeeOfficial/"],"country": "SG","activeStatus": "active","sortBy": "most_recent","maxAds": 100,"scrapeAdDetails": true,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
2. Keyword exact-phrase winners
Lock the phrase so you are not matching every word in any order. Sort by impressions to see what actually ran at scale.
{"keywords": ["best running shoes"],"searchType": "keyword_exact_phrase","country": "US","activeStatus": "active","sortBy": "impressions","mediaType": "all","maxAds": 150,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
3. Creative strategist swipe file
Pull video (or image) ads for a theme, optionally on Instagram only, and keep details on so you can see who paid when Meta discloses it.
{"keywords": ["skincare routine"],"searchType": "keyword_exact_phrase","country": "GB","mediaType": "video","publisherPlatforms": ["instagram"],"sortBy": "impressions","activeStatus": "active","maxAds": 80,"scrapeAdDetails": true,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
4. Agency multi-country
Set country to ALL, or paste one Ad Library URL per market so each URL keeps its own country filter. Bound a delivery window if you only care about a flight.
{"urls": ["https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&view_all_page_id=22437985072&search_type=page","https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=GB&view_all_page_id=22437985072&search_type=page","https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=DE&view_all_page_id=22437985072&search_type=page"],"startDateMin": "2026-01-01","maxAds": 200,"scrapeAdDetails": true,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
A pasted Library URL already carrying country, start_date, or publisher_platforms keeps those values. The fields above fill in anything the URL leaves blank.
5. Scheduled monitor with durable creatives
{"urls": ["https://www.facebook.com/BachaCoffeeOfficial/"],"country": "SG","activeStatus": "active","sortBy": "most_recent","maxAds": 80,"mode": "monitor","emitAdChanges": true,"saveMedia": true,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
The first monitor run saves matching ads (charged as ad) and records their ids. Later runs only write unseen ads. With emitAdChanges, later runs also write itemType: "ad-change" when snapshotted fields move. saveMedia stores files in the run key-value store and adds savedImageUrls / savedVideoUrls.
Pricing
Pay per result. Two add-ons stay off unless you turn them on.
| Event | Default | Price | When it charges |
|---|---|---|---|
ad | always | $0.0006 ($0.60 / 1,000) | Each unique ad written to the dataset |
media-save | off (saveMedia) | $0.0015 ($1.50 / 1,000) | Each ad that had at least one image or video saved to the run key-value store |
ad-change | off (mode: "monitor" and emitAdChanges) | $0.0008 ($0.80 / 1,000) | Each previously seen ad whose snapshotted fields changed |
Failed downloads, duplicate ads, and unchanged previously seen ads are free. The first monitor run with emitAdChanges only stores snapshots; it does not charge ad-change. Basic imageUrls / videoUrls stay in the ad item even when saveMedia is off.
You also pay Apify's standard Actor start event. Set a maximum cost per run in Apify Console and the Actor stops cleanly when the limit is reached.
| Ads | ad only | All 1,000 ads also saved media |
|---|---|---|
| 100 | $0.06 | $0.21 |
| 1,000 | $0.60 | $2.10 |
| 10,000 | $6.00 | $21.00 |
Input
| Field | Type | Default | Notes |
|---|---|---|---|
keywords | string[] | ["coffee"] (prefill) | One query per keyword. Pair with searchType. |
urls | string[] | – | Ad Library URLs, Facebook page URLs, page IDs or single-ad URLs. |
country | string | SG | ISO-2 code (US, GB, DE, …) or ALL. |
activeStatus | active / inactive / all | active | |
mediaType | all / image / video / meme / none | all | |
maxAds | integer | 50 | Per query (each keyword / URL). |
sortBy | impressions / most_recent | impressions | Same as the site's sort. |
searchType | keyword_unordered / keyword_exact_phrase | keyword_unordered | Exact phrase for tight copy/angle hunts. |
startDateMin | YYYY-MM-DD | – | Ads that delivered on or after this date. |
startDateMax | YYYY-MM-DD | – | Ads that delivered on or before this date. |
publisherPlatforms | string[] | – | facebook, instagram, messenger, audience_network, threads. Empty = all. |
mode | search / monitor | search | monitor only writes ads not stored for this query fingerprint (named store meta-ads-library-monitor, cap 20,000 ids). |
emitAdChanges | boolean | false | Monitor only. Emit itemType: "ad-change" when snapshotted creative fields change. First run stores snapshots only. |
saveMedia | boolean | false | Download creatives into the run key-value store and add durable Apify URLs. Charged as media-save once per ad with at least one saved file. |
scrapeAdDetails | boolean | true | One extra small request per ad for payer/beneficiary/EU fields. Fails soft. |
includeRaw | boolean | false | Adds the unmodified Meta objects as raw. |
paginationFallback | boolean | true | See Limitations. |
proxyConfiguration | proxy | { "useApifyProxy": true } | Use the RESIDENTIAL group when maxAds > 30. |
Output
One dataset item per unique ad (and, if you asked for them, ad-change rows). The dataset has views for Overview, Creatives & media, Payer, beneficiary & EU reach, and Ad changes.
| Field | Description | Availability |
|---|---|---|
adArchiveId, adLibraryUrl | Ad Library ID and public link | always |
pageId, pageName, pageProfileUrl, pageProfilePictureUrl, pageLikeCount, pageCategories | Advertiser page | always (likes may be null) |
primaryText, headline, description, ctaText, ctaType, linkUrl, linkCaption | Ad copy & destination. For dynamic product ads ({{product.name}} templates) the first card's real text is used | when the ad has them |
displayFormat | IMAGE, VIDEO, CAROUSEL, DPA, DCO, MULTI_IMAGES, … | always |
imageUrls, videoUrls (HD + SD), videoPreviewImageUrls | Media of the ad and all its cards | when present. fbcdn URLs are signed and expire after about 1-4 days |
savedMedia, savedImageUrls, savedVideoUrls, mediaStoreId | Durable copies in this run's key-value store (media/{adArchiveId}/…) | only with saveMedia, and only for files that downloaded |
itemType, changes, before | ad or ad-change, plus the field diffs | itemType/changes/before when emitAdChanges is on |
publisherPlatforms | FACEBOOK, INSTAGRAM, AUDIENCE_NETWORK, MESSENGER, THREADS | always |
startDate, endDate | ISO dates. For active ads endDate is the latest delivery date (≈ today) | always |
isActive, daysRunning | Days from start to today (active) or to end date | always |
collationId, collationCount | Group of near-identical versions ("N ads use this creative") | usually |
impressionsText, impressionsIndex, spend, currency, reachEstimate | Only disclosed by Meta for political/issue ads (and some regions); null for normal commercial ads | rarely |
payer, beneficiary, payerBeneficiary | Who paid / who benefits | with scrapeAdDetails, for ads that reached the EU or fall under regional disclosure rules |
targetsEu, euTotalReach, euAudience | EU DSA transparency: total EU reach, gender, age range, locations | with scrapeAdDetails, EU-delivered ads |
instagramUsername, instagramFollowers, pageVerification | From the details endpoint | with scrapeAdDetails |
country, keyword, sourceUrl, foundVia, scrapedAt | Query context. foundVia = ssr / pagination / fallback | always |
snapshot | Cleaned creative: body, title, images, videos, cards[] for carousels / DPA, extra texts/links | always |
raw | Unmodified Meta data | only with includeRaw |
Sample item
Real output from a run with keywords: ["coffee"], country: "SG" (long URLs shortened):
{"adArchiveId": "27086586400970202","adLibraryUrl": "https://www.facebook.com/ads/library/?id=27086586400970202","pageId": "114040321562599","pageName": "Muse App","pageLikeCount": 965,"pageProfileUrl": "https://www.facebook.com/100088641745631/","pageCategories": ["Photo & Video"],"primaryText": "Easy Reels Maker! ✨ Try it now!","headline": "Muse: Best Reels Maker","description": null,"ctaText": "Install now","ctaType": "INSTALL_MOBILE_APP","displayFormat": "VIDEO","linkUrl": "http://itunes.apple.com/app/id1638320348","linkCaption": "itunes.apple.com","imageUrls": [],"videoUrls": ["https://video-iad3-1.xx.fbcdn.net/o1/v/t2/f2/m366/AQMVY5oW3u…","https://video-iad3-1.xx.fbcdn.net/o1/v/t2/f2/m412/AQO2-5UGzc…"],"publisherPlatforms": ["FACEBOOK", "INSTAGRAM", "AUDIENCE_NETWORK", "MESSENGER", "THREADS"],"startDate": "2026-06-13T07:00:00.000Z","endDate": "2026-11-05T08:00:00.000Z","isActive": true,"daysRunning": 146,"collationId": "1306591561637611","collationCount": 1,"impressionsText": null,"impressionsIndex": -1,"spend": null,"currency": null,"payer": "BIGROWTH LTD","beneficiary": "BIGROWTH LTD","targetsEu": true,"euTotalReach": 739152,"euAudience": {"gender": "All", "ageMin": 20, "ageMax": 65,"locations": [{ "name": "Worldwide", "type": "country_groups", "excluded": false },{ "name": "Africa", "type": "country_groups", "excluded": true }]},"instagramUsername": "muse.templates","instagramFollowers": 433982,"pageVerification": "NOT_VERIFIED","detailsFetched": true,"country": "SG","keyword": "coffee","foundVia": "ssr","scrapedAt": "2026-11-05T08:50:06.405Z","snapshot": {"displayFormat": "VIDEO","body": "Easy Reels Maker! ✨ Try it now!","title": "Muse: Best Reels Maker","videos": [{ "videoHdUrl": "https://video-iad3-1…", "videoSdUrl": "https://video-iad3-1…", "videoPreviewImageUrl": "https://scontent-iad6-1…" }],"cards": []}}
Run summary (OUTPUT key-value record)
Each run writes:
- ads saved per query, and Meta's reported total for the query
- pages fetched, and whether pagination was blocked
- how many ads came from each source (
foundVia) - per-query errors
- detail-fetch stats
- media-save stats (files saved / failed) when
saveMediais on - monitor fingerprint, previously seen count, and change stats when
modeismonitor - HTTP stats: requests, retries, blocks by type, session rotations
- the GraphQL
doc_ids used
Limitations
Read these before running large jobs:
- Meta throttles pagination. The first ~30 results of every search come embedded in the page and are reliable. Further pages need Meta's
AdLibrarySearchPaginationQuery, which Meta often answers withRate limit exceededfor logged-out traffic from datacenter IPs. The Actor backs off and rotates proxy sessions; RESIDENTIAL proxy gives the best chance. - Pagination fallback. If pagination stays blocked and
paginationFallbackis on, the Actor re-runs the first results page over narrower slices of your search. A slice can be a content language, media type, platform, past month, or the other sort order. In tests this raised a no-proxy run from 30 to 300 unique ads for "coffee" in Singapore. All ads still match your filters, but:- the extra ads are not in your chosen sort order;
- the fallback cannot reach every ad of a query with tens of thousands of results;
foundVia: "fallback"marks these ads.
- Spend, impressions and reach are mostly
null. Meta only publishes them for political/issue ads, plus EU reach for EU-delivered ads (needsscrapeAdDetails). - Media URLs expire. Signed Meta CDN links last about 1-4 days. Turn on
saveMediaif you need copies in the run key-value store (charged asmedia-save). Those files last as long as the run storage does. - Monitor baseline is what this Actor fetched, not the entire Ad Library. The first
mode: "monitor"run records every ad it actually saw (up to pagination / fallback). Later runs emit ads that were not in that set. Ids are stored inmeta-ads-library-monitor, keyed by a hash of keywords, urls, country, status, media type, search type, dates and platforms, and capped at 20,000. - Political & issue ad type (
ad_type=political_and_issue_ads) may need a logged-in session on some countries; this Actor does not log in. - Page URLs by vanity name (facebook.com/SomeBrand) are resolved to a page ID on a best-effort basis. If that fails, use an Ad Library URL with
view_all_page_id=. - Meta changes its internal web API without notice. The
doc_ids are discovered from the live page and fall back to known values. Expect occasional breakage; please report issues.
Is it legal?
The Actor only collects data that Meta publishes openly in its Ad Library for transparency, and it never logs in. You are responsible for how you use the data, including any personal data, under GDPR and other laws, and for complying with Meta's terms.
Coming later
These already work if you paste an Ad Library URL that sets them. They are not first-class input fields yet:
- Content language (
content_languageson a Library URL) - Ad type, including political and issue ads (
ad_type=on a Library URL). Some countries may need a logged-in session for that filter; this Actor does not log in. - A list of countries in one keyword search, without using
ALLor one URL per market
FAQ
Do I need a Facebook account or API token? No.
Why did I get only ~30 ads? Pagination was blocked. Switch the proxy to RESIDENTIAL and keep paginationFallback on. The OUTPUT record shows paginationBlocked per query.
Can I monitor competitors? Yes. Set mode: "monitor" on a scheduled run with the same keywords or page URLs. The Actor only writes ads it has not stored for that query fingerprint. Turn on emitAdChanges if you also want rows when an already-seen ad changes copy, CTA, landing URL, media, status, platforms or dates.
How do I keep creatives after the CDN links expire? Set saveMedia to true. Files land in the run key-value store under media/{adArchiveId}/… and the dataset item gets savedMedia plus savedImageUrls / savedVideoUrls. You pay media-save once per ad that stored at least one file.