# Facebook Ads Library Scraper (`coregent/facebook-ads-library-scraper`) Actor

Search the public Facebook/Meta Ads Library by keyword, advertiser Page, or Ads Library URL. Get normalized ad copy, creative media, CTA, advertiser data, delivery dates, platforms and public transparency ranges. Optional advertiser and landing-page enrichment. No Facebook login needed.

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

## Pricing

from $1.00 / 1,000 ad results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

Search and scrape the public Facebook/Meta Ads Library by keyword, advertiser Page, or Ads Library URL. Get normalized ad copy, creative media, CTA, advertiser metadata, delivery dates, platforms, countries, and public transparency fields. Optional advertiser and landing-page enrichment are charged separately, and **no Facebook login or cookies are required from you**.

![Facebook Ads Library Scraper](https://raw.githubusercontent.com/coregentdevspace/facebook-ads-library-scraper-assets/main/thumbnail-facebook-ads-library-scraper.png)

***

### What this Actor does

Meta publishes every ad running on Facebook, Instagram, Messenger, WhatsApp, Threads and Audience Network in its public Ads Library. This Actor turns that into a dataset.

- **Three ways in** — keywords, advertiser Pages, or a full Ads Library URL you paste from your browser. Mix them freely in one run.
- **One row per unique ad**, deduplicated by Meta's own ad archive ID across every keyword, advertiser and country in the run.
- **The complete creative** — ad copy, headline, description, call-to-action, destination link, every image and video URL, and every carousel card.
- **Results stream in as they are found.** The dataset fills during the run, not at the end.
- **A global result limit that is shared fairly.** One broad keyword cannot swallow the whole run before your other inputs have been searched.
- **A free count-only mode** to size a search before you commit to it.
- **No credentials, ever.** No login, no cookies, no session ID, no access token, no ad-account access.

***

### 1. Search by keyword

```json
{
  "searchTerms": ["running shoes", "marathon training"],
  "countries": ["US", "GB"],
  "adStatus": "active",
  "maxResults": 200
}
```

Each keyword is searched separately in each country, so this is four searches sharing one limit of 200 ads. Turn on `exactPhrase` to match the whole phrase rather than loose words — far more precise for brand and product names.

### 2. Search by advertiser Page

```json
{
  "pageUrlsOrIds": [
    "https://www.facebook.com/nike",
    "@adidas",
    "15087023444"
  ],
  "countries": ["US"],
  "adStatus": "all",
  "maxResults": 500
}
```

Page URLs, `@handles` and numeric Page IDs all work. Handles are resolved to the advertiser's Ads Library Page ID automatically.

> **If an advertiser you know is advertising comes back with zero ads**, give the numeric ID instead. Open their Ads Library page in a browser and copy the `view_all_page_id=` value out of the URL. Facebook keys its Ads Library on a different Page ID from the one on their public Page, and while this Actor resolves it correctly for nearly every advertiser, the numeric ID always works.

### 3. Use an Ads Library URL

Build the search you want on `facebook.com/ads/library`, then paste the URL:

```json
{
  "startUrls": [
    "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=GB&q=gym&search_type=keyword_unordered&media_type=video"
  ],
  "maxResults": 100
}
```

**The filters already in the URL win** over the settings in the form. If they disagree, the run log says exactly which URL and which filters — your pasted search is never silently changed into a different one. Any parameter this Actor does not support is reported and dropped rather than ignored in silence.

### 4. Country, status and date filters

| Input | What it does |
|---|---|
| `countries` | ISO country codes. The Ads Library shows the ads running **in** a country, so each is its own search. |
| `adStatus` | `active`, `inactive`, or `all`. |
| `dateFrom` / `dateTo` | Bounds on the ad's **delivery start date**. |
| `mediaTypes` | Image, video, meme, image-and-meme, or text-only. |
| `adType` | Meta's regulated categories — politics/issues, housing, employment, credit. |
| `publisherPlatforms` | Facebook, Instagram, Messenger, WhatsApp, Threads, Audience Network. |
| `advertiserNameInclude` / `advertiserNameExclude` | Keep or drop by advertiser name. |

Everything except the last two is applied by Facebook itself, so ads that do not match are never fetched and never charged for.

> **A note on `dateFrom`.** Facebook's own date filter matches ads *delivering* in a window, so a long-running ad that started years ago still matches it. This Actor's field says "started on or after" and enforces exactly that, which means it may return fewer ads than the equivalent filter on facebook.com. That is deliberate — a field named "started on or after" that returns ads which started earlier is worse than one that returns fewer.

### 5. Output schema

One row per unique ad:

```json
{
  "recordType": "facebook_ad",
  "adArchiveId": "1016451784037642",
  "adLibraryUrl": "https://www.facebook.com/ads/library/?id=1016451784037642",
  "status": "active",
  "advertiser": {
    "pageId": "15087023444",
    "name": "Nike",
    "pageUrl": "https://www.facebook.com/nike",
    "adsLibraryUrl": "https://www.facebook.com/ads/library/?...&view_all_page_id=15087023444...",
    "profilePictureUrl": "https://scontent.../n.jpg",
    "categories": ["Sportswear Store"],
    "likeCount": 39000000,
    "isDeleted": false,
    "isVerified": null
  },
  "delivery": {
    "startDate": "2026-08-01",
    "endDate": null,
    "countries": null,
    "publisherPlatforms": ["facebook", "instagram"],
    "totalActiveTimeSeconds": null
  },
  "creative": {
    "body": "Move with everything you've got.",
    "headline": "Nike Pegasus 41",
    "description": "Free delivery for members.",
    "caption": "nike.com",
    "ctaType": "SHOP_NOW",
    "ctaText": "Shop Now",
    "linkUrl": "https://www.nike.com/us/en_us",
    "displayFormat": "DPA",
    "media": [{ "type": "video", "url": "...", "thumbnailUrl": "..." }],
    "cards": [{ "headline": "...", "linkUrl": "...", "media": [] }]
  },
  "transparency": {
    "categories": [],
    "fundingEntity": null,
    "spend": null,
    "currency": null,
    "impressions": null,
    "reach": null,
    "demographics": null
  },
  "collation": { "collationId": "1443586990871629", "collationCount": 1 },
  "source": {
    "inputType": "advertiser",
    "searchTerm": null,
    "pageInput": "https://www.facebook.com/nike",
    "country": "US"
  },
  "advertiserDetails": null,
  "landingPage": null,
  "scrapedAt": "2026-09-10T09:41:12.004Z"
}
```

#### Five dataset views

Every run writes **one row per unique ad**. The five views are projections of that same row — nothing is fetched twice and nothing is charged twice. Pick a view above the results table in the Console, or append `?view=creatives` to the dataset API URL.

![Facebook Ads Library Scraper — all output fields in the Console table view](https://raw.githubusercontent.com/coregentdevspace/facebook-ads-library-scraper-assets/main/facebook-ads-library-scraper-output-all-fields-table-view.png)

The samples below are **real rows from one real run** — the keyword `running shoes`, US, both enrichments on. Four of the five are the *same ad* (`4037862493156385`, a New York shoe shop) so you can see exactly what each view keeps and what it drops. Long ad copy and signed media URLs are shortened here with `…`; the dataset carries them in full.

> The table above shows dotted column names like `advertiser.name` — that is how the **table** addresses a nested field. The **JSON** the API returns keeps the nesting, exactly as printed below.

##### Ads — `?view=overview`

The default. One row per ad: who ran it, when, where it was shown, and the core creative.

```json
{
  "adArchiveId": "4037862493156385",
  "adLibraryUrl": "https://www.facebook.com/ads/library/?id=4037862493156385",
  "status": "inactive",
  "advertiser": {
    "name": "Tip Top Shoes",
    "pageId": "231908974693",
    "pageUrl": "https://www.facebook.com/tiptopshoe/"
  },
  "delivery": {
    "startDate": "2025-04-01",
    "endDate": "2026-05-02",
    "publisherPlatforms": [
      "facebook",
      "instagram",
      "audience_network"
    ]
  },
  "creative": {
    "body": "👟✨ Since 1940, Tip Top Shoes has been NYC’s sole mate! 💫\n\nWhether you're a local strolling the Upper West Side or a traveler hunting for the perfect fit, Tip Top Shoes has everything …",
    "headline": "All Your Favorite Brands",
    "ctaType": "LEARN_MORE",
    "linkUrl": "https://tiptopshoes.com/",
    "displayFormat": "VIDEO"
  },
  "collation": {
    "collationCount": 1
  },
  "source": {
    "searchTerm": "running shoes",
    "country": "US"
  },
  "scrapedAt": "2026-09-22T23:38:24.234Z"
}
```

##### Advertisers — `?view=advertisers`

The advertiser behind each ad, plus everything **advertiser enrichment** adds. Without that add-on the `advertiserDetails` fields are `null` and the rest still populates.

```json
{
  "advertiser": {
    "name": "Tip Top Shoes",
    "pageId": "231908974693",
    "pageUrl": "https://www.facebook.com/tiptopshoe/",
    "adsLibraryUrl": "https://www.facebook.com/ads/library/?active_status=all&ad…",
    "categories": [
      "Shoes"
    ],
    "likeCount": 16472,
    "isVerified": false
  },
  "advertiserDetails": {
    "emails": [
      "info@tiptopshoes.com"
    ],
    "phones": [
      "(212) 787-4960"
    ],
    "website": "https://tiptopshoes.com/",
    "address": {
      "formatted": "155 W 72nd St, New York, NY, United States, 10023"
    },
    "metrics": {
      "followers": 16000,
      "recommendPercent": 82
    },
    "business": {
      "priceTier": 3
    }
  },
  "adArchiveId": "4037862493156385"
}
```

##### Creatives — `?view=creatives`

The full creative payload — every image and video URL, and for carousel and catalogue ads a `creative.cards` array with one entry per card. This ad is a single video, so `cards` is `[]`.

```json
{
  "adArchiveId": "4037862493156385",
  "advertiser": {
    "name": "Tip Top Shoes"
  },
  "creative": {
    "displayFormat": "VIDEO",
    "body": "👟✨ Since 1940, Tip Top Shoes has been NYC’s sole mate! 💫\n\nWhether you're a local strolling the Upper West Side or a traveler hunting for the perfect fit, Tip Top Shoes has everything …",
    "headline": "All Your Favorite Brands",
    "description": "155 W 72nd St, New York, NY 10023",
    "caption": "tiptopshoes.com",
    "ctaType": "LEARN_MORE",
    "ctaText": "Learn more",
    "linkUrl": "https://tiptopshoes.com/",
    "media": [
      {
        "type": "video",
        "url": "https://video.fclo12-1.fna.fbcdn.net/o1/v/t2/f2/m366/AQOd_…",
        "sdUrl": "https://video.fclo12-1.fna.fbcdn.net/o1/v/t2/f2/m412/AQNIJ…",
        "hdUrl": "https://video.fclo12-1.fna.fbcdn.net/o1/v/t2/f2/m366/AQOd_…",
        "watermarkedUrl": null,
        "thumbnailUrl": "https://scontent.fclo12-1.fna.fbcdn.net/v/t39.35426-6/4877…"
      }
    ],
    "cards": []
  },
  "adLibraryUrl": "https://www.facebook.com/ads/library/?id=4037862493156385"
}
```

##### Transparency — `?view=transparency`

Meta publishes spend, impressions and reach **only** for political, issue, housing, employment and credit ads, and only ever as ranges. This sample is therefore a **different ad from the same run** — on the shoe-shop ad above, every one of these fields is `null`, which is the correct answer rather than a gap.

```json
{
  "adArchiveId": "518237284022783",
  "advertiser": {
    "name": "Many Hopes"
  },
  "transparency": {
    "categories": [
      "POLITICAL"
    ],
    "fundingEntity": "ONE HOME MANY HOPES INC",
    "spend": "$400K - $450K",
    "currency": "USD",
    "impressions": ">1M",
    "reach": ">1M",
    "stateMediaRunLabel": null
  },
  "delivery": {
    "startDate": "2024-08-26",
    "endDate": "2026-03-09"
  },
  "adLibraryUrl": "https://www.facebook.com/ads/library/?id=518237284022783"
}
```

##### Landing pages — `?view=landingPages`

Where the ad actually sends people, plus everything **landing-page enrichment** pulls off that destination.

```json
{
  "adArchiveId": "4037862493156385",
  "advertiser": {
    "name": "Tip Top Shoes"
  },
  "creative": {
    "linkUrl": "https://tiptopshoes.com/"
  },
  "landingPage": {
    "domain": "tiptopshoes.com",
    "finalUrl": "https://tiptopshoes.com/",
    "title": "Tip Top Shoes of New York - Dedicated to comfort footwear since 1940!",
    "description": "Tip Top Shoes is a family-owned shoe store located in NYC, offering the best selection for fitness, comfort, style &amp; leisure.",
    "emails": [],
    "phones": [
      "800-925-5464"
    ],
    "socialLinks": {
      "instagram": "http://instagram.com/tiptopshoesnyc",
      "youtube": "https://www.youtube.com/channel/UCAhzt73mR2P7ZY7CQnLF3SA",
      "facebook": "http://facebook.com/tiptopshoe"
    }
  }
}
```

#### How to read nulls

| Value | Meaning |
|---|---|
| `[]` | We looked, and this ad publishes none. |
| `null` | Meta does not publish this for this ad. |

Two cases worth knowing about, because both are honest answers rather than gaps:

- **`delivery.countries` is usually `null`.** Meta does not publish which countries an ordinary ad runs in. The country you *searched* is in `source.country`, where it belongs — writing it into `delivery.countries` would state as fact something the source never said.
- **`transparency.spend`, `impressions` and `reach` are usually `null`.** Meta publishes these only for political, issue, housing, employment and credit ads, and only ever as **ranges** — `"€50K-€60K"`, `">1M"`. They are kept exactly as published. This Actor will never convert a range into a fake exact number.

There is also no star rating anywhere in the output. Facebook retired 5-star Page ratings; `advertiserDetails.metrics.recommendPercent` carries the real signal.

### 6. Count-only / preflight mode

```json
{ "searchTerms": ["running shoes"], "countries": ["US", "GB", "AU"], "onlyTotalCount": true }
```

Returns one row per search with the number of matching ads and **charges nothing** — no ad-result events at all. One request per search.

> Facebook reports this figure as a **live estimate** and **caps it at 50,000**. A `totalCount` of `50001` means "50,000 or more" and nothing further; rows carry `atProviderCeiling: true` when that happens. Narrow by country, date or advertiser to get an exact number.

### 7. Optional enrichment

Both are **off by default** and each is charged as its own separate event, only when it actually returns data. This is the input that produced every output sample in §5:

```json
{
  "searchTerms": ["running shoes"],
  "countries": ["US"],
  "maxResults": 100,
  "includeAdvertiserDetails": true,
  "includeLandingPageEnrichment": true
}
```

#### Advertiser details

Fetches the advertiser's own Facebook Page for public business data the Ads Library does not carry: **email, phone, WhatsApp, website, street address and coordinates, price range, services, service areas, opening status, recommend percentage and review count**, plus the verified badge.

**Charged once per unique advertiser, never per ad.** An advertiser with 200 ads in your results is a single charge.

> **Expect around 88% coverage, not 100%.** Measured on a 52-advertiser run: 46 Pages returned business data and 6 did not, because those Pages are not publicly readable at all — no address, proxy or retry reaches them. A Page that cannot be read is reported as such and **is never charged**, so the gap costs you nothing but is worth knowing before you plan around it.

#### Landing-page enrichment

Follows each ad's destination link and extracts the page **title, meta description, emails, phone numbers and social links**.

**Charged once per unique domain, never per ad.** Fifty ads pointing at one shop is a single charge. Requests are guarded against private-network access and capped in size and time.

### 8. Pricing and charging

This Actor is **pay-per-event**: you pay for results, not for runtime. There is **no Actor-start fee, no flat fee and no rental** — a run that finds nothing costs you nothing.

This README deliberately quotes **no figures at all**, not even free-tier credit amounts — every one of them can change, and the Pricing tab is the only place that cannot go stale. Please don't reintroduce numbers here.

| Event | Charged |
|---|---|
| `ad-result` | Once per unique ad returned. |
| `advertiser-enrichment` | Once per unique advertiser Page enriched. Opt-in. |
| `landing-page-enrichment` | Once per unique destination domain enriched. Opt-in. |

**You are never charged for:**

- an ad you already received in the same run, however many of your keywords or advertisers found it;
- an ad removed by your filters;
- an ad already in your `skipAdIds` list;
- a count-only run;
- an enrichment that failed, was blocked, or returned nothing;
- the run summary record.

**Full creative details are free.** `includeAdDetails` is on by default and costs nothing — the media, cards and CTA text arrive in the same response as the base ad, so switching it off makes rows narrower rather than cheaper.

Your **per-run spending limit is respected**: the run stops cleanly when it is reached and tells you so, rather than continuing to produce rows it cannot bill for.

### 9. Incremental runs

```json
{
  "pageUrlsOrIds": ["https://www.facebook.com/nike"],
  "dateFrom": "2026-09-01",
  "skipAdIds": ["1016451784037642", "1384448050545020"]
}
```

`adArchiveId` is Meta's own stable identifier, so it is safe to store and feed back. Ads in `skipAdIds` are dropped **before** anything is fetched, enriched or charged, and are counted separately from duplicates in the run summary — so a scheduled run that correctly skipped 900 known ads does not look like one that wasted 900 requests.

### 10. Run summary

Every run writes a `RUN_SUMMARY` record to the key-value store: inputs, searches planned and run, provider requests, raw/unique/filtered/emitted ad counts, duplicates, skip-list hits, enrichment counts, retries, throttling, bandwidth, stop reason, time to first result, and exactly what was charged.

### 11. Public data and no login

This Actor reads only the **publicly accessible** Ads Library and public destination pages. It never asks for and never accepts a Facebook username, password, cookie, session ID, browser profile or access token, and it has no access to private ad-account analytics or authenticated advertiser data.

You remain responsible for lawful use of the data, and for compliance with Meta's terms and any privacy obligations that apply to you.

### 12. Proxy

Use **Apify Residential** proxy — it is the default, and it is what this Actor is built for.

**Datacenter and no-proxy runs are capped at roughly the first 30 ads per search.** Facebook serves page one of every search as ordinary HTML, which any address can read, but rate-limits the request that fetches page two onwards. Measured on the platform: from the Apify datacenter pool that request comes back "Rate limit exceeded" on the very first attempt, so the run finishes successfully having returned only page one. The run warns loudly when this happens rather than reporting a short result as a complete one.

You can still choose datacenter or no proxy — a run that only needs ~30 ads per search, or a count-only preflight, works fine on either and costs less.

You can also use the proxy editor's **Custom proxy URLs** to route through your own provider account, which is honoured in full:

```
http://user:pass@proxy.iproyal.com:12321
http://user:pass@brd.superproxy.io:33335
http://user:pass@proxy.oxylabs.io:7777
```

### 13. API example

```bash
curl -X POST "https://api.apify.com/v2/acts/coregent~facebook-ads-library-scraper/runs?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "searchTerms": ["running shoes"],
        "countries": ["US"],
        "adStatus": "active",
        "maxResults": 100
      }'
```

Then read the dataset:

```bash
curl "https://api.apify.com/v2/datasets/<datasetId>/items?view=creatives&token=$APIFY_TOKEN"
```

Every input has a default, so a minimal call needs only one of `searchTerms`, `pageUrlsOrIds` or `startUrls`.

### 14. Limits worth knowing

- **The total count is capped at 50,000** by Facebook and is an estimate. See §6.
- **A single very broad keyword has a practical depth limit.** Adding countries, or narrowing by date, returns more distinct ads than paging deeper into one query.
- **Delivery countries and spend are not published for ordinary ads.** See §5.
- **Advertiser enrichment reaches about 88% of advertisers.** The rest have no publicly readable Page. See §7.
- **Advertiser enrichment is slow for large brands** — some Pages are several megabytes. It is opt-in for that reason.

### 15. Support

Found an advertiser that resolves wrongly, a field that should be mapped, or a filter that behaves unexpectedly? Open an issue on the Actor page with the input you used and a run ID.

# Actor input Schema

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

Keywords or phrases to search the public Ads Library for — the same box you would type into on facebook.com/ads/library. Each term is searched separately in every country you select, so 2 terms × 3 countries is 6 searches sharing one result limit.

## `pageUrlsOrIds` (type: `array`):

Facebook Page URLs, @handles, or numeric Page IDs. Returns every ad that advertiser is running in the countries you select. A handle is resolved to its Ads Library Page ID automatically; if an advertiser you know is advertising comes back empty, paste the numeric `view_all_page_id=` value from their own Ads Library URL instead — that always works.

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

Full public Ads Library search URLs, pasted straight from your browser. The filters already in the URL are used as-is and take priority over the settings below — any disagreement is reported in the log rather than silently resolved.

## `countries` (type: `array`):

Two-letter ISO country codes (US, GB, AU, DE …). The Ads Library shows the ads running in a given country, so each country is a separate search. Defaults to US.

## `adStatus` (type: `string`):

Whether to return ads currently running, ads that have stopped, or both.

## `dateFrom` (type: `string`):

Only ads whose delivery started on or after this date (YYYY-MM-DD). Also the simplest way to run incrementally — set it to your last run's date.

## `dateTo` (type: `string`):

Only ads whose delivery started on or before this date (YYYY-MM-DD).

## `mediaTypes` (type: `array`):

Limit to ads using particular creative types. Leave empty for all. Selecting exactly one is applied by Facebook; selecting several is applied after fetching, because the Ads Library accepts only one media type per search.

## `maxResults` (type: `integer`):

Total ads to return across every search in this run. Reached fairly: searches are interleaved, so one broad keyword cannot consume the whole limit before your other inputs have run.

## `onlyTotalCount` (type: `boolean`):

Preflight mode: report how many ads each search matches and return no ad rows. FREE — no ad-result events are charged. Facebook publishes this figure as a live estimate and caps it at 50,000, so a result of 50,001 means '50,000 or more' and is flagged as such in the output.

## `includeAdDetails` (type: `boolean`):

Include the full creative in each row: link description, CTA text, every image and video URL, and all carousel cards. FREE — this data arrives in the same response as the base ad, so switching it off makes rows narrower, not cheaper. On by default.

## `includeAdvertiserDetails` (type: `boolean`):

Fetch each advertiser's own Facebook Page for public contact and business data: email, phone, WhatsApp, website, street address and coordinates, price range, services and recommend/review figures. CHARGED once per unique advertiser, never per ad — an advertiser with 200 ads in your results is one charge. A Page that cannot be read, or that publishes none of these, is not charged.

## `includeLandingPageEnrichment` (type: `boolean`):

Follow each ad's destination link and extract the page title, description, emails, phones and social links. CHARGED once per unique domain, never per ad — fifty ads pointing at one shop is one charge. A site that is unreachable, blocked, or publishes nothing is not charged.

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

Meta's regulated ad categories. Political, housing, employment and credit ads publish extra transparency data (spend and impression ranges, funding entity) that ordinary commercial ads do not.

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

Limit to ads shown on particular Meta platforms. Leave empty for all.

## `exactPhrase` (type: `boolean`):

Search terms must appear as a whole phrase rather than as loose words. Narrower and usually far more precise for brand or product names.

## `skipAdIds` (type: `array`):

Ad archive IDs you already have. They are skipped before anything is fetched, enriched or charged — the simplest way to run this on a schedule and pay only for what is new.

## `maxResultsPerSource` (type: `integer`):

Safety cap on how many ads any single keyword, advertiser or URL may contribute. 0 means no per-search cap — only the overall maximum applies.

## `advertiserNameInclude` (type: `array`):

Keep only ads whose advertiser name contains one of these (case-insensitive). Applied after fetching — the Ads Library has no such filter.

## `advertiserNameExclude` (type: `array`):

Drop ads whose advertiser name contains one of these (case-insensitive). Ads with no advertiser name are kept.

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

Two-letter language codes to limit results to. Leave empty for all languages.

## `proxyConfiguration` (type: `object`):

Apify RESIDENTIAL proxy is recommended and is what this Actor is built for. Measured on the platform: Facebook rate-limits the Apify Datacenter pool on the pagination request, so a datacenter run returns only the first ~30 ads per search while reporting success. Datacenter and no-proxy still fetch page one. Custom proxy URLs from your own provider are honoured in full.

## Actor input object example

```json
{
  "searchTerms": [
    "running shoes"
  ],
  "pageUrlsOrIds": [
    "https://www.facebook.com/nike"
  ],
  "startUrls": [],
  "countries": [
    "US"
  ],
  "adStatus": "all",
  "dateFrom": "",
  "dateTo": "",
  "mediaTypes": [],
  "maxResults": 100,
  "onlyTotalCount": false,
  "includeAdDetails": true,
  "includeAdvertiserDetails": false,
  "includeLandingPageEnrichment": false,
  "adType": "all",
  "publisherPlatforms": [],
  "exactPhrase": false,
  "skipAdIds": [],
  "maxResultsPerSource": 0,
  "advertiserNameInclude": [],
  "advertiserNameExclude": [],
  "contentLanguages": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

One row per unique ad.

## `advertisers` (type: `string`):

Advertiser columns, including enriched contact data when that option is on.

## `creatives` (type: `string`):

Ad copy, media URLs and carousel cards.

## `transparency` (type: `string`):

Spend, impression and reach ranges for regulated ad categories.

## `landingPages` (type: `string`):

Ad destinations, when landing-page enrichment is on.

## `runSummary` (type: `string`):

Counts, stop reasons, bandwidth and charging totals for this run.

# 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 = {
    "searchTerms": [
        "running shoes"
    ],
    "pageUrlsOrIds": [
        "https://www.facebook.com/nike"
    ],
    "startUrls": [],
    "countries": [
        "US"
    ],
    "mediaTypes": [],
    "maxResults": 100,
    "publisherPlatforms": [],
    "skipAdIds": [],
    "maxResultsPerSource": 0,
    "advertiserNameInclude": [],
    "advertiserNameExclude": [],
    "contentLanguages": [],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("coregent/facebook-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 = {
    "searchTerms": ["running shoes"],
    "pageUrlsOrIds": ["https://www.facebook.com/nike"],
    "startUrls": [],
    "countries": ["US"],
    "mediaTypes": [],
    "maxResults": 100,
    "publisherPlatforms": [],
    "skipAdIds": [],
    "maxResultsPerSource": 0,
    "advertiserNameInclude": [],
    "advertiserNameExclude": [],
    "contentLanguages": [],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("coregent/facebook-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 '{
  "searchTerms": [
    "running shoes"
  ],
  "pageUrlsOrIds": [
    "https://www.facebook.com/nike"
  ],
  "startUrls": [],
  "countries": [
    "US"
  ],
  "mediaTypes": [],
  "maxResults": 100,
  "publisherPlatforms": [],
  "skipAdIds": [],
  "maxResultsPerSource": 0,
  "advertiserNameInclude": [],
  "advertiserNameExclude": [],
  "contentLanguages": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call coregent/facebook-ads-library-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,coregent/facebook-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/YVbP3Hs5Ty7VazbNb/builds/PllAH1iqYmNSAeXy4/openapi.json
