πŸ“’ Facebook Ads Library Scraper Plus avatar

πŸ“’ Facebook Ads Library Scraper Plus

Pricing

$15.99/month + usage

Go to Apify Store
πŸ“’ Facebook Ads Library Scraper Plus

πŸ“’ Facebook Ads Library Scraper Plus

The Facebook Ads Library Scraper extracts ad data from Facebook’s Ad Library, including advertiser details, ad text, impressions, and targeting info. Ideal for ad research, marketing insights, and competitor analysis, it delivers clean, structured data for analysis or integration via API.

Pricing

$15.99/month + usage

Rating

2.8

(6)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

5

Bookmarked

404

Total users

1

Monthly active users

12 days ago

Last modified

Share

Facebook Ads Library Scraper β€” Ad Copy, EU Reach and Advertiser Data

Facebook Ads Library Scraper Plus pulls ads from Meta's public Ad Library and returns each one as structured JSON β€” ad copy, CTA, landing URL, creative type, advertiser details, and the EU DSA transparency reach block broken down by age, country and gender. Search by keyword or paste page and Ad Library URLs, filter the results down, and optionally run each ad's copy through an AI model for sentiment, tone and audience enrichment. No HTML parsing, no login required β€” start a run on Apify and download typed JSON in minutes.

What is Facebook Ads Library Scraper Plus?

Facebook Ads Library Scraper Plus is an Apify Actor that queries Meta's public Ad Library for Facebook and Instagram ads and returns each ad as a normalized JSON row β€” copy, creative, timing, advertiser context, and (for EU-delivered ads) the DSA reach breakdown. It does not require a Facebook account, login, or API token from you: the Actor establishes its own public browsing session against the Ad Library using a headless browser. It's built for marketers and researchers running competitor ad monitoring, and for developers who want structured ad data feeding a dashboard, spreadsheet, or AI agent instead of a screen full of ad cards.

What Facebook Ad Library data is publicly available to scrape?

Meta's Ad Library is a public transparency tool β€” every active ad, and (for political, social-issue, housing, employment and credit ads) every historical one for the past several years, is browsable by anyone without a Facebook account. Nothing this Actor returns sits behind a login; the gate here is ad category and delivery region, not authentication.

Data categoryPublicly availableRestricted
Ad creative & copy (text, image, video, carousel, CTA)Shown to any visitorβ€”
Page identity (name, category, profile photo, like count)Shown to any visitorβ€”
Ad timing (start/end date, active status)Shown to any visitorβ€”
Publisher platforms (Facebook, Instagram, Messenger, Audience Network, Threads, WhatsApp)Shown to any visitorβ€”
Spend range & impressions rangeOnly for political/issue ads and ads delivered in the EUWithheld by Meta for ordinary commercial ads outside the EU β€” this Actor reports null, never a fabricated 0
EU DSA reach by age Γ— country Γ— genderOnly for ads delivered in the EUNot published for non-EU delivery
Advertiser "About this page" business details (address, admin country counts, name-change history)Partially shown on the Ad Library's About tabThis Actor's includeAboutPage fetches only the page's public title/description, not the full About-tab business record
Ad account targeting settings (interest lists, custom audiences, exact bid data)Never published by Meta, for anyoneNot available through the Ad Library at all

Facebook Ads Library Scraper Plus only returns publicly visible data β€” what any visitor to the Ad Library sees. Nothing behind a login wall.

What data can I extract with Facebook Ads Library Scraper Plus?

Each dataset row covers ad identity and creative, timing and classification, and β€” where Meta discloses it β€” spend, impressions, EU reach and advertiser context. The dataset's default Overview view surfaces 20 of these as table columns; the fields below are everything the Actor actually writes to each row, read from the output-builder in src/main.py.

Identity, creative & copy fields

Field nameDescription
typeAlways "ad" for a normal row (the onlyTotal mode emits a different row shape β€” see Output below).
isChildAlways false in the current build β€” reserved for collated child ads.
adArchiveIdMeta's ad archive ID.
pageIdAdvertiser's numeric Facebook page ID.
pageNameAdvertiser page name at scrape time.
currentPageNamePage name backfilled from the advertiser block when the listing name is missing.
adLibraryUrlDeep link straight to this ad in the Ad Library (?id=<adArchiveId>).
sourceUrlThe Ad Library search or page URL this row came from.
bodyTextThe ad's main copy.
titleAd title, when present β€” never backfilled from the page name.
captionAd caption, when present β€” never backfilled from bodyText.
ctaText / ctaTypeCall-to-action label and type (e.g. SHOP_NOW).
ctaPresenttrue if either CTA field is set.
linkUrlLanding URL from the ad creative.
linkDescriptionLink description text under the CTA.
destinationDomainHostname parsed out of linkUrl (www. stripped).
displayFormatMeta's creative format label (e.g. MULTI_IMAGES).
creativeTypeDerived: CAROUSEL / VIDEO / IMAGE / TEXT / UNKNOWN, computed from card count, video/image presence and body text.
hasVideo / hasImage / isCarousel / cardCountDerived creative-shape flags and the carousel card count.
imageUrl / videoUrlFirst usable image/video URL found across the snapshot's images, videos and carousel cards.
pageProfileUri / pageProfilePictureUrlAdvertiser page URL and profile photo.
pageCategoriesPage category list (e.g. ["Beauty Salon"]).
snapshotThe full raw creative block (camelCased) β€” cards, extra links/images/videos, disclaimer label, branded-content flag, and everything else Meta returns for the creative, kept for completeness beyond the flattened fields above.

Timing, status & classification fields

Field nameDescription
startDate / endDateUnix timestamps from Meta.
startDateFormatted / endDateFormattedISO-8601 versions of the same.
adDurationDaysDerived: days between start and end, or start and now if still active.
isActiveAd's current active/inactive status.
scrapedAtISO-8601 timestamp for this run.
publisherPlatformArray of platforms the ad runs on (FACEBOOK, INSTAGRAM, etc.), upper-cased.
platformCount / isMultiPlatformDerived count and boolean from publisherPlatform.
containsSensitiveContent / containsDigitalCreatedMedia / isAaaEligibleBoolean flags Meta attaches to the ad.
categoriesAd category list from the listing response.
politicalCountriesCountries the ad is flagged as political/issue in, if any.
entityTypeAdvertiser entity type, e.g. PERSON_PROFILE or a business page type.
archiveTypesMeta's archive-type classification array.
isProfilePageWhether the advertiser page is a personal profile rather than a Page.
hiddenSafetyDataMeta's safety-data-hidden flag.
targetedOrReachedCountriesCountries Meta reports the ad targeted or reached.
collationCount / collationIdMeta's ad-collation grouping fields (near-duplicate ad variants).
adIdMeta's internal ad ID, when set (often null).
totalAdsFromPageTotal ad count Meta reports for the advertiser's page in this query context.

Metrics, EU reach & advertiser fields

Field nameDescription
pageLikeCountAdvertiser page's like count.
currencyCurrency code for spend, when reported.
spendRaw spend range object (camelCased), when Meta discloses it.
spendMidpointDerived midpoint of the spend range.
impressionsText / impressionsLowerBound / impressionsUpperBound / impressionsMidpointMeta's impressions range and its derived midpoint.
reachEstimateMeta's reach estimate object, when present.
euTotalReachTotal EU DSA reach, summed from the age/country/gender breakdown when Meta doesn't report a total directly.
targetsEutrue if any EU reach data, countries, or breakdown rows were found for this ad.
euReachCountriesSorted list of EU countries with reach data.
euReachByCountryAgeGenderArray of {country, ageRange, male, female, unknown} rows β€” the flagship DSA transparency breakdown.
euAgeAudience / euGenderAudienceMeta's own age/gender audience summaries, camelCased.
transparencyByLocationThe full raw transparency_by_location block Meta returns, camelCased, kept alongside the flattened fields above.
advertiserNested object: pageName, pageCategory, pageProfileUri, likes, instagramHandle, instagramFollowers, verification, pageCreationDate, entityType, pageSpend.
advertiserIgHandle / advertiserIgFollowers / advertiserVerification / pageCreationDateThe same four advertiser values flattened to the top level for convenience.

Fields Meta's raw response carries that this Actor does not currently surface: has_user_reported, report_count, menu_items, state_media_run_label and gated_type are present in the intermediate listing response the Actor parses, but are not written into the output row. If your workflow needs them, open an issue (see Feedback below).

Conditional fields, present only when the matching input triggers them:

Field namePresent when
aboutPageincludeAboutPage is on and the source is a Facebook page URL β€” an object with url, title, description (Open Graph tags only).
adSentiment, adTone, adSummary, adTargetAudience, adKeywords, adLanguageaiEnhancement is on and a usable API key was resolved. If enrichment is requested but no key is found, these six keys are absent from the row entirely β€” not null.

πŸ€– Add-on: Need additional Facebook data?

Pair this Actor with the Facebook Comments Scraper with Replies + AI to pull the comment threads under any ad or post the page has published, or with the Instagram B2B Lead & Contact Scraper to turn advertisers you find here into outreach-ready contact records. Both are Scraper Engine Actors and share the same Apify run/export flow.

How does Facebook Ads Library Scraper Plus differ from the official Meta Ad Library API?

Meta publishes an official Graph API endpoint for Ad Library data (ads_archive), and its scope is narrower than what this Actor returns. Per Meta's own reference documentation for that endpoint (developers.facebook.com/docs/graph-api/reference/ads_archive/, checked 2026-07-26): the ad_type filter covers political/social-issue ads, financial products, employment, housing, or all ads, but "ads that did not reach any location in the EU will only return if they are about social issues, elections or politics" β€” meaning the official API excludes ordinary commercial ads outside the EU entirely.

FeatureMeta Ad Library API (ads_archive)Facebook Ads Library Scraper Plus
Ad categoriesPolitical/issue, financial, employment, housing, or all ads via ad_typeSame five categories via adType, plus free-text searchTerms
Non-EU commercial adsExcluded unless the ad is political/social-issueReturned β€” this Actor queries the same public search the Ad Library website uses
AccessRequires a Meta access_token (Graph API app setup)No Facebook login, token or app review β€” just Actor input
Search term lengthCapped at 100 characters, per the docsNo length cap enforced in searchTerms handling
Page-specific searchUp to 10 page IDs per call, comma-separatedOne page URL per line in startUrls, spanning as many sources as one run needs
Rate limitingDocumented error code 613 for exceeded call volume, no published numeric ceilingHandled internally via proxy-tier escalation and retry (see below) β€” nothing for you to manage
Output shapeRaw Graph API JSON matching the ads_archive schemaNormalized camelCase row with derived fields (creativeType, adDurationDays, spendMidpoint, etc.) the raw API doesn't compute

Use the official API directly if you already have a verified Meta developer app and only need political/issue-ad coverage. Use Facebook Ads Library Scraper Plus when you need ordinary commercial ads worldwide, a no-code Apify run, or the derived and enrichment fields the raw API doesn't provide.

How to use Facebook Ads Library Scraper Plus

No input field is required β€” the schema ships with a prefilled example search, so a default run returns results immediately. For real work, replace the source and narrow the scope.

  1. Open Facebook Ads Library Scraper Plus on the Apify Store and click Try for free.
  2. Add your source: leave startUrls empty and use searchTerms + searchCountries for keyword search, or paste one or more Facebook page / Ad Library URLs into startUrls, one per line.
  3. Set the filters worth narrowing before you pay for rows β€” countryCode, activeStatus, adType, mediaType, keywords / excludeKeywords, minPageLikes, minEuReach, and the date-range fields.
  4. Leave isDetailsPerAd on (the default) for the EU reach and advertiser block, or turn on aiEnhancement with an API key for AI ad-copy scoring.
  5. Click Start, then open the run's Dataset tab and export as JSON, JSONL, CSV, Excel, HTML or XML.

How to scale to bulk ad extraction

startUrls is an array β€” add as many page or Ad Library URLs as you want, one per line, and each can carry its own label, limit and period when passed as an object instead of a plain string. In keyword mode, every searchTerms entry is crossed with every searchCountries entry, so 3 terms Γ— 4 countries becomes 12 searches in a single run. resultsLimit caps the total rows across every source in the run (default 10, maximum 50000); there is no separate bulk-input endpoint to call in a loop.

What can you do with Facebook ad data?

  • πŸ•΅οΈ A competitive analyst monitoring a rival brand uses bodyText, ctaText and creativeType to track which hooks and creative formats a competitor is currently running.
  • πŸ“Š A growth marketer planning an EU launch uses euTotalReach and euReachByCountryAgeGender to see which age and country segments a competitor's EU campaigns are actually reaching.
  • βš–οΈ A policy researcher auditing political ad spend uses adType filtering together with politicalCountries and containsSensitiveContent to scope regulated-category ads by country.
  • 🏒 A sales or lead-gen team uses advertiser.pageProfileUri, advertiserIgHandle and pageLikeCount to qualify which advertisers are actively spending on ads before outreach.
  • πŸ€– An AI engineer feeds bodyText plus the optional adSentiment / adTone / adSummary / adKeywords enrichment fields into a RAG pipeline or an LLM agent that tracks a market's messaging trends over time.

Because every field above is typed JSON with stable names, the same dataset can be pulled straight into an agent tool call without any HTML scraping step in between.

How does Facebook Ads Library Scraper Plus handle rate limits and blocking?

Every request starts direct (no proxy) to establish cookies and resolve Facebook's GraphQL doc_ids. If a listing request fails, or the response body contains "checkpoint" or "rate limit", the Actor escalates: first to Apify's datacenter proxy group, then to the residential proxy group, retrying up to 4 times per page with an exponential backoff (2^attempt seconds) between attempts. Once a residential IP succeeds for a source, it becomes "sticky" and is reused for the rest of that source. If you enable Apify Proxy in proxyConfiguration, the Actor skips straight to residential-only routing for the whole run. Session cookies come from a real headless-browser visit to the Ad Library, not spoofed headers alone.

If a listing page still fails after every escalation step, the Actor logs an error and stops that one source β€” it does not fabricate partial results β€” while any other sources in the same run continue independently. There is no published uptime or success-rate figure for this behavior; it is retry logic, not a guarantee.

⬇️ Input

ParameterRequiredTypeDescriptionExample value
startUrlsNoarrayFacebook page URLs or Ad Library links. Each line is one source β€” a plain URL, or an object with url (optional label, limit, period). Legacy field urls is still accepted.["https://www.facebook.com/SHEINOFFICIAL"]
searchTermsNoarrayKeyword-search mode β€” searches the Ad Library without a URL. Each term becomes a search across searchCountries.["vpn", "protein powder"]
searchCountriesNoarrayISO country codes for keyword search; EU codes (DE/FR/IE…) populate the reach block. ALL for worldwide. Defaults to countryCode.["DE", "FR"]
searchModeNostring, default "keyword_unordered"How keyword search matches ad text. Enum: keyword_unordered (match any word), keyword_exact_phrase."keyword_unordered"
resultsLimitNointeger, default 10, min 1, max 50000Upper bound on ad rows returned across all sources in the run. Legacy field limitPerSource is still accepted.200
isDetailsPerAdNoboolean, default trueRuns a per-ad details lookup to add the EU DSA reach block, advertiser info and richer creative. Off = faster, listing-only export with no EU reach.true
onlyTotalNoboolean, default falseOne summary row per source with a total ad count, instead of individual ad rows.false
includeAboutPageNoboolean, default falseFor page sources, attaches the public Open Graph title/description text when available.false
countryCodeNostring, default "ALL"Default region for page sources when the URL doesn't pin one. Enum of 40 ISO country codes plus ALL; see the input schema for the full list."US"
activeStatusNostring, default "all"Filters by ad status for page sources or unspecified Ad Library links. Enum: all, active, inactive."active"
adTypeNostring, default "all"Which Ad Library category to query. Enum: all, political_and_issue_ads, housing_ads, employment_ads, credit_ads."all"
mediaTypeNostring, default "all"Restricts to a creative format, queried and post-filtered. Enum: all, image, video."all"
publisherPlatformsNoarray, default []Keep only ads shown on the selected Meta platforms; empty = all. Enum items: FACEBOOK, INSTAGRAM, MESSENGER, AUDIENCE_NETWORK, THREADS, WHATSAPP.["FACEBOOK", "INSTAGRAM"]
keywordsNoarrayKeep only ads whose copy/title/caption contains at least one of these words.["sale", "free shipping"]
excludeKeywordsNoarrayDrop ads whose copy contains any of these words.["casino", "crypto"]
minPageLikesNointeger, default 0, min 0Keep only ads whose advertiser page has at least this many likes. 0 = no minimum.1000
minEuReachNointeger, default 0, min 0Keep only ads whose EU total reach is at least this value; drops ads without reported reach. 0 = no minimum.5000
onlyAdsNewerThanNostringKeep ads whose start date is on/after this date. Absolute (2025-01-01) or relative (30 days)."30 days"
onlyAdsOlderThanNostringKeep ads whose start date is on/before this date. Absolute or relative."2026-01-01"
periodNostring, default ""Optional time window passed to the Ad Library for page sources. Leave empty for the library default.""
filterDuplicatePageIdsNoboolean, default falseKeep only the first ad from each advertiser page, in addition to the always-on dedup by ad ID.false
aiEnhancementNoboolean, default falseAnalyzes each ad's copy with an LLM for sentiment, tone, summary, audience, keywords and language. Needs an API key below or a matching provider env var.true
aiModelNostring, default "claude-haiku-4-5"AI model/provider β€” auto-detected from the name prefix (claude-*=Anthropic, gpt-*/o1/o3=OpenAI, gemini-*=Google, grok-*=xAI, deepseek-*=DeepSeek, sonar*=Perplexity, mistral-*=Mistral). 14 models in the enum."claude-haiku-4-5"
aiApiKeyNostring (secret)API key for the chosen provider. If empty, the matching env var is used: ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, XAI_API_KEY, DEEPSEEK_API_KEY, PERPLEXITY_API_KEY, MISTRAL_API_KEY."sk-ant-..."
proxyConfigurationNoobjectOptional Apify Proxy. When enabled, the run uses residential endpoints only, for the most natural routing. Requires proxy access on your plan.{"useApifyProxy": false}

Example input

{
"startUrls": [
"https://www.facebook.com/SHEINOFFICIAL"
],
"searchTerms": ["vpn", "protein powder"],
"searchCountries": ["DE", "FR"],
"searchMode": "keyword_unordered",
"resultsLimit": 200,
"isDetailsPerAd": true,
"onlyTotal": false,
"includeAboutPage": false,
"countryCode": "ALL",
"activeStatus": "active",
"adType": "all",
"mediaType": "all",
"publisherPlatforms": ["FACEBOOK", "INSTAGRAM"],
"keywords": [],
"excludeKeywords": ["casino"],
"minPageLikes": 0,
"minEuReach": 0,
"onlyAdsNewerThan": "30 days",
"onlyAdsOlderThan": "",
"period": "",
"filterDuplicatePageIds": false,
"aiEnhancement": true,
"aiModel": "claude-haiku-4-5",
"aiApiKey": "",
"proxyConfiguration": { "useApifyProxy": false }
}

⬆️ Output

One row per ad, written to the run's default dataset as typed, normalized JSON with a consistent schema across runs. Download it as JSON, JSONL, CSV, Excel, HTML or XML from the Dataset tab, or stream it via the API. In onlyTotal mode, rows have a different, smaller shape: {sourceUrl, label, totalAdsCount, scrapedAt}, plus aboutPage when requested.

Billing follows Apify's pay-per-event model: each ad row is billed as one row_result event. The onlyTotal summary row is billed the same way only when a total count was actually determined β€” if a source's page couldn't be resolved or its total count came back unknown, the row is still written to the dataset but is pushed without a charge.

Example output

{
"type": "ad",
"isChild": false,
"adArchiveId": "1234567890123456",
"pageId": "987654321098765",
"pageName": "VoyageVPN",
"currentPageName": "VoyageVPN",
"adLibraryUrl": "https://www.facebook.com/ads/library/?id=1234567890123456",
"sourceUrl": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=DE&media_type=all&search_type=keyword_unordered&q=vpn",
"startDate": 1753500000,
"startDateFormatted": "2025-07-26T06:00:00.000Z",
"endDate": null,
"endDateFormatted": null,
"adDurationDays": 122,
"isActive": true,
"scrapedAt": "2026-07-26T09:12:03.000Z",
"bodyText": "Browse privately from anywhere. 70% off your first year β€” cancel anytime.",
"title": "VoyageVPN β€” Private Browsing",
"caption": "voyagevpn.com",
"ctaText": "Sign Up",
"ctaType": "SIGN_UP",
"ctaPresent": true,
"linkUrl": "https://voyagevpn.com/promo",
"linkDescription": "Get 70% off today",
"destinationDomain": "voyagevpn.com",
"displayFormat": "VIDEO",
"creativeType": "VIDEO",
"hasVideo": true,
"hasImage": false,
"isCarousel": false,
"cardCount": 0,
"imageUrl": null,
"videoUrl": "https://video-fra.xx.fbcdn.net/v/example.mp4",
"pageProfileUri": "https://www.facebook.com/voyagevpn",
"pageProfilePictureUrl": "https://scontent.xx.fbcdn.net/example_profile.jpg",
"pageLikeCount": 48210,
"pageCategories": ["Software Company"],
"publisherPlatform": ["FACEBOOK", "INSTAGRAM"],
"platformCount": 2,
"isMultiPlatform": true,
"containsSensitiveContent": false,
"containsDigitalCreatedMedia": false,
"isAaaEligible": false,
"categories": [],
"politicalCountries": [],
"entityType": "PAGE",
"archiveTypes": [],
"isProfilePage": false,
"hiddenSafetyData": false,
"targetedOrReachedCountries": ["DE", "FR", "AT"],
"collationCount": 1,
"collationId": "9988776655",
"adId": null,
"totalAdsFromPage": 34,
"currency": "EUR",
"spend": { "lowerBound": "2000", "upperBound": "2999" },
"spendMidpoint": 2499.5,
"impressionsText": null,
"impressionsLowerBound": null,
"impressionsUpperBound": null,
"impressionsMidpoint": null,
"reachEstimate": null,
"euTotalReach": 184200,
"targetsEu": true,
"euReachCountries": ["AT", "DE", "FR"],
"euReachByCountryAgeGender": [
{ "country": "DE", "ageRange": "25-34", "male": 41200, "female": 38950, "unknown": 120 }
],
"euAgeAudience": [{ "ageRange": "25-34", "percentage": "0.31" }],
"euGenderAudience": { "male": "0.54", "female": "0.46" },
"transparencyByLocation": { "euTransparency": { "euTotalReach": 184200 } },
"advertiser": {
"pageName": "VoyageVPN",
"pageCategory": "Software Company",
"pageProfileUri": "https://www.facebook.com/voyagevpn",
"likes": 48210,
"instagramHandle": "voyagevpn",
"instagramFollowers": 12800,
"verification": "NOT_VERIFIED",
"pageCreationDate": "2019-03-11T00:00:00.000Z",
"entityType": "PAGE",
"pageSpend": null
},
"advertiserIgHandle": "voyagevpn",
"advertiserIgFollowers": 12800,
"advertiserVerification": "NOT_VERIFIED",
"pageCreationDate": "2019-03-11T00:00:00.000Z",
"snapshot": {
"pageId": "987654321098765",
"pageIsDeleted": false,
"cards": [],
"extraLinks": [],
"extraTexts": [],
"extraImages": [],
"extraVideos": [],
"ecCertificates": []
},
"adSentiment": "positive",
"adTone": "urgent, promotional",
"adSummary": "A discounted VPN subscription ad emphasizing privacy and a limited-time 70% offer.",
"adTargetAudience": "privacy-conscious consumers looking for a discounted VPN",
"adKeywords": ["vpn", "privacy", "discount", "sign up"],
"adLanguage": "en"
}

How does it work?

A headless Playwright browser visits the public Ad Library to collect real session cookies, then the Actor extracts Facebook's current GraphQL doc_ids straight from its JS bundles (static.xx.fbcdn.net) rather than hard-coding them, since Meta rotates these regularly. It then paginates the same public search endpoint the Ad Library website itself uses, in pages of up to 30 ads, and β€” when isDetailsPerAd is on β€” follows up with a per-ad details query for the EU reach and advertiser blocks. Anti-bot handling is the direct-to-datacenter-to-residential proxy escalation described above; there is no CAPTCHA-solving step. Only publicly visible Ad Library data is returned, and the output schema stays stable regardless of Facebook's UI changes β€” though because this path reverse-engineers a public web endpoint rather than calling Meta's official API, Meta can change the underlying GraphQL structure at any time, which is why the doc_ids are resolved fresh on every run instead of being hard-coded.

Integrations

Facebook Ads Library Scraper Plus runs on the Apify platform, so it works with anything that can call Apify's API or trigger an Apify run.

Calling Facebook Ads Library Scraper Plus programmatically

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_API_TOKEN>")
run = client.actor("facebook-ads-library-scraper-plus").call(run_input={
"searchTerms": ["vpn"],
"searchCountries": ["DE", "FR"],
"resultsLimit": 200,
"isDetailsPerAd": True,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["pageName"], item["bodyText"])

Works in Go, Ruby, Node.js, cURL β€” any language that can make an HTTP request.

No-code tools (n8n, Make, LangChain)

In n8n, use the HTTP Request node pointed at the Actor's run-sync-get-dataset-items endpoint with your API token, and drop the response straight into downstream nodes. In Make, the built-in Apify app module can start a run and iterate the resulting dataset without custom code. In LangChain, load results with the ApifyDatasetLoader (or a plain HTTP tool call) to feed bodyText and the AI-enrichment fields into a retriever or agent as documents.

Scraping Meta's public Ad Library is generally lawful, because Meta deliberately publishes this data for advertising transparency and puts none of it behind a login. Facebook Ads Library Scraper Plus returns only what the Ad Library shows any visitor β€” ad creative, copy, timing, and, for regulated or EU-delivered ads, reach and spend figures. This is business and advertising data β€” ad content and advertiser-page identity, not private profile information β€” so terms-of-service and database-rights considerations are the relevant framing here rather than a personal-data regime; a small share of rows may still reference an individual's name where the advertiser is a person's profile rather than a business page (entityType: "PERSON_PROFILE"), so treat any bulk storage of that subset with the same care you'd apply to personal data. Consult legal counsel if your use case involves bulk storage of personal data.

❓ Frequently asked questions

What Facebook ad fields does Facebook Ads Library Scraper Plus return?

The five most commonly used fields are bodyText, ctaText, creativeType, startDate and euTotalReach β€” see the full field tables in What data can I extract above for all 70+ fields.

Does Facebook Ads Library Scraper Plus require a Facebook account or login?

No. The Actor opens its own headless-browser session against the public Ad Library and never asks for your Facebook credentials, an app ID, or an access token.

How many ads can I extract in one run?

resultsLimit accepts any value from 1 up to 50000 and defaults to 10; that cap applies across every source in the run, not per source, unless you set a smaller limit on an individual startUrls entry.

What happens if a keyword search or page returns zero ads?

If a search or page yields zero matching ads, no dataset rows are emitted for that source β€” the run simply finishes with fewer (or zero) rows and a log line noting it, not an error. If a Facebook page URL can't be resolved to a page ID at all (private, removed, or mistyped), the Actor logs an error and skips that source entirely; in onlyTotal mode the same failure instead writes one row with totalAdsCount: null, and that row is not charged.

Can I scrape multiple Facebook Ad Library sources at once?

Yes. startUrls accepts one URL per line, and in keyword mode every searchTerms entry is crossed with every searchCountries entry to build multiple searches automatically β€” both run in the same call, with resultsLimit as the only cap across all of them.

Does Facebook Ads Library Scraper Plus work with Claude, ChatGPT, and other AI agent tools?

Yes, in two ways: the dataset itself is callable as an HTTP endpoint from any agent framework via the Apify API, and the Actor can also enrich each ad's copy directly with Claude, GPT, Gemini, Grok, DeepSeek, Perplexity or Mistral through the aiEnhancement / aiModel / aiApiKey inputs.

How does Facebook Ads Library Scraper Plus compare to other Facebook ad library scrapers?

As observed on the Apify Store on 2026-07-26, none of the three top-ranked competing listings (curious_coder/facebook-ads-library-scraper, apify/facebook-ads-scraper, easyapi/facebook-ads-library-scraper) document a built-in AI ad-copy sentiment/tone/summary enrichment or an EU DSA age Γ— country Γ— gender reach breakdown β€” curious_coder's listing mentions an "EU total reach" toggle but not that breakdown, and apify's listing instead documents an e-commerce product/pricing add-on. curious_coder and apify both require pasting an Ad Library search URL to search by keyword; easyapi's documented input is a searchUrl and maxItems only. This Actor builds the search URL for you from searchTerms and searchCountries directly, with no URL construction step.

Does Facebook Ads Library Scraper Plus return data in a format LLMs can use directly?

Yes. Every row is typed, normalized JSON with consistent field names across runs β€” no HTML parsing or CSS selectors involved. Pass it directly to an LLM prompt, index it into a vector store, or feed it to an agent tool.

What happens when Facebook changes its Ad Library layout or GraphQL structure?

The Actor is maintained and the output schema is designed to stay stable across Facebook UI and internal changes, since doc_ids are re-resolved from Facebook's JS bundles on every run rather than hard-coded. There is no published or contractual turnaround time for fixes if Meta makes a breaking change.

Can I use Facebook Ads Library Scraper Plus without managing proxies or browser infrastructure?

Yes. The Actor runs its own headless browser for session cookies and handles proxy escalation (direct β†’ Apify datacenter β†’ Apify residential) and retries internally β€” you only need to turn on proxyConfiguration if you want every request routed through residential IPs from the start.

Which Facebook ad fields work best for AI training data and RAG indexing?

For RAG, index bodyText, title and caption as the primary text, and β€” when aiEnhancement is on β€” adSummary and adKeywords as supporting metadata. For training data needing consistent structure across records, adArchiveId, startDate, publisherPlatform and creativeType return as stable typed primitives on every row.

Scraper nameWhat it extracts
Facebook Comments Scraper with Replies + AIComment threads and replies from Facebook posts, with optional AI enrichment.
Instagram Reel Virality Intelligence ScraperInstagram Reel performance and virality signals β€” a organic-content counterpart to this ad-focused Actor.
Instagram B2B Lead & Contact ScraperContact and lead data from Instagram business profiles β€” useful for following up on advertisers found here.
LinkedIn Lead & Contact Finder (Google SERP)LinkedIn profile and contact discovery via Google SERP, for cross-platform lead lists.

πŸ’¬ Your feedback

Found a bug, or missing a field you need in the output? Let us know β€” open an issue on the Actor's Issues tab in Apify Console, or reach out through your Apify account's support channel. Reports like the omitted report_count/menu_items fields noted above are exactly the kind of thing that gets fixed fastest when flagged directly.