Facebook Ad Library Scraper — Facebook Ads by Keyword & Country avatar

Facebook Ad Library Scraper — Facebook Ads by Keyword & Country

Pricing

from $2.00 / 1,000 ads

Go to Apify Store
Facebook Ad Library Scraper — Facebook Ads by Keyword & Country

Facebook Ad Library Scraper — Facebook Ads by Keyword & Country

Facebook ads by keyword, advertiser page or country, from the Ad Library: advertiser, page ID, flight dates, live status, platforms, creative text, landing page, media URLs and EU spend bands. From $2.00 per 1,000 ads; transcripts are an opt-in add-on. A search that finds nothing is never charged.

Pricing

from $2.00 / 1,000 ads

Rating

0.0

(0)

Developer

Steadyfetch Team

Steadyfetch Team

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Type a keyword, an advertiser's name or paste an Ad Library link and get their Facebook and Instagram ads back as structured rows. That link can be a single ad's own link (…/ads/library/?id=123…), a bare ad ID, or an advertiser's page link; a single ad is a first-class input here, not a workaround. One JSON row per ad: the advertiser and page ID, when the ad started and stopped, whether it is still running, the platforms it runs on, the ad's headline, caption and body copy, its landing page, its video or image URL, the collation it belongs to, and the EU spend and impressions bands where Meta publishes them. From $2.00 per 1,000 ads on the Business plan ($4.00 on the free plan), platform usage included, no start fee. You are charged only when an ad row lands in your dataset: a search that matches nothing, a walled or rate-limited fetch and an ad whose row cannot be read all cost $0.

Using an AI agent? Pin this actor in Apify's MCP server with one link: https://mcp.apify.com?tools=steadyfetch/facebook-ad-library-scraper

  • Actor id: steadyfetch/facebook-ad-library-scraper
  • Input: { "searchQueries": ["fitness app"] } — the one field you have to set.
  • Cap the bill: set maxTotalChargeUsd on the run (a run option, not Actor input), e.g. 0.50 — the run stops when it reaches it.
  • Your cap is the cap. maxAds (default 100) caps the whole run and searchMaxAds (default 25) caps each keyword or page — the run never delivers or bills a row past either. If your input carries maxItems, resultsLimit, limit or count instead — the names other Ad Library scrapers use — the smallest of them is read as maxAds and one uncharged note row says so. Add maxTotalChargeUsd and the run stops at whichever comes first; rows already in hand ship, uncharged if they could not be billed.

Just want to see it work? Click Start with nothing set and the run is a 3-ad sample — currently running US ads matching "fitness app", charged like any run (about $0.012 on the free plan, $0.006 on Business). Set only the settings — a country, an ad format, a cap — and no keyword or link, and that same sample runs under your settings, with one uncharged note row saying which ones it used.

Want the ad's words too? Turn on Add the ad's transcript (includeTranscript) and every ad with a video creative is also transcribed — transcript, the first-3-seconds hook3s, language, durationSeconds and timestamped segments ride the same row. That is charged as one extra Ad transcript, and only when a transcript is actually delivered. If transcripts are the whole reason you are here, our Facebook Ads Transcript Scraper is built around them and also reads image-ad text and on-screen text on silent ads.

What you get

FieldWhat it is
adArchiveIdThe ad's Ad Library ID — the one stable key.
adLibraryUrlA link straight back to the ad in Meta's Ad Library.
pageName · pageId · pageProfileUriThe advertiser: their page name, its numeric ID and their profile link.
pageCategories · pageLikeCount · pageIsDeleted · bylineHow Meta files the advertiser, their follower count, and the disclaimer byline on political ads.
isActive · startDate · endDate · totalActiveTimeSecondsThe flight: whether it is still running, and when it ran.
publisherPlatforms · countriesWhere the ad runs — Facebook, Instagram, Messenger, Audience Network, Threads — and the countries Meta names for it.
displayFormat · videoUrl · imageUrlThe creative: its format, and a direct link to the video or image.
title · caption · linkDescription · linkUrlThe ad's headline, its display URL and the page it sends people to.
adText · adTextIsTemplateThe ad's body copy — and a flag that is true when Meta serves a dynamic-product placeholder ({{product.name}}) instead of words, so you can filter template ads out instead of reading them as copy.
ctaText · ctaTypeThe call-to-action button.
categories · gatedType · containsSensitiveContent · containsDigitalCreatedMedia · disclaimerLabelMeta's own classification of the ad, including its AI-generated-media flag.
collationId · collationCountOne creative running under several ad IDs: the group, and how many ads are in it.
currency · spend · impressionsText · reachEstimateThe EU spend, impressions and reach bands, where the law makes Meta publish them.
transcript · hook3s · language · durationSeconds · segmentsOnly with Add the ad's transcript on.
searchQuery · sourceIndexWhich keyword, page or link this row came from, so a multi-query run sorts itself.
status · charged · statusReasonWhat happened to this ad, whether it was billed, and why — on every row.

What is null, and why. spend, impressionsText and reachEstimate are filled only where the law makes Meta publish them — EU-targeted and political ads. On an ordinary US ad they are null, and that is the Ad Library's answer, not a scrape miss. The advertiser's phone number, business address and page history live on the advertiser's About tab, not on the ad, and are not on this row. Every one of the 50 fields is present on every row: a value or null, never a column that comes and goes.

Column names are ours and they stay put. Meta's wire is read in one place and renamed once, so a Meta rename does not rename your columns, and a fixed-schema table or sheet never breaks on a new row. What a field means is in the table above, not in a Facebook internal name.

What a row looks like

One delivered row from a real run — the keyword fitness app, 2026-09-12. One ad row is one Ad charge; the transcript fields are null because this run did not turn transcripts on. The advertiser's landing page and the creative's media link are shown here as placeholders — Facebook signs the media URL and expires it within days.

{
"status": "delivered",
"charged": true,
"statusReason": null,
"adArchiveId": "1507887230933843",
"adLibraryUrl": "https://www.facebook.com/ads/library/?id=1507887230933843",
"searchQuery": "fitness app",
"pageId": "102806245720688",
"pageName": "Troponin Supplements",
"pageProfileUri": "https://facebook.com/TroponinSupplements",
"pageCategories": ["Vitamins/supplements"],
"pageLikeCount": 1632,
"pageIsDeleted": false,
"byline": null,
"isActive": true,
"startDate": "2026-03-20T07:00:00.000Z",
"endDate": "2026-09-12T07:00:00.000Z",
"totalActiveTimeSeconds": null,
"publisherPlatforms": ["FACEBOOK","INSTAGRAM","AUDIENCE_NETWORK"],
"countries": null,
"displayFormat": "DCO",
"title": "{{product.name}}",
"caption": "troponiniq.com",
"linkUrl": "https://…",
"linkDescription": null,
"adText": "Always Intelligent, Never Artificial",
"adTextIsTemplate": false,
"ctaText": "Sign up",
"ctaType": "SIGN_UP",
"videoUrl": null,
"imageUrl": "https://…",
"categories": ["UNKNOWN"],
"gatedType": "ELIGIBLE",
"containsSensitiveContent": false,
"containsDigitalCreatedMedia": false,
"disclaimerLabel": null,
"collationId": "2432929267140392",
"collationCount": 1,
"currency": null,
"spend": null,
"impressionsText": null,
"reachEstimate": null,
"transcriptStatus": null,
"transcript": null,
"hook3s": null,
"hookStartSeconds": null,
"language": null,
"durationSeconds": null,
"segments": null,
"chargeEvents": {"adRow":1,"transcript":0,"surchargeMinutes":0},
"sourceIndex": 1,
"#k": "ad:1507887230933843",
"#ce": {"ad-row":1}
}

How often the data changes

Meta's Ad Library updates continuously: new ads appear within hours of going live, and an ad's isActive and endDate change the day an advertiser stops it. For competitor monitoring a daily run is the useful cadence; for a market survey, weekly. The spend, impressions and reach bands on EU political ads move slowly — monthly is enough. Video and image URLs are signed by Facebook's CDN and expire within hours to days, so fetch the creative soon after the run, or re-run to get fresh links.

What one run can reach

Each keyword or advertiser page returns at most searchMaxAds ads (default 25; up to 500 per keyword — ask for more and the run continues at 500) and the whole run at most maxAds (default 100; up to 10,000 per run — ask for more and the run continues at 10,000, with one uncharged row saying so). Meta's own search stops serving deep pages a few thousand results in, whatever its counter says — so a broad keyword is better split by country or ad format than pushed to a huge cap. The run's status line and its OUTPUT record say exactly what happened: how many ads were listed, how many rows delivered and charged, how many your date filters dropped, and how many sat beyond your cap — never a silent stop. If one search stops short because its pages stopped carrying ads new to the run — usually because another of your keywords had already listed them — an uncharged note row says exactly that, names the run's own limit as the run's own, and never reports it as a verdict about the advertiser.

  • Newest ads first? There is no sort control — the Ad Library serves its own order. To get the newest, set startedAfter to a recent date and leave activeStatus on Currently running; the search runs, older ads are dropped uncharged, and the rows that land are the recent ones.

What is never charged

  • A search that returns no ads — including a keyword that matches nothing and an advertiser with nothing running in that country.
  • A search Facebook walled, rate-limited or refused: it comes back as one uncharged row that says so and can be retried — never as an empty run that looks like success.
  • An Ad Library link that will not resolve, or names an ad Meta has removed.
  • An ad whose row could not be read from Meta's response.
  • Ads dropped by your own startedAfter / startedBefore window (they filter on the ad's start date, after the search) and ads beyond your maxAds cap.
  • With Add the ad's transcript on: a silent or music-only ad, an image ad, an expired video link, and any transcription that failed. The ad row is still delivered; only the transcript goes unbilled.
  • A time limit or a cost cap ends the collecting, never the delivering: rows already in hand ship in full, and any that could not be billed ship uncharged and say so.

Pricing

EventWhat it isFree planBusiness
AdOne ad row delivered to your dataset.$0.004$0.002
Ad transcriptOne video ad transcribed, only with includeTranscript on, only when a transcript is delivered. First 3 minutes included.$0.020$0.010
Long-video minute (surcharge)One started minute beyond the included first 3 minutes of a transcribed video.$0.005$0.005

Platform usage is included in those prices — there is no start fee and no compute bill on top.

Notes

If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.

Something wrong or missing? Open an issue on this actor's Issues tab and we will answer there.

Unofficial. Not affiliated with, endorsed by, or sponsored by Meta Platforms, Inc.