# Facebook Ads Library Scraper: every ad version, no login (`alexmorain/facebook-ads-library-scraper`) Actor

- **URL**: https://apify.com/alexmorain/facebook-ads-library-scraper.md
- **Developed by:** [Alex Morain](https://apify.com/alexmorain) (community)
- **Categories:** Marketing, Social media, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.25 / 1,000 ads

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

### What does Facebook Ads Library Scraper do?

It scrapes the [Meta Ad Library](https://www.facebook.com/ads/library/) and returns every Facebook, Instagram, Messenger and Threads ad of a brand: text, headline, images, videos, carousel cards, landing page, call to action, start and end dates, platforms. You give it a brand name, a Facebook page URL, a keyword or a pasted Ad Library URL. No login, no cookies, no Meta developer token: it works as a **Facebook Ad Library API alternative** that covers commercial ads in every country, which the official API does not (it only returns political ads, plus ads delivered in the EU and UK).

The difference with other Ad Library scrapers is the version groups. The Ad Library list groups near-identical ads and shows one ad per group, with a "This ad has multiple versions" link. Other scrapers return that one ad and a `collation_count`. This Actor opens each group and returns every version as its own row, with its own ID, dates and text.

Measured on 18 October 2026, page Decathlon France, all ads in France, first 1,000 ads:

| | Ads |
|---|---|
| Listed by the Ad Library (one per group) | 655 |
| Hidden in 149 version groups, unfolded by this Actor | 345 |
| Total returned | 1,000 |

On that page, a scraper that reads the list only misses one ad in three. It matters most for competitor monitoring: a new version added to an existing group never shows up in the list, so a "new ads only" check built on the list does not see it.

### Why scrape the Meta Ad Library?

- Competitor monitoring: what a brand runs right now, since when, on which platforms, with which offers and landing pages.
- Creative research: build a swipe file of ad texts, images and videos for a market.
- Agency reporting and pitches: export a prospect's ads in one click before a meeting.
- Alerts: schedule the Actor daily in New ads only mode and get a webhook when a competitor launches something.

What you get with this Actor:

- Brand names resolved to the right Facebook page, the way the Ad Library search box does it for your country. Searching a brand as a keyword returns resellers and fan pages too (20% of the results for "decathlon" in France), searching by page does not.
- Every version of every ad, hidden versions marked `fromVersionGroup: true`.
- All the Ad Library filters: country, active or inactive, ad category, media type, platform, language, date range.
- New ads only mode with a persistent registry, and an optional webhook at the end of the run.
- Plain HTTP requests, no browser, so runs are fast and cheap. Apify scheduling, API, integrations and monitoring come with it.

### What data can it extract?

| Field | Type | Description |
|---|---|---|
| `adArchiveId` | string | Ad Library ID of the ad |
| `adLibraryUrl` | string | Public page of the ad |
| `pageName`, `pageId`, `pageUrl` | string | Advertiser page |
| `pageLikeCount` | integer | Likes of the advertiser page |
| `isActive` | boolean | Running now or stopped |
| `startDate`, `endDate`, `daysRunning` | string, integer | When it ran, in days |
| `publisherPlatforms` | array | FACEBOOK, INSTAGRAM, MESSENGER, THREADS, AUDIENCE_NETWORK |
| `displayFormat` | string | IMAGE, VIDEO, CAROUSEL, DCO (dynamic creative), DPA (catalog) |
| `body`, `title`, `linkDescription` | string | Ad text, headline, link description |
| `linkUrl`, `caption` | string | Landing page and displayed domain |
| `ctaText`, `ctaType` | string | Call to action |
| `imageUrls`, `videos`, `cards` | array | Creatives, carousel and dynamic cards |
| `collationId`, `collationCount` | string, integer | Version group and its size |
| `fromVersionGroup` | boolean | true when the ad was hidden in a version group |
| `spend`, `impressions`, `currency` | object, string | Political and issue ads only |
| `input`, `inputType` | string | Which line of your input found the ad |

### How to scrape Facebook ads

1. Open the Actor and type a brand in Advertisers, for example `Decathlon`, or paste its Facebook page URL.
2. Pick a country if you want the ads shown there only. Leave it empty for all countries.
3. Choose active ads, inactive ads or both.
4. Set Max ads per search, or 0 for everything.
5. Press Start, then download the results as JSON, CSV, Excel or HTML, or read them through the API.

To follow a market instead of a brand, use Search terms (`running shoes`). To reproduce a search you built on the Ad Library website, paste its URL in Ad Library URLs: the filters in the URL are kept.

### How much does it cost to scrape the Facebook Ad Library?

You pay per ad returned, plus a small start fee. On the Free plan, 1,000 ads cost $0.50, and the price per ad goes down on paid Apify plans, to $0.25 per 1,000 ads from Gold. Platform usage (compute, proxies) is included in that price. Hidden versions unfolded from a group are ads and are charged as ads: turn Every version of each ad off to get only what the Ad Library list shows.

A brand with 1,000 ads takes about six minutes. New ads only runs read the whole search again but charge only the ads never returned before, so a quiet day costs the start fee.

### Input

See the Input tab for every option. The ones that need a word:

- Advertisers accepts a name, a page URL (`https://www.facebook.com/brand/`) or a page ID. For a name, the log prints the page chosen and the other matches with their IDs. Big brands run ads from several pages (per country, per product line): raise Pages per advertiser name, or paste the page IDs you want.
- Shown from and Shown until follow Meta's rule: an ad is kept if it was shown at some point in the window, even if it started before. To filter by launch date, use `startDate` in the results.
- Countries: several countries are searched together and each ad comes out once.

Example input:

```json
{
    "advertisers": ["Decathlon", "https://www.facebook.com/nike/"],
    "countries": ["FR"],
    "activeStatus": "active",
    "maxAdsPerSearch": 500
}
```

### Output

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel. Two items, shortened:

```json
[
    {
        "adArchiveId": "1600894127669355",
        "adLibraryUrl": "https://www.facebook.com/ads/library/?id=1600894127669355",
        "pageName": "Example Sports",
        "pageId": "122695097770591",
        "isActive": true,
        "startDate": "2026-09-14",
        "endDate": "2026-10-16",
        "daysRunning": 33,
        "publisherPlatforms": ["FACEBOOK", "INSTAGRAM"],
        "displayFormat": "CAROUSEL",
        "body": "Ready for the next run? New trail shoes are in.",
        "linkUrl": "https://www.example.com/trail-shoes",
        "ctaText": "Shop now",
        "collationId": "1403288121396887",
        "collationCount": 3,
        "fromVersionGroup": false,
        "input": "Example Sports",
        "inputType": "advertiser"
    },
    {
        "adArchiveId": "2379460792481796",
        "adLibraryUrl": "https://www.facebook.com/ads/library/?id=2379460792481796",
        "pageName": "Example Sports",
        "isActive": true,
        "startDate": "2026-09-20",
        "displayFormat": "DCO",
        "body": "Grip on every terrain",
        "collationId": "1403288121396887",
        "collationCount": 3,
        "fromVersionGroup": true,
        "input": "Example Sports",
        "inputType": "advertiser"
    }
]
```

### Tips

- Leave the proxy on Residential. Meta refuses datacenter IPs and part of the residential ones; the Actor tries fresh IPs until one is accepted, and the price per ad does not change.
- For a daily alert, schedule a task with Mode New ads only and a Webhook URL (Slack and Discord incoming webhooks work as is).
- Image and video URLs come from Meta's CDN and are signed: they expire after a few days. Download the files you want to keep.
- Catalog and dynamic ads (`DPA`, `DCO`) carry template text like `{{product.name}}`. The Actor puts the text of the first card in `body` when the ad text is only a template; the full cards are in `cards`.

### FAQ, disclaimers and support

#### Does it need a Facebook account or a Meta API token?

No. It reads the public Ad Library website, like a visitor who is not logged in.

#### How far back does the Ad Library go?

Inactive ads stay in the library for one year, political and issue ads for seven years. Spend and impression ranges exist for political and issue ads only; Meta does not publish them for other ads.

#### Why did a name return the wrong page?

A name is matched the way the Ad Library search box does it for the country you chose. Check the log: it lists the other pages with their IDs. Paste the right page URL or ID in Advertisers.

#### Is it legal to scrape the Facebook Ad Library?

Our Actors are ethical and do not extract any private user data, such as email addresses, gender, or location. They only extract what the user has chosen to share publicly. We therefore believe that our Actors, when used for ethical purposes by Apify users, are safe. However, you should be aware that your results could contain personal data. Personal data is protected by the GDPR in the European Union and by other regulations around the world. You should not scrape personal data unless you have a legitimate reason to do so. If you're unsure whether your reason is legitimate, consult your lawyers.

Something wrong or missing? Open an issue in the Issues tab and it gets fixed. To call the Actor from your code, see the API tab. More ad libraries from the same author: [LinkedIn Ad Library Scraper](https://apify.com/alexmorain/linkedin-ad-library-scraper) and [Google Ads Transparency Scraper](https://apify.com/alexmorain/google-ads-transparency-scraper).

# Actor input Schema

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

Brands whose ads you want, one per line: a name (<code>Decathlon</code>), a Facebook page URL (<code>https://www.facebook.com/Decathlon.France/</code>) or a page ID (<code>122695097770591</code>). A name is looked up the way the Ad Library search box does it, for the country you pick: the log lists the page it chose and the other matches with their IDs. Only that page's ads come out, not every ad that mentions the brand.

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

Keyword searches, as in the Ad Library search box: every ad whose text matches, from any advertiser. Use it for a market or a product (<code>running shoes</code>), not to follow one brand: a brand name as a keyword also returns resellers and fan pages (20% of the results for <code>decathlon</code> in France, measured).

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

Search terms match the exact phrase instead of all the words in any order.

## `adLibraryUrls` (type: `array`):

Pages copied from facebook.com/ads/library: a search, an advertiser's ads (<code>?view_all_page_id=...</code>) or a single ad (<code>?id=...</code>). The filters in the URL (country, status, media type, platforms, languages, dates) are kept for that URL. One URL per line.

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

Only ads shown in these countries: two-letter codes like <code>US</code>, <code>GB</code>, <code>FR</code>, or names in English. Leave empty for all countries. Several countries are searched together and each ad comes out once.

## `activeStatus` (type: `string`):

Active ads are the ones running now. Inactive ads have stopped. The Ad Library keeps inactive ads for 1 year (7 years for political ads).

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

<b>All ads</b>, or only ads about social issues, elections or politics, which come with spend and impression ranges.

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

Only ads with this kind of creative.

## `platforms` (type: `array`):

Only ads shown on these Meta platforms. Leave empty for all.

## `languages` (type: `array`):

Only ads in these languages: two-letter codes like <code>en</code>, <code>fr</code>, <code>de</code>. Leave empty for all.

## `shownFrom` (type: `string`):

Only ads <b>shown</b> on or after this date. Careful: Meta keeps an ad that started earlier and was still running on this date. It is not a launch date filter: read <code>startDate</code> in the results for that.

## `shownUntil` (type: `string`):

Only ads shown on or before this date.

## `includeAllVersions` (type: `boolean`):

The Ad Library groups ads with the same creative and lists one per group ("This ad has multiple versions"). On, the Actor opens each group and returns the hidden versions too, each with its own ID, dates and text, marked <code>fromVersionGroup: true</code>. One extra request per group, and each version is priced as an ad. Off, you get exactly what the Ad Library list shows.

## `pagesPerAdvertiser` (type: `integer`):

How many Facebook pages to take for each advertiser <b>name</b>. Big brands run ads from several pages (one per country, per product line). 1 takes the page the Ad Library search box puts first for your country.

## `maxAdsPerSearch` (type: `integer`):

Stop each advertiser page, search term or URL after this many ads, versions included. Set <code>0</code> for no cap.

## `mode` (type: `string`):

<b>All ads</b> returns every ad the search finds, on every run.<br><br><b>New ads only</b> keeps a persistent registry of the ads previous runs returned and charged, and returns only ads never returned before: schedule it daily to be told when a competitor launches something new, including a new version added to an existing group. Every run reads the whole search, since the Ad Library does not sort by launch date; you pay only for the new ads.

## `registryKey` (type: `string`):

By default the registry is tied to the exact targets, countries and filters, so changing any of them starts a fresh one. Set your own key to keep one registry while you edit the list.

## `webhookUrl` (type: `string`):

If set, the Actor POSTs a small JSON summary to this URL at the end of the run, <b>only when at least one ad was returned</b>: <code>actor</code>, <code>runId</code>, <code>mode</code>, <code>items</code>, <code>searches</code>. With New ads only and a daily schedule, that is a push notification when a competitor launches ads.<br><br>Use the final URL: a redirect is not followed, except a 307 or 308, which gets the same POST once. An unreachable webhook is logged as a warning and never fails the run. Stored encrypted, since a webhook URL often carries its own access token.

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

Leave as is. Meta refuses datacenter IPs and a good share of residential ones, so the Actor tries fresh residential IPs until one is accepted, then keeps it. The per-ad price is the same whatever you choose.<br><br>With the plain Apify proxy (<code>{"useApifyProxy": true}</code>, typical in API calls) it starts on datacenter IPs and <b>switches to residential by itself</b> when Meta keeps refusing. If you pick your own proxy URLs, the Actor keeps exactly what you chose.

## Actor input object example

```json
{
  "advertisers": [
    "Decathlon"
  ],
  "exactPhrase": false,
  "activeStatus": "active",
  "adType": "all",
  "mediaType": "all",
  "includeAllVersions": true,
  "pagesPerAdvertiser": 1,
  "maxAdsPerSearch": 20,
  "mode": "all",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All ads in the default dataset.

# 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 = {
    "advertisers": [
        "Decathlon"
    ],
    "maxAdsPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("alexmorain/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 = {
    "advertisers": ["Decathlon"],
    "maxAdsPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("alexmorain/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 '{
  "advertisers": [
    "Decathlon"
  ],
  "maxAdsPerSearch": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call alexmorain/facebook-ads-library-scraper --silent --output-dataset

```

## MCP server setup

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