Facebook Ad Scraper avatar

Facebook Ad Scraper

Pricing

$19.99/month + usage

Go to Apify Store
Facebook Ad Scraper

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

Scraper Engine

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

6 hours ago

Last modified

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 adId per run and drops exact repeats before they reach your dataset
  • ๐ŸŽจ Creative-format classification โ€” every ad is classified as CAROUSEL / VIDEO / SINGLE_IMAGE from 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_SUMMARY written 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 whose adId was 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 adds advertiserDetails, transparencyByLocation, verifiedVoiceContext, violationTypes when Meta exposes them
  • ๐ŸŒ Country/date filtering โ€” dateRange preset, 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
}
FieldTypeDescription
adLibrarySourcesarray๐Ÿ” Ad Library search URLs or Facebook Page URLs (required)
proxyConfigobject๐Ÿ›ก๏ธ Apify Proxy config (required)
maxAdsPerSourceinteger๐Ÿ“Š Max ads to collect per source URL (0 = unlimited)
fetchAdDetailsboolean๐Ÿ“ฆ Fetch an extra per-ad details call (slower, adds enrichment-only fields)
dateRangestring๐Ÿ“… Date preset for search sources (LAST_90_DAYS, LAST_30_DAYS, LAST_7_DAYS, LAST_3_DAYS, TODAY, or empty)
pageAdsActiveStatusstring๐ŸŸข Active status filter when scraping a Page URL directly (all/active/inactive)
pageAdsCountryCodestring๐ŸŒ Country filter when scraping a Page URL directly
deduplicateAdsboolean๐Ÿ”„ Skip already-seen adId values in this run (default true)
creativeFormatFilterarray๐ŸŽจ Keep only ads with a computed format in this list (empty = keep all)
minAdvertiserLikesinteger๐Ÿข 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"]
}
}
FieldDescription
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 / impressionsText are null for 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 โ€” use creativeFormat, mediaAssetCount, adDurationDays, and advertiserScaleTier instead, all of which were populated on every real ad tested.
  • adDurationDays means "days running so far" for an active ad, not a guaranteed final duration. Meta's own end_date for 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 same end_date (the scrape date). For an inactive ad, end_date is the true campaign end and adDurationDays is the real total lifetime.
  • categories is ['UNKNOWN'] on most ordinary ads โ€” Meta only populates this for certain regulated ad categories (housing, employment, credit, political). Use creative.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:

  1. 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.
  2. 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.
  3. 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"

  • 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!