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

Scrape the Meta Ad Library without login: ads from any Facebook page, keyword or Ad Library URL, in every country — creatives, copy, CTA, links, platforms, dates, spend and reach, advertiser details and the products behind each ad. Exact results limit. Pay per ad.

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

## Pricing

from $2.72 / 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

## Facebook Ads Library Scraper – Meta, Instagram Ads & Spend

**Get every ad of any brand, page or keyword from the Meta Ad Library in a clean spreadsheet, in seconds, without a Facebook account.**

Scrape the **Meta Ad Library** (Facebook Ads Library): ad copy, creatives (images, videos, carousels), CTA, destination links, platforms (Facebook, Instagram, Messenger, Threads, WhatsApp, Audience Network), start and end dates, spend and reach when Meta publishes them, advertiser details, and the **products and prices behind each ad**. **Your results limit is exact**: you never get, or pay for, more ads than you asked for.

### How to use it in 3 steps

1. **Paste what you want to watch**: a Facebook page URL (`https://www.facebook.com/nike`), any Meta Ad Library URL (set your filters on [facebook.com/ads/library](https://www.facebook.com/ads/library/) and copy the address bar), or type keywords such as `running shoes` in **Search keywords / page names**.
2. **Set the results limit** per URL or keyword (for example 50).
3. Click **Start**. Ads appear in the **Ads** table during the run; export them to CSV, Excel or JSON.

The prefilled example (20 Sephora ads) finishes in about 10 seconds and costs under $0.10.

### What can it do?

- 🧩 Ads published across Meta platforms: Facebook, Instagram, Messenger, Threads, WhatsApp and Audience Network
- 🔎 Any Ad Library search: brand, keyword, country, language, media type, active or inactive, ad category. Paste the URL and every filter in it is kept
- 🔤 Keyword search without building a URL: type `black friday` and choose a country (or ALL)
- 🎯 A single ad: paste `https://www.facebook.com/ads/library/?id=…`
- 🌍 Every country at once (`ALL`) or one country
- 📅 Date range, sorting (impressions or most recent) and active status
- 🏢 Advertiser details: Instagram username and followers, page likes, category, verification, page spend, related pages in other countries
- 💸 Spend, reach, impressions and EU audience (age, gender, country, payer and beneficiary) when Meta publishes them
- 🛒 E-commerce enrichment: the product, price, brand and rating behind each ad's link
- 🔢 Only the total number of ads of a page, for quick market sizing
- 📤 Export to JSON, CSV, Excel, XML or HTML, or use the API, webhooks, Make, Zapier, n8n or the Apify MCP server

Use it for competitor ad research, creative inspiration, market and pricing intelligence, and transparency or regulatory reporting.

#### What you can paste

| You paste | What you get |
|---|---|
| `https://www.facebook.com/SHEINOFFICIAL` (also `m.`, `web.`, `fb.com`, `/pg/…`) | All ads of that page |
| `https://www.facebook.com/profile.php?id=100064…` or `/people/Name/100064…/` | All ads of that page |
| `https://www.facebook.com/ads/library/?…&view_all_page_id=…` | That advertiser, with the URL's filters |
| `https://www.facebook.com/ads/library/?…&q=shoes&country=FR` | That search, with the URL's filters |
| `https://www.facebook.com/ads/library/?id=1106694961565426` | That one ad |
| Keyword: `running shoes` (in **Search keywords / page names**) | Ads whose text matches, in the chosen country |
| Page name: `@nike` | All ads of that page |

Several URLs pasted on one line (separated by commas or new lines), duplicates and extra spaces are cleaned up automatically. Anything that is not Facebook (an Instagram link, a Facebook group) is skipped and listed in **FAILED_INPUTS**, never billed. With an empty input, the Actor runs the Sephora example and says so in the log.

#### Input example

```json
{
  "startUrls": [
    { "url": "https://www.facebook.com/SHEINOFFICIAL" },
    { "url": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&media_type=image&search_type=page&view_all_page_id=15087023444" }
  ],
  "searchQueries": ["running shoes", "@nike"],
  "resultsLimit": 50,
  "country": "US",
  "activeStatus": "active",
  "sorting": "total_impressions",
  "onlyAdsNewerThan": "3 months",
  "isDetailsPerAd": false,
  "enrichWithEcommerceData": false
}
```

The input uses the same field names as the Store's reference Facebook Ads scraper (`startUrls`, `resultsLimit`, `onlyTotal`, `isDetailsPerAd`, `includeAboutPage`, `activeStatus`, …), so an existing input works as it is.

#### Output example

One row per ad (shortened; real row from a run):

```json
{
  "inputUrl": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&media_type=all&search_type=page&view_all_page_id=16453004404",
  "facebookUrl": "https://www.facebook.com/sephora/",
  "pageID": "16453004404",
  "adArchiveID": "1570487428042958",
  "pageName": "Sephora",
  "isActive": true,
  "startDateFormatted": "2026-08-01T07:00:00.000Z",
  "endDateFormatted": "2026-10-09T07:00:00.000Z",
  "startDate": 1785567600,
  "endDate": 1791529200,
  "publisherPlatform": ["FACEBOOK", "INSTAGRAM", "MESSENGER", "THREADS"],
  "categories": ["UNKNOWN"],
  "collationCount": null,
  "spend": null,
  "reachEstimate": null,
  "impressionsWithIndex": { "impressionsText": null, "impressionsIndex": -1 },
  "snapshot": {
    "pageName": "Sephora",
    "pageProfileUri": "https://www.facebook.com/sephora/",
    "pageLikeCount": 20340746,
    "pageCategories": ["Health/beauty"],
    "title": "{{product.name}}",
    "body": { "text": "Shop these picks, thank yourself later 💖" },
    "caption": "sephora.com",
    "ctaText": "Shop now",
    "ctaType": "SHOP_NOW",
    "linkUrl": "http://www.sephora.com/",
    "displayFormat": "DPA",
    "cards": [{ "body": "Shop these picks, thank yourself later 💖", "ctaType": "SHOP_NOW", "originalImageUrl": "https://scontent.xx.fbcdn.net/…" }],
    "images": [],
    "videos": []
  }
}
```

With **extra ad details**, each row gets `ad_details` (advertiser page and Instagram data, page spend, EU audience and reach). With **About**, it gets `pageInfo`. With **e-commerce**, it gets `ecommerceData`:

```json
{ "ecommerceData": [{ "name": "Soft Pinch Liquid Blush", "offers": { "price": 25, "priceCurrency": "USD" },
  "brand": { "slogan": "Rare Beauty" }, "rating": 4.6, "reviewCount": 1820, "url": "https://www.sephora.com/product/…" }] }
```

With **Get only the total ad count**, you get one row per URL: `{ "inputUrl": "…", "pageID": "16453004404", "totalCount": 1680, "url": "…" }`.

Every run also writes two records in the key-value store:

- **RUN_SUMMARY**: `{ "status": "SUCCEEDED", "adsSaved": 13, "productsBilled": 0, "urls": 7, "failedInputs": 2, "stoppedByBudget": false, … }`
- **FAILED_INPUTS**: what gave nothing, with the reason (never billed), e.g. `{ "input": "https://www.instagram.com/nike/", "type": "invalidInput", "reason": "Skipped: not a Facebook page URL or Meta Ad Library URL. …" }`

### Pricing

Pay per event. Platform usage is included, and there is no start fee.

| Event | Free | Starter | Scale | Business |
|---|---|---|---|---|
| **Ad** (or one total with `onlyTotal`) | $4.64 / 1,000 | $4.00 / 1,000 | $3.36 / 1,000 | $2.72 / 1,000 |
| Add-on: E-commerce enrichment (per product found) | $4.56 / 1,000 | $2.56 / 1,000 | $2.24 / 1,000 | $1.92 / 1,000 |

Example: 1,000 ads on the Free plan = $4.64. **The results limit is exact**, and you can also cap any run with a maximum cost per run: the Actor stops cleanly before going over it. Failed inputs are never billed.

### What it can and cannot do

| ✅ Can | ❌ Cannot |
|---|---|
| Every ad Meta shows logged-out visitors in the Ad Library, in every country | Ads Meta does not show in the Ad Library |
| Spend, reach and impressions **when Meta publishes them** (political, social-issue and EU-targeted ads) | Spend or reach of ordinary commercial ads outside the EU: Meta publishes none |
| Page name, URL, photo, description, related pages (About) | Confirmed owner, address and page history (shown only to signed-in users) |
| Products behind real product pages | Catalog templates (`{{product.name}}`), sign-up pages and app links (skipped, not billed) |
| Keyword search exactly like the Ad Library search box | Better relevance than Meta's own search (keywords match words anywhere in the ad) |

### FAQ

**Do I need a Facebook account, cookies or the Meta API?** No.

**Can I scrape all countries?** Yes: `country` is `ALL` by default, or paste an Ad Library URL with any country.

**How many ads can I get?** Everything the Ad Library lists for your search. Leave the limit empty for all ads; a big brand can have thousands.

**Why is `totalCount` 50001?** The Ad Library stops counting at 50,000: 50001 means "more than 50,000 ads".

**How fast is it?** About 30 ads every few seconds. Extra ad details add one request per ad.

**Why do some keyword results look unrelated?** Meta's keyword search matches the words anywhere in the ad (text, page, links). For exact results, open the Ad Library, search with quotes ("running shoes"), and paste that URL.

**Is it legal?** The Actor only collects data Meta makes public in the Ad Library. Some of it may be personal data under the GDPR: use it for a legitimate purpose only.

### Troubleshooting

- **A URL is in FAILED_INPUTS with "not found"**: the page has no ads in the Ad Library, or its alias is different. Open the page in the [Ad Library](https://www.facebook.com/ads/library/), select it, and paste that URL (it contains `view_all_page_id`).
- **"No ads match these settings"**: your filters (active status, dates, country) exclude every ad. Try `activeStatus` = Active and inactive, or country ALL.
- **"Partial: Facebook rate-limited"**: Facebook slowed the run down after many pages. You keep (and pay for) only what was saved; run again later for the rest.
- **The run stopped early**: check RUN_SUMMARY: `stoppedByBudget: true` means your maximum cost per run was reached.
- **Something else?** Open an issue on the **Issues** tab with your run ID.

### Use it via API

**Python** (`pip install apify-client`)

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("badr_xba/facebook-ads-library-scraper").call(run_input={
    "startUrls": [{"url": "https://www.facebook.com/nike"}],
    "searchQueries": ["running shoes"],
    "resultsLimit": 50,
    "country": "US",
})
for ad in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(ad["pageName"], ad["adArchiveID"], (ad.get("snapshot") or {}).get("linkUrl"))
```

**JavaScript** (`npm install apify-client`)

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('badr_xba/facebook-ads-library-scraper').call({
    startUrls: [{ url: 'https://www.facebook.com/nike' }],
    searchQueries: ['running shoes'],
    resultsLimit: 50,
    country: 'US',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'ads');
```

**cURL** (runs the Actor and returns the ads in one call)

```bash
curl -X POST "https://api.apify.com/v2/acts/badr_xba~facebook-ads-library-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"startUrls":[{"url":"https://www.facebook.com/nike"}],"resultsLimit":20}'
```

### Integrations

Connect the results to **Make**, **Zapier**, **n8n**, Google Sheets, Slack or any webhook from the **Integrations** tab, schedule daily runs to track competitors' new ads, or let an AI agent call it through the **Apify MCP server** (`mcp.apify.com`).

# Actor input Schema

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

One per line. Accepted: a Facebook Page URL (https://www.facebook.com/SHEINOFFICIAL, m.facebook.com/nike, facebook.com/profile.php?id=100064…), an advertiser or search URL from the Meta Ad Library (set your keyword, country, platforms, language and media filters on facebook.com/ads/library, then copy the URL), or a single ad URL (https://www.facebook.com/ads/library/?id=…). Duplicates and extra spaces are removed automatically; a URL that is not Facebook is skipped and listed in FAILED_INPUTS (not billed).

## `searchQueries` (type: `array`):

Optional. Keywords searched in the text of the ads, in the country below (ALL = every country), e.g. running shoes or black friday. Start with @ to get a page's ads instead: @nike. Each keyword uses the same results limit.

## `resultsLimit` (type: `integer`):

Ads per URL or keyword. The limit is exact: you never get (or pay for) more. Leave empty to get every ad the library shows (a big brand can have thousands).

## `country` (type: `string`):

Where the ads ran, for page URLs, page names and keywords. ALL = every country. Ad Library URLs keep their own country.

## `onlyTotal` (type: `boolean`):

One row per URL with the total number of ads, instead of the ads themselves.

## `includeAboutPage` (type: `boolean`):

Adds pageInfo: page name, URL, profile photo, description and the related pages of the advertiser in other countries.

## `isDetailsPerAd` (type: `boolean`):

Opens each ad's detail and adds ad_details: Instagram username and followers, page likes, category and verification, page spend and political status, and for EU ads the audience, reach by country, age and gender, payer and beneficiary. Slower (one request per ad).

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

Ads still running, stopped ones, or both. A value already in the URL wins.

## `sorting` (type: `string`):

Order of the ads. A value already in the URL wins.

## `onlyAdsNewerThan` (type: `string`):

Only ads active on or after this date: 2024-01-01 or "2 months".

## `onlyAdsOlderThan` (type: `string`):

Only ads active on or before this date: 2024-12-31 or "1 year".

## `enrichWithEcommerceData` (type: `boolean`):

Follows each ad's links and adds the product behind them (name, price, currency, brand, image, description, rating) as ecommerceData. Billed per product found; links that are not product pages (sign-up, app, catalog templates) are free.

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

Leave empty: the Actor reads the Ad Library directly and switches to Apify addresses when Facebook refuses.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&media_type=all&search_type=page&view_all_page_id=16453004404"
    }
  ],
  "searchQueries": [
    "running shoes",
    "@nike"
  ],
  "resultsLimit": 20,
  "country": "ALL",
  "onlyTotal": false,
  "includeAboutPage": false,
  "isDetailsPerAd": false,
  "activeStatus": "",
  "sorting": "",
  "enrichWithEcommerceData": false
}
```

# Actor output Schema

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

Advertiser, dates, platforms, title, text, CTA, link.

## `products` (type: `string`):

Products behind the ads (add-on).

## `totals` (type: `string`):

One row per URL when "Get only the total ad count" is on.

## `allFields` (type: `string`):

Every field as JSON, CSV or Excel.

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

Ads saved, products billed, failures.

## `failedInputs` (type: `string`):

URLs that gave nothing.

# 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": [
        {
            "url": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&media_type=all&search_type=page&view_all_page_id=16453004404"
        }
    ],
    "resultsLimit": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("badr_xba/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 = {
    "startUrls": [{ "url": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&media_type=all&search_type=page&view_all_page_id=16453004404" }],
    "resultsLimit": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("badr_xba/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 '{
  "startUrls": [
    {
      "url": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&media_type=all&search_type=page&view_all_page_id=16453004404"
    }
  ],
  "resultsLimit": 20
}' |
apify call badr_xba/facebook-ads-library-scraper --silent --output-dataset

```

## MCP server setup

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