Facebook Ads Library Scraper | Meta Ads by Keyword & Advertiser
Pricing
from $1.50 / 1,000 results
Facebook Ads Library Scraper | Meta Ads by Keyword & Advertiser
Search the Meta Ad Library by keyword, advertiser page or pasted search link - any country, active or past ads. Each ad returns its archive ID, advertiser, dates, platforms, ad text, CTA and media links. Failed searches are not charged. Export JSON, CSV or Excel.
Pricing
from $1.50 / 1,000 results
Rating
0.0
(0)
Developer
Philip Kirkbride
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 hours ago
Last modified
Categories
Features
Share
Facebook Ads Library Scraper
Search the Meta Ad Library (facebook.com/ads/library) the way you do on the site: type a keyword, open an advertiser's page of ads, or paste a search link you already filtered. Every ad comes back as one clean row with its Ads Library ID and link, the advertiser's name, page and like count, the dates it started and stopped running, the platforms it runs on (Facebook, Instagram, Messenger, Audience Network, Threads, WhatsApp), the ad text, headline, link, call to action and format, plus links to its images, videos and carousel cards.
Results page through automatically up to your limit, so one search can return hundreds of ads instead of the first screen. No Facebook account is needed.
Use cases
- Competitor ad research: list every ad a competitor is running right now, with the text, offer, landing page and call to action they test.
- Creative inspiration: collect ads for a keyword or niche and study the hooks, formats and media that keep running for months.
- Market and trend monitoring: re-run the same searches on a schedule and see which advertisers start, stop or scale campaigns.
- Lead generation: find businesses that advertise in your market, with their page links and landing pages.
- Political and issue ad tracking: collect ads with their paid-for-by line, and spend and impression ranges where the Ads Library publishes them.
Quick Start
{"searchTerms": ["running shoes"],"country": "US","activeStatus": "active","maxItemsPerSearch": 50}
Every ad of one advertiser, in every country, running or stopped:
{"pageIds": ["15087023444"],"country": "ALL","activeStatus": "all","maxItemsPerSearch": 200}
Input
| Input | Type | Default | Meaning |
|---|---|---|---|
searchTerms | array | — | Keywords to search ad text for, as you would type them in the Ads Library search box. Each term is one search. |
pageIds | array | — | Numeric advertiser page IDs. Each one lists that advertiser's ads. You find the ID in an Ads Library link after view_all_page_id=. |
adLibraryUrls | array | — | Search or advertiser links copied from facebook.com/ads/library. The filters inside a link are applied to that link; a filter the actor cannot reproduce is refused instead of dropped (see Pasted Ads Library links). |
country | string | US | Two-letter country code where the ads were shown (US, GB, DE, BR...), or ALL for every country. |
activeStatus | string | active | active (running now), inactive (stopped) or all. |
mediaType | string | all | all, image, video, meme, image_and_meme or none (ads without an image or video). |
maxItems | integer | empty | Optional total ad limit across all searches; empty means no extra run-wide limit. |
maxItemsPerSearch | integer | 100 | Stop each search after this many ads, up to 5,000. |
You can mix search terms, page IDs and links in one run. Ads already returned by an earlier search in the same run are not repeated.
Empty input runs a small example on its own: {} collects up to 10 ads for running shoes (US, active), and the status message and run summary (usedExample: true) say it was the example. A search field left empty on purpose ({"searchTerms": []}) stops at once with a message naming it. Add your own search to collect what you need instead.
Pasted Ads Library links
A pasted link is reproduced exactly or refused with an explanation. It is never turned into a broader search than the one you filtered, so you are never charged for rows you did not ask for.
| Link parameter | What the actor does |
|---|---|
country, active_status, media_type | Applied to that link; they override the form's values. |
q with search_type=keyword_unordered (the default) | A keyword search for the term. |
view_all_page_id with search_type=page | That advertiser's ads. |
ad_type=all | Applied to that link; it is the only ad type this actor reproduces. |
is_targeted_country=false | Ignored: it is the Ads Library default and the value the actor already uses; any other value is refused. |
fbclid, ref, ref_id, locale, session_id, load_date, sort_data[...], the utm_source/utm_medium/utm_campaign/utm_term/utm_content/utm_id campaign keys and the usual share/tracking keys (mibextid, rdid, share_url, __cft__[...], __tn__) | Ignored: tracking, locale and display order cannot change which ads match. An unfamiliar key is refused rather than skipped as tracking. |
Only filters this actor has verified are applied; the rest are refused rather than passed on as a guess, because a wrong guess would silently run a different search. These links are refused with an error that says what to change:
- an exact-phrase search (
search_type=keyword_exact_phrase): removesearch_type=from the link to search the words as a keyword search, or run the phrase search yourself; ad_typeother thanall,publisher_platforms[...]andcontent_languages[...]: remove them from the link, then filter the returned rows if needed (categoriesfor special ad categories,publisherPlatformsfor where each ad runs);- the date range (
start_date[...],end_date[...]) andexcluded_ids: remove them, or filter the returned rows bystartDate/endDate; is_targeted_countryset to anything butfalse;- any other parameter the actor does not know as a filter;
- an advertiser link that also carries a keyword (
q=), because this actor cannot combine a keyword with an advertiser link — paste the keyword as a search term instead; - a keyword
search_typeon a link withview_all_page_id, orsearch_type=pagewithout one; - a single-ad link (
?id=...); - the same filter twice with different values.
Example queries
{"searchTerms": ["meal kit", "protein powder"], "maxItemsPerSearch": 300}: up to 300 running US ads for each keyword.{"searchTerms": ["solar panels"], "country": "GB", "mediaType": "video"}: video ads about solar panels shown in the UK.{"pageIds": ["15087023444"], "activeStatus": "all", "country": "ALL"}: the advertiser's full ad history across countries.{"adLibraryUrls": ["https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=DE&q=versicherung&search_type=keyword_unordered&media_type=all"]}: a search you set up on the site, pasted as is.
What it returns
The dataset contains one row per ad:
| Field | Meaning |
|---|---|
kind | ad for every dataset row. |
adArchiveId, adLibraryUrl | The Ads Library ID of the ad and its link in the Ads Library. |
pageId, pageName, pageProfileUrl, pageProfilePictureUrl | The advertiser's page. |
pageLikeCount, pageCategories | Page likes and categories, as shown with the ad. |
isActive, startDate, endDate | Whether the ad is running, the date it started and the date it stopped (or the latest date it was seen running), YYYY-MM-DD. |
publisherPlatforms | Where it runs: facebook, instagram, messenger, audience_network, threads, whatsapp. |
displayFormat | IMAGE, VIDEO, CAROUSEL, DCO (dynamic creative), DPA (product catalog) and so on. |
body, title, linkDescription, caption | The ad text, headline, link description and the domain shown on the ad. |
linkUrl, ctaText, ctaType | Where the ad leads and its call-to-action button. |
images | Image links, each with originalUrl and resizedUrl. |
videos | Video links, each with hdUrl, sdUrl and previewImageUrl. |
cards | Carousel and catalog cards, each with its own title, body, linkUrl, linkDescription, caption, ctaText, ctaType and image or video links. |
collationId, collationCount | Shared ID and count when several ads use the same creative. |
categories | Special ad categories (for example POLITICAL, HOUSING) when declared. |
containsSensitiveContent | Whether the Ads Library marks the ad as sensitive; null if not published. |
spend, currency, impressions, reachEstimate | Ranges published for political and issue ads; empty for other ads. |
disclaimer | The paid-for-by line, when the ad carries one. |
searchTerm, searchPageId, searchCountry, searchUrl | The search this row came from. |
scrapedAt | Collection time (UTC). |
The run summary (OUTPUT) lists each search with the number of ads and pages returned and how it ended:
| Field | Meaning |
|---|---|
ads | Ads this search delivered, after removing ads an earlier search already returned. |
pages | Result pages it read. |
duplicates | Ads it matched that the run had already returned. |
complete | true only when the search reached the end of its results, so nothing is missing. |
stopReason | null when complete; otherwise maxItemsPerSearch (its own limit), maxItems (the run-wide limit), chargeLimit (your maximum charge), safetyStop (the source stopped making progress) or error. |
error | NO_RESULTS (nothing matched), NO_NEW_ADS (the search reached the end of its results and everything it matched was already returned by an earlier search in this run), SAFETY_STOP, or the source's error code. A search stopped by a limit, a charge stop or an error before the end of its results leaves error empty rather than claim its matches were all delivered; stopReason says what stopped it. |
OUTPUT.searchesComplete and OUTPUT.searchesIncomplete count the searches that reached the end of their results and those stopped by a limit, a charge stop, a safety stop or an error. OUTPUT.stoppedBy records the run-wide reason, and OUTPUT.skippedSearches lists searches not attempted, which are not charged. OUTPUT.usedExample is true when empty input ran the prefilled example search. Searches that failed or stopped early are also listed in OUTPUT.searchErrors with the number of ads they delivered before stopping. After three searches in a row are refused or slowed down without returning ads, or when the run cannot renew access, it stops.
Output example
{"kind": "ad","adArchiveId": "1702938977100376","adLibraryUrl": "https://www.facebook.com/ads/library/?id=1702938977100376","pageId": "15087023444","pageName": "Nike","pageProfileUrl": "https://www.facebook.com/nike/","pageProfilePictureUrl": "https://scontent-phl2-1.xx.fbcdn.net/v/t39.35426-6/431756959_712871054375981_n.jpg","pageLikeCount": 39500444,"pageCategories": ["Sportswear", "Product/service"],"isActive": true,"startDate": "2025-04-07","endDate": "2026-10-19","publisherPlatforms": ["facebook", "instagram", "audience_network", "messenger"],"displayFormat": "DPA","body": "Bring sports more fully into your day with the Nike App.","title": "{{product.name}}","linkUrl": "http://itunes.apple.com/app/id1095459556","linkDescription": null,"caption": "itunes.apple.com","ctaText": "Install now","ctaType": "INSTALL_MOBILE_APP","images": [],"videos": [],"cards": [{"title": "Nike Run Swift 3 Women's Road Running Shoes","body": "Bring sports more fully into your day with the Nike App.","linkUrl": "https://www.nike.com/ph/t/run-swift-3-road-running-shoes-2fHbzp/DR2698-002","linkDescription": "₱3,995","caption": "itunes.apple.com","ctaText": "Install Now","ctaType": "INSTALL_MOBILE_APP","originalImageUrl": "https://scontent-phl2-1.xx.fbcdn.net/v/t39.35426-6/431679131_949214166636263_n.jpg","resizedImageUrl": "https://scontent-phl2-1.xx.fbcdn.net/v/t39.35426-6/431679131_949214166636263_s600x600.jpg","videoHdUrl": null,"videoSdUrl": null,"videoPreviewImageUrl": null}],"collationId": null,"collationCount": null,"categories": [],"containsSensitiveContent": false,"spend": null,"currency": null,"impressions": null,"reachEstimate": null,"disclaimer": null,"searchTerm": null,"searchPageId": "15087023444","searchCountry": "ALL","searchUrl": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&media_type=all&search_type=page&view_all_page_id=15087023444","scrapedAt": "2026-10-20T05:41:00+00:00"}
Image and video links are shortened here; real links are longer and expire after some days, so download media you want to keep soon after the run.
Good to know
- Results come in the Ads Library's own default order; a sort order pasted in a link is ignored. Matching does not depend on that order, but a run capped by
maxItemsPerSearchormaxItemsreturns the first ads in the default order, not the top ads for another sort. Keyword searches match ad text, so broad words return many advertisers. titleandbodyof catalog ads (DPA) can hold placeholders such as{{product.name}}; the real product names are incards.- Spend, impressions and reach are only published by the Ads Library for political and issue ads (and some regulated regions); they are empty otherwise.
- Ads already returned by an earlier search in the same run are skipped, so overlapping searches do not return duplicates. A search keeps paging past those duplicates, however many pages they fill, and still reaches its later new ads; only a search that reaches the end of its results with nothing new is reported as
NO_NEW_ADS. - If the source keeps repeating a page or serves page after page of ads that search has already seen, that search stops as
SAFETY_STOPwithcomplete: falseinstead of claiming it finished. - If a search stops early, the ads it already returned are kept and the reason is shown in the run summary. If no search returns any ad because of an error, the run fails with a clear message instead of finishing empty. A search with no matching ads is reported in the run summary as no results and is not charged.
maxItemsPerSearchgoes up to 5,000 ads per search. Large searches take a few minutes per thousand ads.- Automated access is subject to Meta's terms; use this actor only where you have the necessary permissions and a lawful basis.
FAQ
Why did I get fewer ads than my limit? A search stops when the Ads Library reaches the end of its results for that keyword or advertiser, and a search with no matches returns zero ads and is not charged. Broad words return many advertisers; narrow or misspelled words can match nothing.
How do I list every ad of one advertiser? Paste the numeric page ID into pageIds (the number after view_all_page_id= in an Ads Library link), or paste the advertiser link itself into adLibraryUrls. Set country to ALL and activeStatus to all for the full history.
Why are spend, impressions and reach empty? The Ads Library publishes those ranges only for political and issue ads (and some regulated regions). For all other ads the fields stay null.
Why did an image or video link stop working? Media links are signed and expire after some days. Download the media you want to keep soon after the run.
What does empty input do? It runs the example search (running shoes, US, active ads, up to 10) so a first run shows real rows immediately; the status message and OUTPUT.usedExample say it was the example. Setting any search term, page ID, link or limit uses your input instead; an empty search list is an input error.
Can I run this daily? Yes. Schedule the run with the same searches and compare startDate/endDate between runs, or read the dataset through the Apify API and export it as JSON, CSV or Excel. The run summary tells you which searches finished completely and which stopped at a limit.
What does a failed search cost? Nothing. Searches that fail or return no matches are not charged, and the run summary is free; only delivered ads are billed.
Pricing
$0.0015 per ad returned plus a $0.00125 start event per run; the Pricing tab has the current rates. The start event is charged per GB of memory, so the default 2048 MB run is two start events.
What a run costs: the Console's sample input (one keyword search, up to 50 ads) returns up to 50 rows, about $0.078 in total — $0.075 in ad charges plus the $0.0025 start. A smaller first run with maxItemsPerSearch: 10 costs about $0.018. Searches that fail are not charged, and the run summary is free. Start with a small maxItemsPerSearch and check the run cost before scheduling larger collections.