# Facebook Ads Library Scraper \[$0.13/1K💰] | Meta & IG Ad Spy (`ahmed_jasarevic/meta-ads-library-scraper`) Actor

Spy on any brand's Facebook & Instagram ads with no login or Meta API token. Scrape ad copy, creatives, CTA, landing pages, run dates and EU reach into 46-field JSON rows — and track new, edited and ended ads on a schedule.

- **URL**: https://apify.com/ahmed_jasarevic/meta-ads-library-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.11 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Facebook Ads Library Scraper — Meta & Instagram Ads Without A Login

Extract every ad a brand runs on Facebook and Instagram from the public [Meta Ad Library](https://www.facebook.com/ads/library/) into **50+ field JSON rows** — ad copy, creatives, CTA, landing URL, run dates and EU/UK/BR reach — with **no Meta API token, no login and no browser**. At **$0.13 per 1,000 ads** it is the lowest per-ad price among the Meta Ad Library scrapers on the Apify Store, and it tracks **new, edited and ended ads** across scheduled runs.

### Main Use Cases: Competitor Ad Spying, Creative Research And Ad Monitoring

- **Facebook ad spy / competitor ad analysis** — pull every active ad an advertiser is running, then read the copy, creative, CTA and landing page to reverse-engineer their angles.
- **Creative swipe file** — the raw `image_urls`, `video_urls` and `carousel_cards` let you download the winning creatives in bulk instead of screenshotting them one by one.
- **Ad running-days intelligence** — `running_days` / `total_active_time` show which creatives a brand keeps spending on; long-running ads are the strongest available proxy for a proven winner.
- **Competitor ad monitoring on a schedule** — a `watchlistId` remembers every ad already delivered, so daily runs return only the ads that are `new`, `edited` or `ended`.
- **Dropshipping and e-commerce product research** — search by keyword across a whole niche (e.g. "weight loss", "black friday") and export the full Meta Ad Library result set to CSV or Excel.
- **EU / UK / Brazil transparency and reach data** — for regulated categories the rows carry Meta's own `data_reach`, `impressions_with_index` and `transparency_by_location` breakdowns (age, gender, region).

### How It Works

![How the Meta Ads Library Scraper works: public ad searches become creative rows with copy, media, run dates and transparency data.](https://api.apify.com/v2/key-value-stores/oeuA0kLOyhbRM4fzt/records/how-it-works-meta-ads-library-scraper)

The Actor bootstraps the public Ad Library page to obtain the current session parameters, parses the server-rendered first page, then follows the same paginated GraphQL query the web app uses. Requests run through residential proxies with backoff and proxy rotation when Meta throttles; optional detail queries enrich individual ads.

Each row is one ad with 50+ top-level fields depending on format and detail mode, including advertiser identity, ad copy, CTA, landing URL, image/video/carousel assets, run dates, platforms, reach, spend and transparency objects. Search is by advertiser, keyword or Ad Library URL; no Meta login or official API token is required.

### Extract Facebook & Instagram Ads Without A Meta API Token

The official **Meta Ad Library API** is built for transparency, not research: it returns **political and social-issue ads worldwide plus all ad types only for the EU and the UK** (the last 12 months), requires Facebook Developer credentials and app review, and expires its access token every 60 days. For commercial ads outside the EU/UK it returns nothing.

This actor reads Meta's own public Ad Library response instead, so it returns **commercial ads from every country you name** with no account, no app review and no token. Two things you should know before you scale it up:

- **Country (`searchCountries`) controls the result set**, not the proxy exit country.
- **Pagination needs a residential-class proxy** — Meta returns page 1 to any IP but serves the GraphQL pages only from high-quality IPs (`Rate limit exceeded`, code `1675004`). The actor defaults to Apify Residential and rotates on throttle; pass `proxy` only to bring your own.
- **Meta only exposes reach/impression figures for the transparency categories** (political/issue, housing, employment, credit). For everything else those fields are absent in Meta's own response — this actor does not invent them.

#### Meta Ad Library scrapers compared (verified, 2026-10-09)

| Actor | Price per 1,000 ads (BRONZE) | Notes |
|---|---|---|
| **This actor** | **$0.13** | Cheapest verified; watchlist change tracking; no login |
| `jmlp/meta-ad-library-scraper` | $0.15 | |
| `solidcode/meta-ads-library-scraper` | $0.40 | |
| `memo23/facebook-ads-library-scraper-cheerio` | $0.45 (+ $1/1K details) | Very close input schema |
| `igolaizola/facebook-ad-library-scraper` | $0.50 | |
| `automation-lab/facebook-ads-library` | $0.50 | |
| `azzouzana/meta-facebook-instagram-ads-library` | $0.50 | |
| `scrapesage/facebook-ad-library-scraper` | $0.75 | |
| `dltik/facebook-ads-scraper` | $3.00 | Ad-spy positioning, AI add-ons |
| `brilliant_gum/facebook-ads-library-scraper` | $15.00 | |
| `apify/facebook-ads-scraper` | $5.00 + $3.20 e-commerce add-on | Official Apify, largest in the cluster |

### Facebook Ads Library Scraper Input Parameters

| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| `startUrls` | array | One of\* | Ad Library "nike" URL | Full Meta Ad Library URLs (all filters honoured) or Facebook Page URLs. |
| `advertisers` | array | One of\* | `[]` | Facebook **Page IDs**, Ad Library URLs with `view_all_page_id=…`, or Page URLs — every ad of each advertiser is returned in full. |
| `searchTerms` | array | One of\* | `[]` | Keywords/brand names, one search per term per country. |
| `searchCountries` | array | No | `["US"]` | ISO‑2 country codes or `ALL`; each country is queried per term. |
| `adDetailIds` | array | No | `[]` | Re-fetch specific ads by archive id or `?id=…` link. |
| `adActiveStatus` | string | No | `active` | `active`, `inactive` or `all`. |
| `mediaType` | string | No | `all` | `all`, `image` or `video`. |
| `adType` | string | No | `all` | `all` or a regulated category: `political_and_issue_ads`, `housing_ads`, `employment_ads`, `credit_ads`, `financial_products_and_services_ads`. |
| `publisherPlatforms` | array | No | `[]` | `facebook`, `instagram`, `messenger`, `audience_network`. |
| `contentLanguages` | array | No | `[]` | ISO 639‑1 creative languages (`en`, `de`, …). |
| `searchMode` | string | No | `keyword_unordered` | `keyword_exact_phrase` for phrase matching. |
| `sortBy` | string | No | `""` | `total_impressions` for most-impressions-first. |
| `startDateMin` | string | No | `""` | `YYYY-MM-DD` lower bound on the ad start date. |
| `startDateMax` | string | No | `""` | `YYYY-MM-DD` upper bound on the ad start date. |
| `categories` | array | No | `[]` | Keep only ads whose Meta category matches (case-insensitive). |
| `includeAdReach` | boolean | No | `false` | Keep `data_reach`, `reach_estimate` and impression fields on every row. |
| `scrapeAdDetails` | boolean | No | `false` | One extra GraphQL query per ad → `advertiser`, `aaa_info` and full `transparency_by_location`. Bills the `additional_data` event. |
| `includeTotalActiveAds` | boolean | No | `false` | Ask Meta for the advertiser's total active ad count. |
| `filterDuplicatePageIds` | boolean | No | `false` | Keep only the first ad per `page_id`. |
| `watchlistId` | string | No | `""` | Named memory of which `ad_archive_id`s this run already delivered. Letters, numbers, `-`, `_`. |
| `newAdsOnly` | boolean | No | `false` | Drop already-seen ads (they are not billed). Requires `watchlistId`. |
| `trackChanges` | boolean | No | `false` | Add `change_type` (`new`/`edited`/`unchanged`/`ended`). Requires `watchlistId`. |
| `emitEndedAds` | boolean | No | `false` | Also emit rows for ended ads. Combine with `trackChanges`. |
| `raw` | boolean | No | `false` | Emit only Meta's untouched ad node. |
| `maxItems` | integer | No | `10000` | Hard cap on ads per run. Free Apify plan caps at 30 ads + 1 search target. |
| `detailsConcurrency` | integer | No | `15` | Parallel per-ad detail queries when `scrapeAdDetails` is on. |
| `minDelay` | number | No | `0` | Lower bound of the randomized pause after each GraphQL page. |
| `maxDelay` | number | No | `1` | Upper bound of that pause. |
| `maxConcurrency` | integer | No | `10` | Search targets processed at once. |
| `maxRequestRetries` | integer | No | `8` | Retries with exponential backoff; throttled requests also rotate the proxy. |
| `proxy` | object | No | (empty) | Leave empty — Apify Residential is used by default; set only for your own proxies. |
| `debug` | boolean | No | `false` | Emit session tokens, chosen `doc_id`s and pagination progress. |

\* The schema requires no field formally, but a run needs at least one target: `startUrls`, `advertisers`, `searchTerms` or `adDetailIds`.

### Main Ad Fields Returned By The Facebook Ad Library Scraper

One dataset row per ad (50+ possible fields; recent samples expose 52 top-level keys, depending on ad format and detail mode). The dataset ships **four views** — **Overview**, **Creatives**, **Transparency** and **Changes** — and exports as JSON, CSV or Excel.

**Ad identity (9)** — `ad_archive_id` (row key for dedup and change tracking), `page_id`, `page_name`, `Company Name`, `FB AD Link`, `ad_library_url`, `url`, `FB Link`, `Website`.

**Creative (10)** — `display_format`, `ad_body`, `ad_title`, `cta_text`, `cta_type`, `link_url`, `image_urls`, `video_urls`, `carousel_cards`, and the raw `snapshot` (Meta's untouched creative object).

**Run dates (7)** — `start_date`, `end_date` (unix seconds), `start_date_formatted`, `end_date_formatted` (ISO 8601), `running_days`, `total_active_time`, `is_active`.

**Delivery & transparency (10)** — `publisher_platform`, `categories`, `spend`, `currency`, `impressions_with_index`, `reach_estimate`, `data_reach`, `targeted_or_reached_countries`, `is_aaa_eligible`, plus the detail-only `advertiser`, `aaa_info`, `transparency_by_location`, `verified_voice_context`, `contains_digital_created_media`, `contains_sensitive_content`.

**Search context (5)** — `position`, `total`, `query`, `scraped_at`, plus `change_type` when a watchlist is active. Raw fields such as `snapshot`, `collation_count`, `collation_id`, `menu_items`, `regional_regulation_data`, `fev_info` and the transparency objects are passed through when Meta returns them.

### Example Input And Output: Facebook Ad Library Rows

Example input — an ad spy sweep over one brand keyword in two countries, with watchlist tracking:

```json
{
  "searchTerms": ["nike"],
  "searchCountries": ["US", "GB"],
  "adActiveStatus": "active",
  "mediaType": "all",
  "sortBy": "total_impressions",
  "includeAdReach": true,
  "scrapeAdDetails": true,
  "watchlistId": "nike-daily",
  "trackChanges": true,
  "maxItems": 500,
  "proxy": {}
}
```

Example output (one dataset row, values illustrative):

```json
{
  "ad_archive_id": "1550534860016399",
  "page_id": "115939511520844",
  "page_name": "Depop",
  "Company Name": "Depop",
  "FB AD Link": "https://www.facebook.com/ads/library/?id=1550534860016399",
  "url": "https://www.facebook.com/ads/library/?id=1550534860016399",
  "FB Link": "https://www.facebook.com/depop/",
  "Website": "play.google.com",
  "display_format": "IMAGE",
  "ad_body": "Free Depop Shipping just dropped. Go find something you love.",
  "ad_title": "Super hot widgets app!",
  "cta_text": "Install now",
  "cta_type": "INSTALL_MOBILE_APP",
  "link_url": "http://play.google.com/store/apps/details?id=com.example.app",
  "image_urls": ["https://scontent.xx.fbcdn.net/v/t39.35426-6/728226168_n.jpg"],
  "video_urls": [],
  "carousel_cards": [],
  "start_date": 1779174000,
  "end_date": 1791442800,
  "start_date_formatted": "2026-05-19T07:00:00Z",
  "end_date_formatted": "2026-10-08T07:00:00Z",
  "running_days": 142,
  "total_active_time": 12268800,
  "is_active": true,
  "publisher_platform": ["FACEBOOK", "INSTAGRAM"],
  "categories": ["UNKNOWN"],
  "targeted_or_reached_countries": ["US"],
  "position": 1,
  "total": 23836,
  "query": "nike",
  "change_type": "new",
  "scraped_at": "2026-10-08T14:02:33.300201Z"
}
```

### Competitor Ad Monitoring With Scheduled Watchlist Runs

The **watchlist** is the actor's change-tracking memory. It lives in the actor's key-value store under `watchlist:<id>` and survives across runs — point a schedule at one `watchlistId` and only new/changed ads are billed.

- `newAdsOnly` — already-seen ads are dropped from the dataset (and **not billed**); the search still runs in full. Requires `watchlistId`.
- `trackChanges` — every row gains `change_type`: `new`, `edited`, `unchanged`, plus `ended` for ads that stopped running.
- `emitEndedAds` — also emit rows for ended ads (combine with `trackChanges`).

`ended` is produced only for a search that was **scanned to completion** in that run. If a run stops early (`maxItems` reached, or Meta throttles), nothing is marked ended — so a partial crawl can never mass-close ads. A recommended setup is a **daily schedule** on a competitor's Page ID or a keyword, with `trackChanges: true`, feeding a Slack or Sheets webhook.

### Integrations & Automation

- **Apify API** — start runs, poll status and pull dataset items as JSON; the output `ads` field links to the default dataset items endpoint. Export JSON, CSV or Excel from the console.
- **Webhooks** — fire on run success/failure to push only the `new`/`edited` rows into a pipeline, Slack channel or Google Sheet.
- **Zapier and Make** — trigger workflows on new dataset items (append to Sheets, create CRM/Airtable records, alert on a competitor's new creative).
- **Schedules** — for competitor ad monitoring, run **daily or weekly** against the same `watchlistId`; recurring runs also feed the Apify Store recommendation signals that surface the actor in searches.

### Related Actors

- [apify/facebook-ads-scraper](https://apify.com/apify/facebook-ads-scraper) — the largest and most authoritative actor in the topic cluster (official Apify), covers Facebook, Instagram, WhatsApp, Threads.
- [igolaizola/facebook-ad-library-scraper](https://apify.com/igolaizola/facebook-ad-library-scraper) — the second-largest Meta Ad Library scraper by users.
- [memo23/facebook-ads-library-scraper-cheerio](https://apify.com/memo23/facebook-ads-library-scraper-cheerio) — closest feature/schema parity, "richest schema" positioning with EU reach and transparency.
- [automation-lab/facebook-ads-library](https://apify.com/automation-lab/facebook-ads-library) — no-login Ad Library scraper by keyword, Page URL or pasted URL.
- [solidcode/meta-ads-library-scraper](https://apify.com/solidcode/meta-ads-library-scraper) — Ad Library scraping across Facebook, Instagram, Messenger, Audience Network, Threads and WhatsApp.

### FAQ

#### What is the Meta Ad Library?

The Meta Ad Library (`facebook.com/ads/library`) is Meta's free, public transparency database. It shows all **active** ads running across Facebook, Instagram, Messenger and the Audience Network. Political and social-issue ads are archived for 7 years with spend and reach data; ads that reached the EU are archived for one year. This actor reads that public Ad Library and turns it into structured JSON.

#### Is there a Meta Ad Library API?

Yes, but it is narrow: Meta's official **Ad Library API** returns political/social-issue ads worldwide and all ad types only for the **EU and UK** (last 12 months). It requires Facebook Developer credentials, app review and a token that expires every 60 days — and returns no commercial ads outside the EU/UK. This actor is the alternative that returns commercial ads from any country without a token.

#### What are the limitations of the Meta Ad Library API?

Documented limits (2026): political/EU-only content scope, bucketed spend ranges rather than exact figures, strict per-hour call limits, mandatory app review, and 60-day token expiry. There is no official API for commercial ads outside the EU/UK — which is the gap this actor fills by reading the public Ad Library directly.

#### Do I need a Meta login or API token to use this actor?

No. It runs against the public Ad Library with **no account, no API key and no browser**. The only requirement is that large paginated runs use a residential-class proxy (the actor defaults to Apify Residential, which is already configured) because Meta serves its GraphQL pages only to IPs of that quality.

#### Does this actor return Instagram ads?

Yes. The Meta Ad Library covers Facebook, Instagram, Messenger and the Audience Network; every row reports `publisher_platform`, and you can narrow a run with the `publisherPlatforms` input (e.g. `["instagram"]`).

#### How do I download all ads from the Meta Ad Library?

Give the actor a `searchTerms` keyword, an `advertisers` Page ID, or a full `startUrls` Ad Library URL. It paginates the entire result set automatically (raise `maxItems` for a complete crawl) and writes one row per ad, which you can export as JSON, CSV or Excel — including the `image_urls` and `video_urls` creative assets.

#### Why is my run returning only a few ads?

Two common reasons: (1) on a **free Apify plan** a run is capped at **30 ads and one search target** — paid plans remove both caps; (2) Meta throttled pagination, in which case the actor rotates to a fresh residential IP and reports an empty page rather than silently stopping. Watch the run log (`debug: true`) and raise `maxItems` for a full crawl.

#### What are the alternatives to this actor?

Other Meta Ad Library scrapers on the Apify Store include `apify/facebook-ads-scraper` (official, multi-platform), `igolaizola/facebook-ad-library-scraper`, `memo23/facebook-ads-library-scraper-cheerio`, `automation-lab/facebook-ads-library`, `solidcode/meta-ads-library-scraper` and `jmlp/meta-ad-library-scraper`. Beyond Apify, paid ad-spy tools (Minea, BigSpy, Foreplay, AdSpy) add multi-platform coverage and saved swipe files on subscription, while this actor gives you the raw Meta rows on a pay-per-ad basis.

#### How do I track when a competitor changes their ads?

Set a `watchlistId` and turn on `trackChanges` (add `emitEndedAds` to also receive ads that stopped). Every subsequent run labels each ad `new`, `edited`, `unchanged` or `ended`, so you can detect a rewritten creative or a launched campaign without diffing exports by hand.

### SEO Keywords

facebook ads library scraper, meta ad library scraper, facebook ads library api, meta ad library api, facebook ad spy, ad spy tool, competitor ads analysis, download facebook ads, scrape facebook ads without login, facebook ads library download, facebook ads library search, competitor ad tracking, ad library api, instagram ad library, meta ads library scraper, facebook ads scraper, dropshipping ad spy, ecommerce competitor ads, facebook ads transparency, political ads api, eu ad transparency data, facebook ads library bulk download, ad creative research, ad library alternative

### For AI Agents & LLM Apps

**Purpose:** return structured Meta Ad Library ads (one JSON object per ad, 50+ fields) for a keyword, advertiser Page ID or Ad Library URL, without a login, token or browser.

Minimal working input:

```json
{ "searchTerms": ["nike"], "searchCountries": ["US"], "maxItems": 100 }
```

Variant — ad spy by advertiser Page ID with change tracking:

```json
{ "advertisers": ["15087023444"], "searchCountries": ["US"], "watchlistId": "nike-daily", "trackChanges": true, "maxItems": 200 }
```

Output fields (one dataset object per ad): `ad_archive_id`, `page_id`, `page_name`, `Company Name`, `FB AD Link`, `ad_library_url`, `url`, `FB Link`, `Website`, `display_format`, `ad_body`, `ad_title`, `cta_text`, `cta_type`, `link_url`, `image_urls`, `video_urls`, `carousel_cards`, `snapshot`, `start_date`, `end_date`, `start_date_formatted`, `end_date_formatted`, `running_days`, `total_active_time`, `is_active`, `publisher_platform`, `categories`, `spend`, `currency`, `impressions_with_index`, `reach_estimate`, `data_reach`, `targeted_or_reached_countries`, `advertiser`, `aaa_info`, `transparency_by_location`, `verified_voice_context`, `is_aaa_eligible`, `contains_digital_created_media`, `contains_sensitive_content`, `position`, `total`, `query`, `change_type`, `scraped_at`, plus raw collation, menu, regulation and transparency objects when present.

Behaviors an agent should know:

- At least one target is needed: `startUrls`, `advertisers`, `searchTerms` or `adDetailIds`. A run with none produces no rows.
- `searchCountries` controls the **result set**; the proxy exit country is separate and does not filter results.
- `scrapeAdDetails: true` issues **one extra billed `additional_data` query per ad** and implies `includeAdReach`; `includeTotalActiveAds` adds another request per advertiser.
- `newAdsOnly`, `trackChanges` and `emitEndedAds` require `watchlistId`; without it they do nothing. Already-seen ads skipped by `newAdsOnly` are **not billed**.
- `ended` is only emitted for a search scanned to completion in that run — a run cut short by `maxItems` or throttling marks nothing as ended. Default `maxItems` is `10000`; on a **free Apify plan** a run is capped at **30 ads and one search target**.
- Pagination requires residential-class IPs; leave `proxy` empty to use the built-in Apify Residential rotation (`minDelay`/`maxDelay` pace GraphQL calls; `maxConcurrency` caps targets at 10 by default).
- Data is emitted only for ads Meta actually returns — reach/impression fields exist only for the transparency categories (political/housing/employment/credit) and EU/UK/BR delivery.

Billing: pay-per-event — `apify-actor-start` ($0.00005), `result` ($0.00013 per emitted ad on FREE/BRONZE, lower on higher tiers), `additional_data` ($0.0004 per `scrapeAdDetails` query). Ads filtered out by `newAdsOnly`/`filterDuplicatePageIds` are not billed.

### Legal & Compliance Disclaimer

- This is an independent third-party tool. It is **not affiliated with, endorsed by, sponsored by or connected to Meta Platforms, Inc., Facebook or Instagram**.
- The actor reads only **publicly available** Ad Library data (Meta's server-rendered search page and the same GraphQL queries the public Ad Library web app issues). It does **not** log in, bypass authentication, defeat CAPTCHAs or access private data.
- You are solely responsible for ensuring your runs and your use of the output comply with Meta's Terms of Service and applicable data-protection law. Because rows can include EU/UK ad transparency data, **GDPR** obligations may apply to how you store and process that data.
- The output describes advertisers and their creatives. Do not use it for unlawful targeting or for unsolicited commercial outreach that violates applicable law.
- Data is provided as-is with no warranty of completeness or accuracy; the Ad Library changes constantly and Meta may throttle or alter its responses at any time.

# Actor input Schema

## `startUrls` (type: `array`):

Paste full Meta Ad Library URLs (with any filters) or Facebook Page URLs. This mode supports every Ad Library filter: country, media type, status, keyword/phrase, date range, political/housing/employment/credit category, publisher platform and language. Example: <code>https://www.facebook.com/ads/library/?active_status=active\&ad_type=all\&country=US\&media_type=all\&q=nike\&search_type=keyword_unordered</code> — or a page URL like <code>https://www.facebook.com/Nike</code>.

## `advertisers` (type: `array`):

Scrape every ad from specific advertisers. Enter one or more Facebook <b>Page IDs</b> (the numeric id, e.g. <code>103582046420186</code>), paste an Ad Library URL containing <code>view_all_page_id=…</code>, or a Facebook Page URL. Each advertiser's ads are returned in full. Works on its own or alongside the URL and keyword inputs.

## `searchTerms` (type: `array`):

Keywords or brand names to look up in the Meta Ad Library — e.g. <code>nike</code>, <code>weight loss</code>, <code>black friday</code>. Each term is searched separately against every country below. Leave empty if you are pasting URLs above.

## `searchCountries` (type: `array`):

Two-letter country codes (<code>US</code>, <code>GB</code>, <code>DE</code> …) or <code>ALL</code> for worldwide. Each country is queried separately per search term. Defaults to <code>US</code>.

## `adDetailIds` (type: `array`):

Fetch one or more specific ads by Ad Library archive id, or by a pasted <code>https://www.facebook.com/ads/library/?id=…</code> link. Use this to re-fetch an ad you already know.

## `adActiveStatus` (type: `string`):

Filter by whether ads are currently running.

## `mediaType` (type: `string`):

Filter ads by creative media type.

## `adType` (type: `string`):

<b>All ads</b> covers every commercial ad. The special categories are the regulated transparency categories — those carry impression and reach data.

## `publisherPlatforms` (type: `array`):

Only return ads shown on these platforms. Valid values: <code>facebook</code>, <code>instagram</code>, <code>messenger</code>, <code>audience_network</code>. Leave empty for all platforms.

## `contentLanguages` (type: `array`):

Only return ads whose creative is in these languages. Use ISO 639-1 two-letter codes (<code>en</code>, <code>es</code>, <code>de</code> …). Leave empty for all languages.

## `searchMode` (type: `string`):

<b>Broad</b> matches any of the words; <b>Exact phrase</b> matches the words together.

## `sortBy` (type: `string`):

Default keeps Facebook's own ranking. <b>Most impressions first</b> sorts by total impressions (works best for page searches and EU-transparency ads).

## `startDateMin` (type: `string`):

Only include ads that started running on or after this date. Leave empty to skip.

## `startDateMax` (type: `string`):

Only include ads that started running on or before this date. Leave empty to skip.

## `categories` (type: `array`):

Keep only ads whose Facebook category or page category matches one of these values (case-insensitive), e.g. <code>Software</code>, <code>UNKNOWN</code>. One value per line.

## `includeAdReach` (type: `boolean`):

Keep <code>data_reach</code>, <code>reach_estimate</code> and impression fields on every row. When off, those bulky fields are dropped.

## `scrapeAdDetails` (type: `boolean`):

Issue one extra GraphQL query per ad to attach <code>advertiser</code>, <code>aaa_info</code> and the full <code>transparency_by_location</code> (EU, UK and BR reach with age/gender/country breakdowns). Implies includeAdReach. Adds one request per ad and bills the <code>additional_data</code> event.

## `includeTotalActiveAds` (type: `boolean`):

Ask Meta for the advertiser's total active ad count alongside each result.

## `filterDuplicatePageIds` (type: `boolean`):

If enabled, only one ad per unique page_id is kept (first occurrence).

## `watchlistId` (type: `string`):

Named memory that remembers which ads (by <code>ad_archive_id</code>) this run already delivered. Use the same id on a schedule (for example <code>nike-daily</code>) and later runs leave them out, so a monitored advertiser or keyword bills only the ads that are new. Letters, numbers, hyphens, underscores.

## `newAdsOnly` (type: `boolean`):

Ads already stored on the Watchlist ID are left out of the dataset and not charged. The search itself still runs in full. Requires <code>watchlistId</code>.

## `trackChanges` (type: `boolean`):

Every row gains a <code>change_type</code> of <code>new</code>, <code>edited</code> or <code>unchanged</code>, and ads that disappeared from the search since the last run are emitted with <code>change_type: "ended"</code>. Requires <code>watchlistId</code>. This is how you detect a competitor rewriting a creative.

## `emitEndedAds` (type: `boolean`):

Also emit rows for ads that stopped running since the previous watchlist run. Implies nothing on its own — combine with <code>trackChanges</code>. Requires <code>watchlistId</code>.

## `raw` (type: `boolean`):

Emit only the untouched Meta ad node, without the derived fields (ad link, website, running days, position, query tag).

## `maxItems` (type: `integer`):

Maximum number of ads scraped per run. On a free Apify plan a run is capped at 30 ads and to ONE search target — the first start URL, search-term/country pair or advertiser the input builds. Paying Apify accounts remove both caps.

## `detailsConcurrency` (type: `integer`):

How many per-ad detail queries run at once when <code>scrapeAdDetails</code> is on.

## `minDelay` (type: `number`):

Lower bound of a randomized pause after every GraphQL page. Default 0 — pacing is adaptive: the scraper only backs off when Meta throttles.

## `maxDelay` (type: `number`):

Upper bound of the randomized pause after every GraphQL page.

## `maxConcurrency` (type: `integer`):

Maximum number of search targets processed at the same time.

## `maxRequestRetries` (type: `integer`):

Retries with exponential backoff before a search target is given up on. Throttled requests also trigger a proxy rotation.

## `proxy` (type: `object`):

Leave empty — the scraper routes traffic through Apify Residential proxies and rotates to a fresh IP whenever Meta throttles. Set this only to use your own proxies.

## `debug` (type: `boolean`):

Emit detailed logs including session tokens, chosen doc_ids and pagination progress.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&is_targeted_country=false&media_type=all&q=nike&search_type=keyword_unordered"
  ],
  "advertisers": [],
  "searchTerms": [],
  "searchCountries": [
    "US"
  ],
  "adDetailIds": [],
  "adActiveStatus": "active",
  "mediaType": "all",
  "adType": "all",
  "publisherPlatforms": [],
  "contentLanguages": [],
  "searchMode": "keyword_unordered",
  "sortBy": "",
  "startDateMin": "",
  "startDateMax": "",
  "categories": [],
  "includeAdReach": false,
  "scrapeAdDetails": false,
  "includeTotalActiveAds": false,
  "filterDuplicatePageIds": false,
  "watchlistId": "",
  "newAdsOnly": false,
  "trackChanges": false,
  "emitEndedAds": false,
  "raw": false,
  "maxItems": 10000,
  "detailsConcurrency": 15,
  "minDelay": 0,
  "maxDelay": 1,
  "maxConcurrency": 10,
  "maxRequestRetries": 8,
  "debug": false
}
```

# Actor output Schema

## `ads` (type: `string`):

One dataset item per scraped ad: ad archive ID, advertiser page, creative copy, CTA, landing URL, media URLs, run dates, position in the search and change type when a watchlist is used.

## `watchlist` (type: `string`):

Key-value store keys that hold the watchlist state (already-seen ad archive IDs) between runs.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&is_targeted_country=false&media_type=all&q=nike&search_type=keyword_unordered"
    ],
    "advertisers": [],
    "searchTerms": [],
    "searchCountries": [
        "US"
    ],
    "adDetailIds": [],
    "publisherPlatforms": [],
    "contentLanguages": [],
    "categories": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/meta-ads-library-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": ["https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&is_targeted_country=false&media_type=all&q=nike&search_type=keyword_unordered"],
    "advertisers": [],
    "searchTerms": [],
    "searchCountries": ["US"],
    "adDetailIds": [],
    "publisherPlatforms": [],
    "contentLanguages": [],
    "categories": [],
}

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/meta-ads-library-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&is_targeted_country=false&media_type=all&q=nike&search_type=keyword_unordered"
  ],
  "advertisers": [],
  "searchTerms": [],
  "searchCountries": [
    "US"
  ],
  "adDetailIds": [],
  "publisherPlatforms": [],
  "contentLanguages": [],
  "categories": []
}' |
apify call ahmed_jasarevic/meta-ads-library-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/meta-ads-library-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/p4ACtjEXNv1xLdONG/builds/GGbhc97MCnkJCya2D/openapi.json
