Facebook Ad Scraper
Pricing
$19.99/month + usage
Facebook Ad Scraper
Analyze Facebook Ad Library campaigns to uncover advertisers, active ads, creatives, copy, landing pages, and targeting signals. Build structured competitor intelligence datasets for ad research, market analysis, creative benchmarking, and performance strategy.
Pricing
$19.99/month + usage
Rating
0.0
(0)
Developer
Scraper Engine
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
6 hours ago
Last modified
Categories
Share
๐ Facebook Ad Library Intelligence Scraper
Scrape Facebook & Instagram Ad Library ads by keyword or Page โ with automatic duplicate suppression, creative-format classification, ad-longevity analytics, and advertiser-scale tiering built on top of Meta's own Ad Library GraphQL API.
๐ Why Choose This Actor?
- ๐ Automatic duplicate suppression โ Meta's own pagination can hand back the same ad twice across overlapping pages (verified live); this actor tracks
adIdper run and drops exact repeats before they reach your dataset - ๐จ Creative-format classification โ every ad is classified as
CAROUSEL/VIDEO/SINGLE_IMAGEfrom its actual media payload (card/image/video counts), not just Meta's raw internal code, which mixes formats - ๐
Ad-longevity analytics โ every ad gets a computed
adDurationDays, plus a run-level average, so you can see which creatives/advertisers keep running (a strong signal of "this is working for them") - ๐ข Advertiser-scale tiering โ every advertiser Page is bucketed micro โ mega by like count, so you can separate small local advertisers from major brands in one pass
- ๐ Search or Page mode โ point it at an Ad Library search URL (keyword) or a Facebook Page URL directly
- ๐ Run analytics โ a
RUN_SUMMARYwritten to the key-value store: totals, dedup/filter counts, format & tier breakdowns, top advertisers by ad count
โจ Key Features
- ๐ฏ Keyword or Page sources โ mix search URLs and Page URLs in the same run
- ๐
deduplicateAdsโ on by default; skips any ad whoseadIdwas already collected this run - ๐จ
creativeFormatFilterโ only keep ads matching a chosen format (CAROUSEL,VIDEO,SINGLE_IMAGE,UNKNOWN) - ๐ข
minAdvertiserLikesโ filter out low-authority/throwaway advertiser Pages - ๐ฆ
fetchAdDetailsโ optional per-ad enrichment call that addsadvertiserDetails,transparencyByLocation,verifiedVoiceContext,violationTypeswhen Meta exposes them - ๐ Country/date filtering โ
dateRangepreset, per-source country codes via the source URL
๐ฅ Input Configuration
{"adLibrarySources": [{ "url": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=US&q=shoes&search_type=keyword_unordered&media_type=all" }],"proxyConfig": { "useApifyProxy": true },"maxAdsPerSource": 50,"fetchAdDetails": true,"dateRange": "","pageAdsActiveStatus": "all","pageAdsCountryCode": "ALL","deduplicateAds": true,"creativeFormatFilter": [],"minAdvertiserLikes": 0}
| Field | Type | Description |
|---|---|---|
adLibrarySources | array | ๐ Ad Library search URLs or Facebook Page URLs (required) |
proxyConfig | object | ๐ก๏ธ Apify Proxy config (required) |
maxAdsPerSource | integer | ๐ Max ads to collect per source URL (0 = unlimited) |
fetchAdDetails | boolean | ๐ฆ Fetch an extra per-ad details call (slower, adds enrichment-only fields) |
dateRange | string | ๐
Date preset for search sources (LAST_90_DAYS, LAST_30_DAYS, LAST_7_DAYS, LAST_3_DAYS, TODAY, or empty) |
pageAdsActiveStatus | string | ๐ข Active status filter when scraping a Page URL directly (all/active/inactive) |
pageAdsCountryCode | string | ๐ Country filter when scraping a Page URL directly |
deduplicateAds | boolean | ๐ Skip already-seen adId values in this run (default true) |
creativeFormatFilter | array | ๐จ Keep only ads with a computed format in this list (empty = keep all) |
minAdvertiserLikes | integer | ๐ข Skip ads whose advertiser Page like count is below this (0 = disabled) |
๐ค Output Format
{"adId": "123456789012345","advertiserName": "Example Shoe Co","pageId": "987654321","isActive": true,"startDateFormatted": "2026-06-01 00:00:00","endDateFormatted": "2026-08-30 00:00:00","adDurationDays": 90,"publisherPlatforms": ["FACEBOOK", "INSTAGRAM"],"creativeFormat": "CAROUSEL","mediaAssetCount": 6,"advertiserScaleTier": "medium","adLibraryUrl": "https://www.facebook.com/ads/library/?id=123456789012345","spend": null,"reachEstimate": null,"creative": {"headline": "Step Into Summer","bodyText": "New arrivals, 20% off this week only.","ctaText": "Shop Now","ctaType": "SHOP_NOW","linkUrl": "https://example.com/summer","carouselCards": ["โฆ"],"pageLikeCount": 812345,"pageCategories": ["Shopping"]}}
| Field | Description |
|---|---|
adId | โ
Meta's ad_archive_id โ the stable identifier used for deduplication |
advertiserName | ๐ข Advertiser Page name |
isActive | ๐ข Whether the ad is currently running |
startDateFormatted / endDateFormatted | ๐ Human-readable start/end (see longevity note below) |
adDurationDays | ๐ New. Days between start and end |
creativeFormat | ๐จ New. CAROUSEL / VIDEO / SINGLE_IMAGE / raw Meta code / UNKNOWN, computed from the actual media payload |
mediaAssetCount | ๐ผ๏ธ New. Total cards + images + videos + extra media attached to the creative |
advertiserScaleTier | ๐ข New. micro (<10K likes) โ small โ medium โ large โ mega (10M+), from the advertiser Page's like count |
publisherPlatforms | ๐ Platforms this ad runs on (Facebook, Instagram, Audience Network, Messenger, Threads) |
spend / reachEstimate / impressionsText | ๐ฐ Spend/reach/impressions โ see note below on when these are actually populated |
creative | ๐จ The full renamed creative object: headline, bodyText, ctaText/ctaType, linkUrl, carouselCards, images, videos, pageLikeCount, pageCategories, etc. |
advertiserDetails / transparencyByLocation / verifiedVoiceContext / violationTypes | ๐ฆ Present only when fetchAdDetails is on and Meta returns them for that ad |
A RUN_SUMMARY object is written to the key-value store at the end of every run: totalAdsCollected, duplicatesSkipped, filteredByCreativeFormat, filteredByAdvertiserLikes, creativeFormatBreakdown, advertiserScaleTierBreakdown, avgAdDurationDays, topAdvertisersByAdCount.
๐ฌ Honest notes on real data (verified against a live sample run)
spend/reachEstimate/impressionsTextarenullfor the vast majority of ads. This is not a bug โ Meta's Ad Library only discloses spend/reach/impression ranges for ads Meta classifies as political or social-issue ads in regulated regions. A live sample of 11 ordinary commercial ads (Google, Flipkart, Meesho, Booking.com, etc.) had zero populated spend/reach/impression fields. Don't build spend-based analysis on top of this actor for ordinary commercial ads โ usecreativeFormat,mediaAssetCount,adDurationDays, andadvertiserScaleTierinstead, all of which were populated on every real ad tested.adDurationDaysmeans "days running so far" for an active ad, not a guaranteed final duration. Meta's ownend_datefor a still-active ad is actually its last-indexed/crawl timestamp, not a real campaign end date โ confirmed against real data where every active ad in a sample shared the sameend_date(the scrape date). For an inactive ad,end_dateis the true campaign end andadDurationDaysis the real total lifetime.categoriesis['UNKNOWN']on most ordinary ads โ Meta only populates this for certain regulated ad categories (housing, employment, credit, political). Usecreative.pageCategories(the advertiser Page's own category) for general classification instead.
๐ ๏ธ Fixed vs. the original scraper (verified live)
This actor's Playwright/cookie-bootstrap pipeline had two bugs that made the original scraper fail on every run, discovered and fixed during development:
- Facebook serves a JS "verify" challenge (HTTP 403, self-reloading page) before the real Ad Library page loads. The original code waited for
networkidle, which never resolves because of the reload cycle โ it timed out after 60s on every run. Fixed by waiting for DOM content first, then giving the challenge's reload a bounded window to finish. - After solving that challenge, the original code re-fetched the same URL with a plain HTTP client to discover GraphQL document IDs โ but a plain HTTP client can't run the challenge's JS, so it hit the same wall a second time and found no data. Fixed by reusing the already-challenge-passed page content captured by the browser session instead of a second raw fetch.
- Meta's GraphQL API returns HTTP 200 with a
{"errors": [...]}body when rate-limited, which is invisible to HTTP-level retry logic. Added GraphQL-level rate-limit detection with backoff-and-retry.
All three are verified fixed against a live run that produced real ad data end-to-end.
๐ฏ Best Use Cases
- ๐ต๏ธ Competitive ad intelligence โ see what creatives, formats, and durations competitors are running
- ๐ Creative trend research โ track carousel vs. video adoption across an industry via
creativeFormat - ๐ข Advertiser landscape mapping โ segment micro/local advertisers from mega brands with
advertiserScaleTier - โฑ๏ธ Longevity signal mining โ long-running ads (
adDurationDays) are a proxy for "this creative is performing"
โ ๏ธ Legal & Ethical Notice
- This actor only collects data from Meta's own public Ad Library, which exists specifically for public ad transparency
- Users are responsible for compliance with applicable laws and Meta's Ad Library Terms of Service
- Do not use collected data to target or harass any individual or organization
โ Frequently Asked Questions
Q: Why is spend/reachEstimate empty on my results?
A: Meta only discloses these for political/social-issue ads in regulated regions. It's expected for ordinary commercial ads โ see the honest notes above.
Q: Can I scrape a specific Page's ads instead of a keyword search? A: Yes โ pass the Page's Facebook URL as a source instead of an Ad Library search URL; the actor auto-detects the source type.
Q: Does fetchAdDetails slow the run down?
A: Yes, it adds one extra GraphQL call per ad (bounded to 5 concurrent requests). Turn it off if you only need the core fields.
๐ Support & Feedback
- ๐ Report issues in the Issues tab
- ๐ก Feature requests welcome
- โญ If this actor saves you time, please leave a review!