Meta Ad Library Spy — Winning & Scaled Facebook Ads | $4/1K avatar

Meta Ad Library Spy — Winning & Scaled Facebook Ads | $4/1K

Pricing

from $3.88 / 1,000 ads

Go to Apify Store
Meta Ad Library Spy — Winning & Scaled Facebook Ads | $4/1K

Meta Ad Library Spy — Winning & Scaled Facebook Ads | $4/1K

Collect public Meta Ad Library ads by keyword or page ID. Get ad records with creative text, media URLs, delivery signals and platforms. Run with keywords or page IDs; no Meta login or Graph API token required. Pricing: $0.004 per ad on Free.

Pricing

from $3.88 / 1,000 ads

Rating

0.0

(0)

Developer

Vitalii Bondarev

Vitalii Bondarev

Maintained by Community

Actor stats

4

Bookmarked

154

Total users

31

Monthly active users

4 days ago

Last modified

Features

Limited permissions

Share

Meta Ad Library Scraper

Collect public Meta Ad Library ads by keyword or advertiser page ID, with copy, media URLs, delivery signals and platforms; no Meta account or token is required. Enable Apify Proxy with the RESIDENTIAL group: without it, Meta can return blocked or empty pages, and Free-plan runs deliver at most 50 ads.

  • Find scaled creatives with is_scaled, collation_count, days_active and longevity_score.
  • Monitor competitor ads by page ID or search term across countries, including active and inactive ads.
  • Build creative swipe files with copy, CTAs, destination links and inline image or video URLs.
  • Research political and issue ads with spend, impression, reach and EU transparency fields.
  • Export flat records containing advertiser, creative, delivery and platform data.

What you get

A field is null when the source does not provide it; arrays can be null or [] as noted.

Field familyFieldsTypeWhat it contains
Ad identityad_archive_id, ad_snapshot_url, page_id, page_namestringIDs, snapshot URL and advertiser; each can be null.
Advertiser profilepage_profile_uri, page_profile_picture_url, page_like_count, page_categories, bylinestring, integer, arrayProfile, audience, categories and disclaimer; profile values and byline can be null; categories can be [].
Delivery signalsis_active, ad_delivery_start_date, ad_delivery_stop_date, total_active_time, days_active, longevity_scoreboolean, ISO 8601 string, number, integerBoolean status; dates are YYYY-MM-DD or null; total_active_time is passed through or null. Positive numeric duration gives days_active = total_active_time // 86400; otherwise it uses start to stop or run date, never below 0. longevity_score is round(min(days_active / 90, 1.0), 3).
Scale signalsis_scaled, collation_count, collation_idboolean, integer, stringis_scaled is true when collation_count is 3 or more; the other fields can be null.
Ad copyad_creative_bodies, ad_creative_link_titles, ad_creative_link_captions, ad_creative_link_descriptionsarray of stringsBody, headlines, captions and descriptions; arrays can be null.
Destination and CTAlink_url, cta_text, cta_type, display_formatstringLanding page, call-to-action and format; each can be null.
Inline mediasnapshot_images, snapshot_videos, media_count, card_countarray of strings, integerImage/video URLs and counts; URL arrays can be null; counts are set, including 0.
Platformspublisher_platformsarray of stringsFACEBOOK, INSTAGRAM, MESSENGER, AUDIENCE_NETWORK, THREADS; [] when none are provided.
Ad metricscurrency, spend_lower, spend_upper, spend_midpoint, impressions_lower, impressions_upper, impressions_midpoint, reach_estimate, has_metricsstring, integer, booleanPublished spend, impressions and reach; metric integers and reach_estimate can be null; reach uses the lower bound.
Targeting and trusttargeted_or_reached_countries, contains_digital_created_media, contains_sensitive_content, categories, entity_typearray, boolean, stringTargeting, content and classification; arrays are [] when empty; scalar values can be null.
Transparencystate_media_run_label, regional_regulation_datastring, objectState-media and regional disclosures; either can be null.
Data qualityparse_confidence, warningsnumber, array of stringsParsing confidence and warnings; warnings is [] or can contain missing_ad_archive_id, missing_page_name, missing_creative, missing_start_date or no_media_or_link.
Run metadatascraped_atISO 8601 stringOne UTC scrape time for every row in a run; always set.

Sample output

The sample is shortened to selected fields from one dataset record.

{
"ad_archive_id": "388120997678322",
"ad_snapshot_url": "https://www.facebook.com/ads/library/?id=388120997678322",
"page_id": "1497383683837785", "page_name": "adidas",
"is_scaled": false, "collation_count": null, "collation_id": null,
"days_active": 601, "longevity_score": 1, "is_active": false,
"ad_delivery_start_date": "2024-09-26", "ad_delivery_stop_date": "2026-05-20",
"ad_creative_bodies": ["Conheça e compre todas as coleções e os principais lançamentos da adidas pelo app!"],
"link_url": "http://play.google.com/store/apps/details?id=com.adidas.app",
"cta_text": "Shop now", "cta_type": "SHOP_NOW", "display_format": "DPA",
"media_count": 6, "card_count": 6,
"publisher_platforms": ["FACEBOOK", "INSTAGRAM", "AUDIENCE_NETWORK", "MESSENGER"],
"has_metrics": false, "parse_confidence": 1, "warnings": [],
"scraped_at": "2026-11-04T05:29:04.147984+00:00"
}

Pricing

This Actor uses pay-per-event pricing. Prices below are for the Free plan.

EventPrice
apify-actor-start$0.00005 per event on the Free plan
apify-default-dataset-item$0.004 per event on the Free plan

The apify-actor-start event is charged once per GB of run memory, minimum one event. No price is shown for either the ad-record or ad-record-political event.

On the Free plan, 1,000 ads cost $4.00 and 10,000 ads cost $40.00, plus the start event(s) of each run.

You pay only for the listed events. This Actor does not use Pay per event + usage, so compute, proxy traffic and storage are not billed separately.

The $5 Free-plan credit covers 24 full runs (1,200 ads) at the 50-ad cap; the exact Free-plan result calculation is 1,249 ads assuming one start event.

How to use

  1. Enter searchTerms, numeric pageIds, or both.
  2. Choose two-letter country codes; each term or page ID is searched for each country.
  3. Select the ad category, active status, media type and keyword match type.
  4. Set maxResults per search and country, and pageSize for streaming batches.
  5. Enable Apify Proxy with the RESIDENTIAL group in proxyConfiguration.
  6. Run the Actor, review the records, and repeat through Apify Schedules; daily is a starting point.
{
"searchTerms": ["nike"],
"countries": ["US"],
"adType": "all",
"activeStatus": "all",
"mediaType": "all",
"matchType": "broad",
"maxResults": 100,
"pageSize": 25,
"proxyConfiguration": {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}
}

Export the dataset as JSON or CSV, or read it through the Apify dataset API.

Input parameters

ParameterWhat it doesDefault
searchTermsKeywords; each runs separately. If this and pageIds are empty, the run uses nike.Empty
pageIdsNumeric advertiser page IDs, searched for every country.Empty
countriesTwo-letter codes, trimmed and upper-cased. Invalid codes are skipped with a run-log warning; US is used if none remain.["US"]
adTypeall or political_and_issue_ads.all
activeStatusall, active or inactive.all
mediaTypeall, image, video, meme or none (text-only).all
matchTypebroad or exact phrase matching for search terms.broad
maxResultsMaximum per term or page ID per country; 1–10000. Other values use the nearest limit; non-numeric values use 100.100
pageSizeAds per streaming batch; 1–100. Other values use the nearest limit; non-numeric values use 25.25
docIdPersisted-query ID; empty extracts the live ID on each page.Empty
proxyConfigurationApify Proxy; a URL is requested per search attempt when set.{"useApifyProxy": false}
disableUsageStatsTurns off the run summary described in Usage statistics when true.false

Limits and edge cases

ConditionWhat you seeWhat to do
Free Apify planAt most the smaller of maxResults and 50 ads are delivered in total.Use a paid Apify plan for uncapped output.
Several terms, page IDs or countriesCountries, then terms, then page IDs are processed; duplicate ad IDs are removed across the run.Use distinct searches for separate result sets.
Every search is blocked or emptyThe run succeeds with an empty dataset; the status is a sentence and OUTPUT.emptyReason gives the reason. SOURCE_EMPTY means only seen ads or the Free cap produced no new rows.Broaden the search, try another country, use activeStatus=all, check filters, or enable RESIDENTIAL.
A search uses a proxy on its first pageUp to 4 attempts are made.Enable RESIDENTIAL and run again later.
A search has no proxy on its first pageUp to 2 attempts are made, reusing the same address.Enable RESIDENTIAL and run again later.
A proxy attempt is madeA fresh proxy URL is requested on each attempt.Enable RESIDENTIAL and run again later.
RESIDENTIAL sees 2 clean pages with no adsThe search stops and is reported as NO_MATCH_FOR_INPUT.Check the keyword, page ID, country and filters.
Every attempt for a search failsThe search is reported as BLOCKED.Enable RESIDENTIAL and run again later.
An empty page is returned without RESIDENTIALThe search is reported as BLOCKED.Enable RESIDENTIAL and run again later.
A first-page failure occursOnly a first-page failure raises the block.Review the status and rerun the search.
The cost guard is checked before an attemptThe search can end early.Try fewer inputs or rerun later.
A later page fails or returns no adsThat search ends; ads already read are kept and the final partial batch is delivered. It is not retried.Review the dataset and rerun the affected search if needed.
A batch reaches pageSize rowsThe batch is pushed to the dataset.Use pageSize to control held rows.
A search finishes with a partial batchThe final partial batch is pushed.Use a smaller pageSize for smaller final batches.
Fewer than pageSize rows are heldHeld rows are neither pushed nor charged.Use a smaller pageSize.
A row is pushedIt is charged immediately after the push.Review the dataset and charging records.
An abort, timeout or crash occursPushed rows stay; held rows are not written.Use a smaller pageSize.
An abort, timeout or crash occurs before completionNo OUTPUT record is written because it is written only at the end.Review rows already pushed.
Cost protection stops a partial runDelivered ads remain; MARGIN_GUARD is written to the default key-value store and the status says results are partial.Review the partial results and rerun with fewer inputs if needed.
Cost protection stops a run with adsOUTPUT.stoppedEarly = "MARGIN_GUARD" and the run finishes normally.Review the partial results.
Cost protection stops a run with search failuresOUTPUT.searchFailures lists the failures.Review the listed failures.
Cost protection stops a run with zero adsOUTPUT.emptyReason = "BLOCKED" and stoppedEarly = "MARGIN_GUARD"; the run fails.Enable RESIDENTIAL and rerun later.

FAQ

How much does it cost to scrape Meta Ad Library ads?

On the Free plan, apify-default-dataset-item costs $0.004 per event and apify-actor-start costs $0.00005 per event. On the Free plan, 1,000 ads cost $4.00 plus the start event(s) of each run.

Why did my run return no ads?

A blocked or empty run can succeed with an empty dataset. Check emptyReason in OUTPUT, broaden the search or filters, try another country, or enable RESIDENTIAL.

What should I do if Meta blocks requests?

Enable an Apify RESIDENTIAL proxy group in proxyConfiguration. Retry behavior is described in Limits and edge cases.

The Actor reads public data shown by the Meta Ad Library without logging in. Source terms and applicable law depend on your use case; review them for your planned use. This Actor does not provide a legal guarantee.

Usage statistics

This Actor creates a small, content-free summary at the end of each run, used only to monitor reliability and improve this Actor. No input values, credentials or scraped data are sent.

A copy of the exact record for your run is saved as USAGE_STATS in your own Apify key-value store.

To opt out, set disableUsageStats to true in the input. Nothing is sent then.

Full list of collected fields