Meta Ad Library Scraper — Resume, Media & $0-on-Block
Pricing
from $1.00 / 1,000 ad scraped (1 result)s
Meta Ad Library Scraper — Resume, Media & $0-on-Block
Search the Meta Ad Library by keyword and country. Returns ad copy, landing URL, CTA type, days running, placement mix, the country each ad was found in, and creative image/video URLs. Never charged for blocked runs.
Pricing
from $1.00 / 1,000 ad scraped (1 result)s
Rating
0.0
(0)
Developer
Sumitsubo
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
9 hours ago
Last modified
Categories
Share
Fetch ads from the Meta (Facebook/Instagram) Ad Library without the two things that make every other scraper frustrating: silent data loss and getting charged when the run was actually blocked.
Why this one
Most Ad Library scrapers crash on large runs, return incomplete data, and can't resume — and some charge you even when Meta blocked the request. This Actor is built reliability-first:
- Resume — a killed or timed-out run continues exactly where it stopped. No re-fetching, no double charges.
- No silent loss — every work unit is checkpointed; failures are written to the dataset as
{_error, _reason}records, never dropped. - $0 when blocked — a blocked/rate-limited request is detected and not billed. You pay only for ads actually delivered.
- Real media files — optionally download creative videos/images to key-value storage (not just snapshot URLs).
- Clean, stable schema — flat, null-safe,
snake_case, withduration_daysand_metaon every row so it drops straight into your pipeline.
What it does NOT do (honest limits)
Meta does not expose spend, impressions, reach, CTR, ROAS, or targeting for commercial ads (only political/issue ads, or EU DSA reach). This Actor does not invent those numbers. If you need spend/impressions, no scraper can get them for commercial ads — don't trust any that claim to.
Input
| Field | Meaning |
|---|---|
mode | snapshot (ads only) or snapshot_with_media (also download creative files) |
advertisers | Keywords to search the Ad Library for (required). This is a keyword search, not an exact advertiser filter — Nike also returns retailers and resellers whose ads mention Nike. Page IDs are not supported. |
countries | ISO country codes (Ad Library is per-country; default US) |
maxItems | Hard cap on ads — you are never charged beyond this |
maxBudgetUsd | Optional hard USD ceiling; the run stops before exceeding it |
Output (one row per ad)
{"ad_archive_id": "123", "country": "US", "page_name": "Nike", "page_id": "p1","ad_delivery_start": "2026-07-01", "ad_delivery_stop": "2026-07-15","duration_days": 14, "is_active": false,"publisher_platforms": ["facebook", "instagram"],"ad_creative_bodies": ["..."],"collation_count": 2,"media": [{ "kind": "video", "url": "...", "stored_key": "media_video_ab12.mp4" }],"image_urls": ["https://scontent.xx.fbcdn.net/..."],"video_urls": ["https://video.xx.fbcdn.net/..."],"landing_url": "https://...", "cta_type": "SHOP_NOW","_meta": { "_scraped_at": "2026-09-02T00:00:00Z", "_source_url": "...", "_actor_version": "<actor build version>", "_schema_version": "<schema version>" }}
country— the country library this ad was found in. The Ad Library is per-country, so this is what lets you split a multi-country run by market.media— creative files that will be downloaded to key-value storage insnapshot_with_mediamode.stored_keyis filled in once the file is saved, so every file you are charged for can be looked up in the run's key-value store. Restricted tofbcdn.nethosts and capped per ad, so a malicious advertiser host can't turn your dataset into a tracking beacon and per-ad media cost can't blow up.image_urls/video_urls— all creative URLs found (full resolution), for reference. These are never downloaded and never billed.
Blocked/failed units appear as { "_error": true, "_reason": "blocked", "_source_url": "...", "_work_unit": "..." }.
Pricing (pay-per-event)
| Event | Price |
|---|---|
actor-start | $0.01 (first runs previewable free) |
ad-scraped | $0.001 per ad |
media-downloaded | $0.007 per file (only in snapshot_with_media) |
You are not charged for blocked runs or for ads beyond maxItems / maxBudgetUsd.
Development status
Live and ready. The reliability core is complete and adversarially reviewed, and the live Meta fetch is wired against Meta's internal Ad Library GraphQL endpoint (AdLibrarySearchPaginationQuery), captured from a real browser session and pinned as an offline fixture (tests/fixtures/). npm test runs the full suite (reliability core + live-fetch parsing/pagination/blocked/429/5xx paths + billing-name pins) with no network access.
Fault-injection coverage proves the money-safety and kill-safety invariants: mid-run kill → no double charge & no data loss; blocked/rate-limited/empty → $0 charge; source exception → error record (no crash); push failure → not billed; media download failure (non-2xx) → not billed; budget/maxItems ceilings enforced and persisted across resumes (integer micro-dollar accounting, no float drift); maxBudgetUsd=0 charges nothing. Invalid input / missing residential proxy → ABORTED, not FAILED (protects Store Quality Score); charge event names are pinned in tests/billing.test.js and reconciled with Console pricing at startup (SUMMARY.monetization).
How the live fetch works
The fetch is a hybrid: bootstrap (getting the lsd token and doc_id from the public Ad Library page) runs in a real headless Chrome (src/sources/browserBootstrap.js, patchright) over a residential proxy with a disposable session per attempt (Meta's JS anti-bot challenge blocks plain HTTP clients on this page — measured 6/6 in production; bad shared-pool IPs are handled by rotating sessions with up to 4 retries). The GraphQL paging itself then runs over lightweight HTTP (got-scraping) — no login, no cookies (the GraphQL endpoint accepts requests with only the lsd token, verified in production). If a page unexpectedly comes back empty (low-reputation IP degradation), the actor discards both the session and the paging client and retries with fresh IPs up to 2 times before accepting a genuine empty result. Logged-out only — this keeps it inside the Meta v. Bright Data (N.D. Cal. 2024) safe harbor for logged-out public data.
If Meta rotates the doc_id and results stop coming back: open https://www.facebook.com/ads/library/?q=nike&country=US&active_status=active&ad_type=all in a browser, find AdLibrarySearchPaginationQuery_facebookRelayOperation in the JS bundle, and update DOC_ID_FALLBACK in metaAdLibrary.js. The code already tries to extract it dynamically first, so this is only a safety net.
Architecture
Platform-independent core (src/core, src/orchestrator.js) is fully unit-tested without the Apify platform. src/platform/apify.js and src/sources/metaAdLibrary.js are thin adapters. This is the shared core reused across the ad-library family (Google/Meta/LinkedIn) with one output-schema contract.