Meta Ads Library Scraper - Meta Ad Library API
Pricing
from $0.50 / 1,000 ad delivereds
Meta Ads Library Scraper - Meta Ad Library API
Meta Ad Library scraper for competitor ads across the whole Meta Ads Library: Facebook, Instagram, Messenger, Audience Network, WhatsApp and Threads, by keyword, page, ad ID or URL. Every website filter, a count with no ad fee, hard caps and a memory for new ads only. JSON, CSV, API. Pay per ad.
Pricing
from $0.50 / 1,000 ad delivereds
Rating
5.0
(5)
Developer
Automation Craft
Maintained by CommunityActor stats
6
Bookmarked
117
Total users
55
Monthly active users
2 days ago
Last modified
Categories
Share
Meta Ads Library Scraper - Meta Ad Library API
Meta Ads Library Scraper: search the Meta Ad Library (Facebook, Instagram, Messenger, Audience Network, WhatsApp and Threads ads) by keyword, advertiser page, ad ID or a pasted Ad Library URL, apply every filter the Ad Library website has, and export the ads as clean JSON, CSV or Excel. No login, no cookies, no API keys, no code, and you pay per ad you receive, plus Apify's start fee of $0.00005 per run and any add-on you turn on.
Built around the complaints buyers leave on other Ad Library scrapers:
- Every Ad Library filter, applied on Meta's side. Country (all 250 plus "All countries"), ad category (all, political, housing, employment, financial), active status, media type, platform, ad language, impressions date window, sort order, "targeted at country", and the political-only filters: paid-for-by, delivery region and estimated audience size. Filters that Meta applies are filters you do not pay for.
- A cap that is honoured, billing that matches it.
maxAdsandmaxAdsPerQueryare hard caps. You are charged exactly once per delivered ad; duplicates, filtered-out ads, total counts and summaries are free, and a run that delivers no ad still pays the run's start fee ($0.00005 at the default memory). - A total count before you pay, with no ad fee. Tick "Only count matching ads" and you get the Ad Library's own total per query without paying for a single ad (the run's start fee, $0.00005 at the default memory, still applies), so you can size a job first.
- Honest coverage. Every query ends with
totalCount,delivered,coverage(complete,partial,empty) andstopReason(cap,end_of_results,thin_pages,blocked,work_allowance). You are told when the data is incomplete instead of guessing. - EU and political transparency, parsed into columns. Political ads carry spend, impressions and audience-size ranges as numbers; "Fetch ad details" adds EU (DSA) reach by country, age and gender, political age / gender and region distributions, payer / beneficiary data and the advertiser profile (page likes, Instagram followers, lifetime spend by disclaimer).
- Free cross-run memory. Name a memory and later runs return only ads you have not received yet. Schedule it and you have a change feed of a competitor's new ads.
- Derived "winning ad" filters, free. Minimum days running, minimum creative reuse (collation count), creative format, text must / must not contain, exclude pages.
- Who is behind a page, optional. "Page details" adds, per advertiser page, when it was created, each name change with its date, the countries its admins are in, the confirmed owner where Meta shows one, and how many ads it runs right now. A page created last month, renamed four times and run from abroad stands out at a glance.
- The exact page a name matched, optional. "Page match" saves which page each advertiser name was matched to, how, and the other pages it could have been, so you can pin the right page ID and stop paying for ads from a lookalike page.
Filter by placement (Facebook, Instagram, Messenger, Audience Network, WhatsApp, Threads) or leave it empty for every Meta surface.
Quick start (30 seconds)
- Type one or more Keywords (one per line), or paste advertiser pages (facebook.com/nike, a page ID, an Instagram profile), advertiser names, Ad Library URLs (every parameter in the URL is applied) or Ad IDs.
- Pick the Countries and any Ad Library filters you would use on the website.
- Set Max ads. Start. Download JSON, CSV or Excel from the Dataset tab, or read it through the API.
Active status matters for cost. The default, activeStatus: "all", returns ads that are no longer running as well as live ones, as the Ad Library does, and every delivered ad is charged. For live ads only, pick "Active ads only" (activeStatus: "active"); on a big brand that is often a small part of the total. Tick "Only count matching ads" (no ad fee) to see both numbers first.
Prices in one line: $0.50 per 1,000 ads delivered, plus $0.25 per 1,000 for ad details and $0.20 per 1,000 for resolved landing pages, so $0.75 per 1,000 for an ad with details. Duplicates, filtered-out ads and total counts are free. Full breakdown below.
What you get
One item per ad. Nested objects (images, videos, cards, details) for developers, flat columns (imageUrls, spendLower, landingDomain, ...) for spreadsheets. IDs are strings (they exceed JavaScript's number range).
| Field group | Fields |
|---|---|
| Identity | adArchiveId, adLibraryUrl, adId, collationId, collationCount (how many ads share this creative), pageId, pageName, pageProfileUrl, pageProfilePictureUrl, pageLikeCount, pageCategories |
| Status and timing | isActive, startDate, endDate, daysRunning, totalActiveTimeSeconds, publisherPlatforms |
| Creative | displayFormat (IMAGE, VIDEO, CAROUSEL, MULTI_IMAGES, DCO, DPA, TEXT), adText, title, caption, linkDescription, ctaText, ctaType, images[] / imageUrls, videos[] (HD, SD, preview) / videoUrls, cards[] (carousel items with their own text, link and media), extraTexts, extraLinks, extraImageUrls, extraVideoUrls, allText |
| Destination | linkUrl (as served), landingUrl (l.facebook.com wrappers unwrapped), landingDomain, landingUtm, linkIsTemplate (catalogue placeholders such as {{product.name}}), landingResolved when resolution is on |
| Transparency (political) | isPolitical, adCategories, byline (paid for by), disclaimerLabel, spendText / spendLower / spendUpper, currency, impressionsText / impressionsLower / impressionsUpper, audienceSizeText / audienceSizeLower / audienceSizeUpper, transparencyScope (POLITICAL, EU_DSA, NONE) |
| Flags | containsDigitalCreatedMedia (AI disclosure), containsSensitiveContent, isAaaEligible, gatedType, hideDataStatus, stateMediaRunLabel, hasUserReported, regionalRegulation, brandedContent, ecCertificates, brazilTaxId |
| Details (optional) | details.advertiser (page alias, category, likes, verification, Instagram handle and followers, lifetime and weekly spend by disclaimer, entity type), details.eu (targetsEu, totalReach, reachByCountry[], reachBreakdown[] by country x age x gender, age / gender / location targeting), details.political (ageGender[], regions[] with share, singleCountry), details.payerBeneficiary[], details.isAdTakenDown, details.violationTypes |
| Provenance | searchTerm, searchCountry, searchAdType, queryKey, scrapedAt, changeType (NEW, UPDATED, DUPLICATE), isKnown, firstSeenAt, lastSeenAt, contentHash, charged |
Which transparency numbers exist depends on the ad, not on this Actor: Meta publishes spend, impressions and audience size only for ads in the "Issues, elections or politics" category, and EU reach breakdowns only for ads that were shown in the EU. For every other ad those fields are null and transparencyScope says why. Measured fill rates from live runs at build time:
| Field | Commercial ads (US, keyword) | Political ads (US) |
|---|---|---|
adText | 98% | 100% |
title | 79% | 82% |
linkUrl | 93% | 78% |
landingDomain | 92% | 78% |
ctaType | 93% | 77% |
imageUrls | 40% | 10% |
videoUrls | 25% | 55% |
pageLikeCount | 98% | 97% |
spendText | 2% | 100% |
impressionsText | 2% | 100% |
audienceSizeText | 2% | 98% |
byline | 2% | 98% |
collationCount | 80% | 93% |
endDate | 100% | 100% |
Output
type: "ad" items, type: "count" rows in count mode, type: "status" rows when something could not be done (never charged), and one type: "run-summary" item at the end with per-query totalCount, delivered, pages, coverage, stopReason, the resolved advertisers, dedup stats, exact charges, proxy statistics and fill rates.
With "Page match" on, the run's key-value store also holds a RESOLVED_PAGES record: for each advertiser name, the page ID and name it matched, how it matched (page_alias, page_name, instagram_username or first_suggestion) and the other pages the Ad Library suggested, with their likes, verification and Instagram handle.
Sample item from a live run:
{"type": "ad","adArchiveId": "1785179045778580","adLibraryUrl": "https://www.facebook.com/ads/library/?id=1785179045778580","adId": null,"collationId": "974017695598335","collationCount": 1,"isActive": false,"startDate": "2026-05-07T07:00:00.000Z","endDate": "2026-06-29T07:00:00.000Z","daysRunning": 53,"totalActiveTimeSeconds": null,"pageId": "721404351056614","pageName": "IControl: Easy Widgets Themes","pageProfileUrl": "https://www.facebook.com/61578892468353/","pageProfilePictureUrl": "https://scontent-lga3-2.xx.fbcdn.net/v/t39.35426-6/695177587_1511487320476777_920753808075...","pageLikeCount": 4501,"pageCategories": ["Topic"],"pageIsDeleted": false,"publisherPlatforms": ["FACEBOOK","INSTAGRAM","AUDIENCE_NETWORK","MESSENGER","THREADS"],"displayFormat": "IMAGE","adText": "❤️Aesthetic presets for your phone✨","title": "🥰a FREE and personalized APP for you","caption": "play.google.com","linkDescription": null,"linkUrl": "http://play.google.com/store/apps/details?id=com.icontrol.easy.widgets.themes","landingUrl": "http://play.google.com/store/apps/details?id=com.icontrol.easy.widgets.themes","landingDomain": "play.google.com","landingUtm": null,"linkIsTemplate": false,"ctaText": "Install now","ctaType": "INSTALL_MOBILE_APP","images": [{"url": "https://scontent-lga3-3.xx.fbcdn.net/v/t39.35426-6/689490050_1634784394244050_406178336346...","resizedUrl": "https://scontent-lga3-2.xx.fbcdn.net/v/t39.35426-6/691720374_1271927545153646_343053186879..."}],"imageUrls": ["https://scontent-lga3-3.xx.fbcdn.net/v/t39.35426-6/689490050_1634784394244050_406178336346..."],"videos": [],"videoUrls": [],"videoPreviewImageUrls": [],"cards": [],"cardCount": 0,"extraTexts": [],"extraLinks": [],"extraImageUrls": [],"extraVideoUrls": [],"allText": "❤️Aesthetic presets for your phone✨\n🥰a FREE and personalized APP for you\nplay.google.com","adCategories": ["UNKNOWN"],"isPolitical": false,"byline": null,"disclaimerLabel": null,"spendText": null,"spendLower": null,"spendUpper": null,"currency": null,"impressionsText": null,"impressionsLower": null,"impressionsUpper": null,"impressionsIndex": -1,"audienceSizeText": null,"audienceSizeLower": null,"audienceSizeUpper": null,"transparencyScope": "NONE","targetedOrReachedCountries": [],"countryIsoCode": null,"gatedType": "ELIGIBLE","isAaaEligible": true,"containsDigitalCreatedMedia": false,"containsSensitiveContent": false,"hideDataStatus": "NONE","stateMediaRunLabel": null,"hasUserReported": false,"reportCount": null,"isReshared": false,"brandedContent": null,"event": null,"brazilTaxId": null,"additionalInfo": null,"ecCertificates": [],"regionalRegulation": {"finserv": {"is_deemed_finserv": false,"is_limited_delivery": false},"tw_anti_scam": {"is_limited_delivery": false}},"searchTerm": "nike","searchCountry": "US","searchAdType": "ALL","queryKey": "term:nike:US","scrapedAt": "2026-09-01T18:30:38.204Z","changeType": "NEW","isKnown": false,"firstSeenAt": "2026-09-01T18:30:38.204Z","lastSeenAt": "2026-09-01T18:30:38.204Z","charged": true}
How much does it cost to scrape Meta ads?
You pay per event, never per hour, and every price below is the current base price on this listing.
| Charge event | What triggers it | Price |
|---|---|---|
Ad delivered (ad-result) | One ad record delivered to the dataset | $0.50 per 1,000 |
Ad details fetched (ad-details) | Advertiser profile, EU (DSA) reach breakdown, political age / gender / region insights and payer data for one ad, when "Fetch ad details" is on | $0.25 per 1,000 |
Landing page resolved (landing-page-resolved) | Final landing URL and domain after redirects, when "Resolve landing pages" is on, charged only when a page answered | $0.20 per 1,000 |
Actor Start (apify-actor-start) | Apify's standard start event, once per run per gigabyte of memory (on this listing from 22 September 2026). This Actor defaults to 256 MB, so it is one event per run | $0.05 per 1,000 runs ($0.00005 per run) |
Two optional events, off unless you turn them on, are charged from 23 October 2026 (before that date both options work and are free):
| Optional event | What triggers it | Free / Bronze | Silver | Gold |
|---|---|---|---|---|
Page details (optional) (page-details) | Once per advertiser page per run whose details came back, when "Page details" is on | $2.50 per 1,000 pages | $2.25 | $2.00 |
Page match (optional) (page-match) | Once per advertiser name that matched a page, when "Page match" is on; names without a match are free | $0.60 per 1,000 names | $0.55 | $0.50 |
Platinum and Diamond plans pay the Gold price. The other events above have one price on every plan.
An ad with details costs $0.75 per 1,000 ads.
Always free: duplicates inside a run, ads the memory already knows across runs, ads dropped by the post-filters, the total counts from "Only count matching ads", status rows and the run summary. A run that finds nothing still pays the run's start fee.
Worked examples (start event included): 500 ads for three keywords = $0.25; 2,000 competitor ads with details = $1.50; a daily re-run under a memory name that finds 8 new ads among 300 known ones = $0.00405; a run that finds nothing, with no add-on turned on = $0.00005.
Input reference
| Input | What it does |
|---|---|
| Keywords, Keyword matching | The Ad Library search box. "Exact phrase" is the quoted search. |
| Advertiser pages, Advertiser names | Page IDs, page URLs, Instagram profiles or plain names (resolved through the Ad Library's own advertiser lookup; the run summary lists the candidates). Returns the page's "See all ads" view. |
| Ad Library URLs | Paste a URL from your browser; keyword, page, country, ad type, status, media type, platforms, languages, dates, sort and political filters are all read from it. ?id= URLs fetch that single ad. |
| Ad IDs | Library IDs to fetch directly (current status, dates, creative, political numbers). Unknown IDs are reported and free. |
| Countries | Any of the 250 countries or All countries. Several countries = one query each (fair share of the cap). EU expands to the 27 member states (API). |
| Ad category | All, political, housing, employment, financial (the regulated categories). Political ads are the ones with spend and impressions. |
| Active status, Media type, Platforms, Ad languages | Exactly the website's filters. |
| Impressions within / from / to | The website's "Impressions by date": ads that were shown in the window (an ad that started earlier and is still running matches). |
| Sort | Default relevance, impressions high to low / low to high, most recent (grouped by launch month), as the website offers. |
| Only ads targeted at the country | The is_targeted_country switch. |
| Paid for by, Delivery regions, Audience size | Political-only filters. Region names as the website shows them ("California"). Audience buckets: 100-1K, 1K-5K, 5K-10K, 10K-50K, 50K-100K, 100K-500K, 500K-1M, above 1M. |
| Minimum days running, Minimum creative reuse, Creative formats, text filters, Exclude pages | Free post-filters. A run where almost no ad passes them stops early (see "What this Actor does NOT do"). |
| Max ads, Max ads per query | Hard caps on delivered, charged ads. maxItems: 0 through the API means no cap. |
| Only count matching ads | Preflight: one count row per query, no ads and no ad fee (the run's start fee still applies). About 20 queries per run at the default memory and proxy; queries past that end with a "Counts stopped early" warning, so split longer lists. |
| Fetch ad details, Resolve landing pages | Paid add-ons, charged only when data came back. |
Page details (optional), fetchPageDetails | Off by default. Adds pageDetails to each ad: pageCreatedAt, nameChangeCount, nameChangedAt, history, adminCountries (country and number of admins), confirmedOwner (name, city, state, postal code, country, phone where Meta shows them), stateMediaLabel, about and activeAdsCount (ads the page runs now in all countries; 50001 means more than 50,000). Read once per page per run. |
Page match (optional), includePageMatches | Off by default. For advertiser names and page handles: the RESOLVED_PAGES run record and matchedAdvertiserName on each ad. The name lookup itself is unchanged and stays free with or without it. |
| Memory name, Also return already-known ads, Reset memory | Cross-run dedup (named key-value store ads-library-memory-v2-<name> in your account). Known ads are never charged. If Apify's storage cannot open, read or save your memory, the run stops with a status row that says so instead of risking a second charge for an ad you already have; to get the ads at once, run it again with "Memory name (only new ads on later runs)" left empty (every matching ad, including ones you may already have, each delivered ad charged). Give runs that can overlap (a schedule and a manual run, say) their own memory names: two runs with the same memory name at the same moment can both deliver an ad that is new to both, and if they save at the same moment the memory can miss one run's ads, so a later run can deliver them again. |
Stop after N fully-known pages (maxKnownPages) | Used only with a memory name; left empty it is 3. For scheduled change feeds: a query stops after 3 consecutive result pages that bring nothing new: each holds at least one ad the memory already knows, and its other ads are ones your filters dropped or ones this run already returned (stopReason known_pages, coverage partial), instead of paging to the cap. A page whose ads your filters all dropped neither counts nor breaks the streak. A new ad the Ad Library orders behind those pages is not delivered by that run; the "Most recent" sort puts newer ads nearer the top, which makes that less likely. 0 also means 3 on a memory run (0 was the default until 1.3.6, so many saved tasks carry it). To page deeper past known ads, set a higher number, up to 1000 (a run that reads many result pages without a new ad still stops early, stopReason: work_allowance). |
| Proxy, Parallel queries, IP retries, Stop after N empty pages | Advanced. Leave the defaults. |
Using the API
Same input as the form.
curl -X POST "https://api.apify.com/v2/acts/automation_craft~meta-ads-library-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"searchTerms": ["adidas"],"countries": ["US"],"activeStatus": "active","mediaType": "video","publisherPlatforms": ["instagram"],"deliveredWithinDays": "30","maxAds": 100}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('automation_craft/meta-ads-library-scraper').call({advertiserNames: ['Nike'], countries: ['ALL'], fetchAdDetails: true, maxAds: 200, memoryName: 'nike-watch',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.filter((i) => i.type === 'ad').length, 'ads');
from apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("automation_craft/meta-ads-library-scraper").call(run_input={"searchTerms": ["vote"], "countries": ["US"], "adType": "political_and_issue_ads", "bylines": ["VOTEVETS"], "fetchAdDetails": True, "maxAds": 50,})ads = [i for i in client.dataset(run["defaultDatasetId"]).iterate_items() if i["type"] == "ad"]
Input keys are tolerant: country names or codes, enum labels or ids, lowercase or uppercase, strings or arrays, and the common aliases other scrapers use (keyword, maxItems, startUrls, period, startDateMin) are accepted.
Recipes
- Competitor monitoring: advertiser pages +
memoryName, scheduled daily. Only new ads arrive;changeType: UPDATED(with "Also return already-known ads") flags ads whose status, dates or creative changed. - Winning ads: keyword +
activeStatus: active+minDaysRunning: 30+minCollationCount: 3+sortBy: total_impressions_desc. - Political spend research:
adType: political_and_issue_ads+bylinesorregions+fetchAdDetails: truefor age / gender / region distributions and lifetime spend by disclaimer. - EU DSA audits: an EU country +
fetchAdDetails: truefor reach by country, age and gender and the declared targeting. - Sizing a job: the same input with
onlyTotalCount: truefirst (no ad fee). - Vetting advertisers: a keyword such as a product or offer +
activeStatus: active+fetchPageDetails: true. Sort the rows bypageDetails.pageCreatedAtandpageDetails.nameChangeCount: new pages with renamed histories and admins in unexpected countries are the ones to check first. - Pinning the right page: advertiser names +
includePageMatches: trueonce, then copy the page IDs fromRESOLVED_PAGESinto "Advertiser pages" for every later run.
What this Actor does NOT do
- It does not log in and never asks for cookies or tokens, so it only sees what the public Ad Library shows. Spend, impressions and audience size exist for political ads only; EU reach data only for ads shown in the EU. Meta's ranges (">$1M", "100K-500K") are ranges, not exact numbers.
- Very broad keywords: the Ad Library's own result pages thin out after a few hundred ads (
stopReason: thin_pages,coverage: partial). Split the job by country, platform, date window or advertiser page for complete coverage. - It does not search on without end for ads it cannot deliver. A run whose searching costs far more than the ads it delivers (most ads dropped by your filters, already in your memory, repeated, or ad IDs not found) stops early, with one status row at the end (
stopReason: work_allowance,coverage: partial) that says what it read. The ads it delivered are charged as usual; nothing is charged for stopping. For more ads, narrow the search (a more specific keyword, one country, an advertiser page) or relax the post-filters. A search that delivers ads at a normal rate stays well inside this allowance (on our tests a plain keyword search used about half of it; with ad details, landing pages, page details or page matches on, the larger 1.3.10 allowance applies, and a search with ad details used about half of that). Searching that delivers nothing new is kept small: such a run stops after about USD 0.001 of searching (a few result pages on the default proxy), so put many keywords, pages or names whose ads you mostly have already into several smaller runs, and for a memory feed sorted by most recent considermaxKnownPages: 1. Count-only runs count about 20 queries per run at the default memory and proxy, and advertiser names are looked up up to about 50 per run (the names looked up are searched); split longer lists. With "Page details" on and a residentialproxyConfiguration, every advertiser page is read through a residential IP and costs about as much as it is charged, so such runs stop early; leaveproxyConfigurationempty for page details. - It does not download media files; it delivers the CDN URLs (which expire after some days, so download what you need).
- It does not read the aggregate "Ad Library report" spend tables (use Meta Ad Spend Scraper), branded-content listings (use Meta Branded Content Scraper) or the collation "summary" panel.
- Meta rate-limits roughly half of all residential IPs. The Actor rotates IPs automatically and the run summary shows
proxy.blockedIps; a query that cannot get a clean IP is reported ascoverage: blockedand costs nothing.
FAQ
Which platforms does the Meta Ad Library cover?
Facebook, Instagram, Messenger, Audience Network, WhatsApp and Threads. The "Platforms" filter keeps ads that ran on at least one placement you pick; leave it empty for every Meta surface. Every ad carries publisherPlatforms so you can see where it actually ran.
Can I paste a Meta Ad Library URL and get a spreadsheet?
Yes. Drop any facebook.com/ads/library URL into "Ad Library URLs" and every parameter in it is applied: keyword, page, country, ad category, active status, media type, platforms, languages, date range, sort and the political filters. A ?id= URL fetches that single ad. Download the dataset as CSV or Excel from the Dataset tab.
How do I monitor a competitor's new Meta ads automatically?
Put the competitor's page in "Advertiser pages", set a "Memory name" and schedule the run. Ads delivered under that name are remembered, so every later run returns only the ads you have not received yet, and ads the memory already knows are never charged again.
Is there a Meta Ads Library API and what does it cost?
Meta runs its own Ad Library API; this Actor does not use it and never asks you for a token for it. It reads the public Ad Library the way the website does, with no login and no cookies, and you pay Apify $0.50 per 1,000 ads delivered. Tick "Only count matching ads" to see how big a search is before you pay for any ad (the run's start fee still applies).
How fast is it?
Measured on Apify at the default 256 MB on 8 October 2026: the 20 ad example input finishes in 5 to 23 seconds (usually under 10), because the first 30 ads of a search come with the page itself. 100 ads from one keyword took 43 seconds and 300 ads 197 seconds; 300 ads spread over three keywords took 102 seconds, because queries run in parallel ("Parallel queries", default 3). Deep pages of one broad keyword are the slow part, so split big jobs by keyword, country or page. "Page details" added 44 to 60 seconds for 11 to 22 pages (read four at a time).
Does the Actor stop at my limit?
Yes. "Max ads" (maxAds) is a hard cap on delivered and charged ads for the whole run, and "Max ads per query" (maxAdsPerQuery) caps each keyword x country or page x country. Several queries share the total: each gets an equal share first, then unused budget moves to queries that still have results. You never receive or pay for more ads than the cap, and the run summary shows delivered and stopReason: cap per query.
How much will it cost to scrape 1,000 Meta ads?
$0.50 for 1,000 ads as delivered, or $0.75 if you also switch "Fetch ad details" on, plus Apify's start event of $0.00005 for the run itself. Ads your memory already knows, ads your post-filters drop and the free total count are not charged, so a scheduled watch is never charged again for ads it already delivered (each run still pays the start fee).
Why does this Actor run with limited permissions?
Least privilege. It only reads and writes its own storages: the run's dataset, and, when you set a memory name, a key-value store it creates itself (ads-library-memory-v2-<your memory name>). It never needs access to the rest of your Apify account, so it does not ask for it, and schedules and API calls do not raise an approval prompt.
Changelog
- 1.3.12: a run with several keywords or pages reads each one's first result page before paging deeper (with the default proxy), so later keywords get read before the allowance is spent on deep pages of the first ones; a run that searches a lot for few new ads now gets the 1.3.10 allowance for its first 10 ads (USD 0.005 of charges) and nothing more for its next ones until it has charged about USD 0.02 to 0.03 (35 to 60 ads); from there it may spend about a fifth of what it charged (before: two fifths on top of the base), while ad details, landing pages, page details and page matches keep the 1.3.10 allowance; a first page that wrongly claims no result is loaded once more. Same item keys, events and prices.
- 1.3.11: the allowance above measures each run's cost more exactly (the result pages as they arrive over the network, compressed), so searches with strict post filters on "Most recent" are no longer stopped at about half of their allowance; runs that deliver nothing new stop as before. Same item keys, events and prices.
- 1.3.10: the allowance for searching that delivers nothing new is smaller: about USD 0.001 per run, no longer growing with the number of keywords, pages, names or ad IDs (it was up to USD 0.023). Runs that deliver ads at a normal rate are unchanged; count-only runs count about 20 queries per run at the default memory and proxy, runs over many queries whose ads you already have (or your filters drop) stop sooner, and so can a run with paid extras (ad details, landing pages, page details) and only a few ads per query whose result pages answer very slowly, all with the same status row. Same item keys, events and prices.
- 1.3.9: a run whose searching costs far more than the ads it delivers (your filters drop them, your memory already has them, they repeat, or ad IDs are not found) now stops early with a status row at the end (
stopReason: work_allowance) instead of searching to the end. A search that delivers ads at a normal rate is not stopped. Same item keys, events and prices. - 1.3.8: on runs with a memory name,
maxKnownPages0 now means 3, like an empty field (0 was the default until 1.3.6 and stayed in saved tasks and copied inputs); to page deeper past known ads set a higher number, up to 1000. A page of known ads plus ads your filters dropped now counts toward the 3 pages (before, one dropped ad restarted the count, so a filtered feed kept paging). Runs without a memory name are unchanged. Same rows, events and prices. - 1.3.7: a run with a memory name that Apify's storage cannot open, read or save stops with a status row instead of going on without the memory (it could have charged an ad you already have); a save keeps the ads another run with the same memory name saved before it; a restarted run stops the same way when its memory missed ads it charged before the restart; memory names longer than about 41 characters now work. Runs without a memory name are unchanged.
- 1.3.6: runs with a memory name stop a query after 3 result pages that hold only ads the memory already knows (
maxKnownPagesleft empty now means 3 with a memory name; since 1.3.8 a higher number pages deeper, 0 also means 3). Runs without a memory name are unchanged. Same rows, events and prices. - 1.3.5: run messages, the input form and this README say that a run with no ads (an empty run, a count-only run, an input with nothing to search) still pays the run's start fee ($0.00005 at the default memory) instead of calling it free. Nothing else changes: same inputs, rows, events and prices.
- 1.3.4: optional settings other than dropdown selects accept an empty (null) value from Zapier, Make or the API and run as if left out.
- 1.3.3: ad ID and ad link lookups work again after Meta changed how it shows a linked ad (14 October 2026).
- 1.3.0: two optional features, off by default: "Page details" (page creation date, name changes, admin countries, confirmed owner, active ads count) and "Page match" (the
RESOLVED_PAGESrecord and the matched name on each ad). Both are charged as listed above from 23 October 2026 (free before that date). Nothing changes for a run that does not turn them on: same inputs, same output, same prices. - 1.1.0: cheaper runs, same results; Apify's standard start event ($0.05 per 1,000 runs) added, every other price unchanged. The first page a run loads now provides the session tokens (no second page load per run); the memory is written only when it changed; runs default to 256 MB (enough for this HTTP-only engine; up to 1024 MB can still be requested); optional
maxKnownPagesfor cheaper scheduled change feeds (off by default). No input or output changes; ad, details and landing-page prices unchanged. - 1.0.0: first release. Keyword, page, advertiser-name, URL and ad-ID modes; every Ad Library filter; exact caps with fair share; free total count; post-filters; cross-run memory; ad details (EU, political, advertiser); landing-page resolution; per-query coverage reporting.
More data tools by Automation Craft
Same engine, other placements:
More Meta transparency data:
- Meta Ad Spend Scraper: Advertiser Spend by Country
- Meta Branded Content Scraper: Paid Partnerships
- Facebook Page ID Finder: Names to Page IDs
Other marketing and ecommerce data: