# Meta Ad Library Scraper — Ad Search & Export (`devilscrapes/meta-ad-library-scraper`) Actor

Search Meta's public Ad Library by brand/keyword or advertiser Page ID and export live ad creatives — CTA text, ad copy, images, platforms, active status. No Meta login, access token, or App Review required. A Meta/Facebook Ads Library API alternative and data exporter.

- **URL**: https://apify.com/devilscrapes/meta-ad-library-scraper.md
- **Developed by:** [DevilScrapes](https://apify.com/devilscrapes) (community)
- **Categories:** SEO tools, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

<div align="center">
  <img src=".actor/icon.svg" width="160" alt="Devil Scrapes mark" />

## Meta Ad Library Scraper

**💰 $3.20 / 1 000 ads**  ·  pay only for results  ·  no credit card to try

*The devil's in the data — we go get it.* 😈

Search Meta's public **Ad Library** (Facebook + Instagram) by brand, keyword, or advertiser Page ID and get every matching ad back as clean, structured rows — CTA text, ad copy, images, platforms, and active status. No Meta login, access token, or App Review needed.

</div>

***

> **Quick answer:** The **Meta Ad Library Scraper** searches Meta's public Ad Library by keyword or advertiser Page ID and returns every matching ad as structured data — CTA button text and type, ad body copy, link description, creative image URLs, publisher platforms, and active/inactive status. No Meta developer account, access token, or App Review is required. Pricing is pay-per-result — **$3.20 per 1 000 ads landed**, no subscription, no card required to try it.

### 🎯 What this scrapes

The [Meta Ad Library](https://www.facebook.com/ads/library/) is Meta's public registry of every ad running across Facebook, Instagram, Messenger, and the Audience Network. This Actor searches it directly — by free-text query (brand or keyword) or by advertiser Page ID — and walks Meta's own pagination to pull every matching ad, not just the first page.

Per ad you get:

| Field | Type | Notes |
|---|---|---|
| `ad_archive_id` | string | Meta's stable id for this ad |
| `page_id` / `page_name` | string | The advertiser's Page |
| `is_active` | boolean | Currently running or not |
| `collation_count` / `collation_id` | int / string | Creative-variation grouping, if any |
| `publisher_platforms` | array | `FACEBOOK`, `INSTAGRAM`, `MESSENGER`, ... |
| `link_url` | string | Advertiser's landing page |
| `cta_text` / `cta_type` | string | Button copy, e.g. "Shop now" / `SHOP_NOW` |
| `body_text` | string | Primary ad copy |
| `link_description` | string | Secondary link text |
| `image_urls` | array | Creative image URLs |
| `display_format` | string | `IMAGE`, `VIDEO`, `CAROUSEL`, ... |
| `start_date_unix` / `end_date_unix` | int | Ad delivery window, unix seconds |
| `ad_library_url` | string | Deep link back to the live Ad Library entry |
| `scraped_at` | string | ISO-8601 UTC timestamp |

### 🔥 What we handle for you

- 🛡️ **We solve Meta's session challenge automatically** — the public search page replies with a short verification step before serving results; we handle it on every run so you never see it.
- 🔁 **We retry with exponential backoff** on rate limits and server errors, honoring `Retry-After` — up to 5 attempts per request.
- 🌐 **We route through Apify Proxy**, pinned to your requested country, so results reflect the market you asked for.
- 🧱 **We paginate for you.** Most Ad Library scrapers stop at the first ~30 results; we keep walking Meta's own pagination until it runs out or you hit your cap.
- 🧊 **Clean, typed dataset rows** — Pydantic-validated, deduplicated across pages and targets.
- 💰 **Pay-per-result pricing.** A small per-run fee, then you're charged only for ads that actually land. No data, no charge.

### 💡 Use cases

- **Competitor ad monitoring** — track what a competitor brand is running on Facebook/Instagram right now, and how their creative changes week to week.
- **Ad copy research** — pull hundreds of live CTA/body-copy combinations for a niche to inform your own creative testing.
- **Brand protection** — search your own brand or trademark to spot unauthorized advertisers riding your name.
- **Market research** — gauge how saturated a vertical is by counting active ads for a set of keywords.
- **Agency reporting** — screenshot-free proof of what's currently live for a client, exported straight to a spreadsheet.
- **AI / RAG ingestion** — feed ad copy and creative image URLs into a vector store for creative-trend analysis.

### ⚙️ How to use it

1. **Click "Try for free"** at the top of the page.
2. **Enter one or more search queries** (brand names or keywords) into the `Search queries` field, e.g. `nike`, `adidas`. Each query is searched independently.
3. *(Optional)* Add **advertiser Page IDs** if you already know them, instead of or alongside queries.
4. **Set a country** — required, and pinned to the proxy exit so results match the market you asked for.
5. **Run.** Each ad is one row in the dataset; export to JSON, CSV, or Excel from the Storage tab.

The first run on a new account uses $5 of free Apify credit — that's roughly **1 500 ads** at our pricing.

### 📥 Input

The schema lives in `.actor/input_schema.json`. The fields:

| Field | Type | Required | Default | Notes |
|---|---|:--:|---|---|
| `queries` | array of string | one of | `["nike", "adidas"]` | Brand/keyword searches |
| `pageIds` | array of string | one of | `[]` | Meta advertiser Page IDs |
| `country` | string | yes | `US` | ISO-3166 alpha-2, pinned to the proxy exit |
| `activeStatus` | enum | no | `active` | `active` / `inactive` / `all` |
| `adType` | enum | no | `all` | `all` / `political_and_issue_ads` |
| `maxResults` | integer | no | `300` | Total dataset items across all targets. `0` = unlimited |
| `maxPages` | integer | no | `20` | Pagination ceiling per target (~8–10 ads/page) |
| `proxyConfiguration` | proxy config | no | RESIDENTIAL enabled | See note below — required for pagination |

At least one of `queries` or `pageIds` must contain an entry.

**Proxy defaults to `RESIDENTIAL` and it isn't a cosmetic choice.** We confirmed live that Meta's ad-search results (page 1) work fine on a datacenter IP, but its pagination endpoint applies a much stricter rate limit to datacenter traffic — page 2+ gets rejected almost immediately. Switching this input to a datacenter proxy will cap every run at the first ~30 ads and fail loud (see [Limitations](#limitations)) once `maxPages` asks for more than that.

#### Example input

```json
{
  "queries": ["nike", "adidas"],
  "country": "US",
  "activeStatus": "active",
  "adType": "all",
  "maxResults": 500,
  "maxPages": 20,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

### 📤 Output

Every row is one ad. Example:

```json
{
  "ad_archive_id": "888379050198830",
  "page_id": "521386541053415",
  "page_name": "EV's clothing boxes",
  "is_active": true,
  "collation_count": 1,
  "collation_id": "1269601201531459",
  "publisher_platforms": ["FACEBOOK", "INSTAGRAM"],
  "link_url": "https://edjvntge.uk/collections/store",
  "cta_text": "Shop now",
  "cta_type": "SHOP_NOW",
  "body_text": "SALE NOW! You had me at 9 vintage tees for £99",
  "link_description": "Limited time only! Get 9 vintage tees for £99!",
  "image_urls": ["https://scontent.fkun1-1.fna.fbcdn.net/..."],
  "display_format": "IMAGE",
  "start_date_unix": 1785740400,
  "end_date_unix": null,
  "ad_library_url": "https://www.facebook.com/ads/library/?id=888379050198830",
  "query": "nike",
  "country": "US",
  "scraped_at": "2026-09-12T18:00:00+00:00"
}
```

Export options once the run finishes:

- **JSON** — full payload, ideal for AI/RAG pipelines
- **CSV / Excel** — for analyst spreadsheets
- **JSONL** — line-delimited, easy to stream into a warehouse
- **API** — fetch programmatically via `GET /v2/datasets/{id}/items`; webhook on `ACTOR.RUN.SUCCEEDED` for live pipelines

### 💰 Pricing

Pay-per-event. You pay for what you get, nothing for what you ask for:

| Event | Price | When charged |
|---|---:|---|
| `actor-start` | **$0.20** | Base fee, once per run (warm-up, challenge-solve, proxy resolution) |
| `ad-result` | **$0.003** | Per unique ad written to the dataset |

**Examples**:

| Pull | Cost |
|---|---:|
| 100 ads | $0.50 |
| 1 000 ads | $3.20 |
| 10 000 ads | $30.20 |

### 🚧 Limitations

- **No `spend`, `impressions`, or `funding_entity` fields.** Those figures are only ever disclosed via Meta's separate, credentialed Graph API for political/issue ads — the public search surface this Actor uses doesn't expose them for ordinary commercial ads, and we don't fabricate numbers Meta doesn't publish.
- **No direct video/image downloads.** `image_urls` links to Meta's own CDN; we don't mirror media files.
- **Pagination needs the RESIDENTIAL proxy tier — this is the default and we recommend keeping it.** We confirmed live that Meta's pagination endpoint rate-limits datacenter IPs almost immediately, even though the first page of results loads fine on one. On the default `RESIDENTIAL` setting our last cloud run walked page 1 → 3 and returned 49 unique ads across 23 advertisers with zero blocks, and a longer walk from a workstation reached 60 pages / 580+ unique ads on a single query; `maxPages` defaults conservatively to 20 so a typical run stays cheap. If you switch to a datacenter proxy to save cost, expect the run to cap at the first ~30 ads per target and fail loud with a clear error the moment `maxPages` asks for more than that — we'd rather fail visibly than quietly under-deliver.
- **Meta can change its session-verification step or internal query IDs without notice.** If that happens, a run will fail loud with a clear status message rather than silently return nothing — and we ship a fix the same way we found the original path.

### ❓ FAQ

**Is this legal?**

Yes. The Meta Ad Library is a public registry Meta operates under EU DSA and US regulatory pressure. We search only what the public UI exposes, at a polite pace, and don't bypass any login wall or paywall.

**Do I need a Meta developer account or access token?**

No. Unlike Meta's official (and access-gated) Graph API, this Actor works entirely against the public Ad Library search surface — no app, no App Review, no `ads_read` permission.

**Why is coverage sometimes less than the total Meta reports on the site?**

Two reasons: your `maxPages`/`maxResults` caps, and identical creatives Meta groups into one "collation" — we return the group's representative ad rather than every duplicate variation.

**Can I search political or issue ads specifically?**

Yes — set `adType` to `political_and_issue_ads`. Meta discloses richer government-facing metadata for these via its own compliance tools; this Actor returns the same ad-copy fields as any other search.

**How do I schedule this to run automatically?**

Apify Console → Schedules → attach this Actor → set your cron. Daily or weekly is plenty; the Ad Library doesn't change fast enough to need more.

**What integrations does this Actor support?**

- **Schedule** — Apify Console → Schedules for recurring monitoring.
- **Webhooks** — register `ACTOR.RUN.SUCCEEDED` to fire a downstream pipeline.
- **API** — `POST /v2/acts/DevilScrapes~meta-ad-library-scraper/run-sync-get-dataset-items` for a single synchronous pull.
- **Make / Zapier** — every Apify Actor surfaces as a node out of the box.

### 💬 Your feedback

Spotted a bug, missing field, or want a new feature? Open an issue on the [Apify Store listing](https://apify.com/DevilScrapes/meta-ad-library-scraper) — we read every one.

Built by **Devil Scrapes** — Apify Actors with attitude. PPE, transparent pricing, no junk fields.

# Actor input Schema

## `queries` (type: `array`):

Brand or keyword searches, e.g. <code>nike</code>. Every matching ad across all pages found is returned.

## `pageIds` (type: `array`):

Optional Meta advertiser Page IDs to scrape instead of, or alongside, search queries. Use when you already know the exact Page.

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

ISO-3166 alpha-2 country code the search is scoped to, e.g. <code>US</code>. <strong>Pinned</strong> — also used to resolve the proxy exit, since a mismatched exit can return a plausible but wrong-country result set.

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

Filter by whether the ad is currently running.

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

Meta's ad-category filter.

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

Total dataset items to keep across all targets. <code>0</code> disables the cap.

## `maxPages` (type: `integer`):

Hard ceiling on GraphQL pagination round-trips per query/Page ID. Each page returns roughly 8–10 ads, so the default 20 covers up to ~200 ads per target. Verified live to 60 pages with no block over a residential-class exit IP — see the Proxy field below, pagination needs it.

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

Apify Proxy, exit country pinned to <em>Country</em> above. <strong>Defaults to RESIDENTIAL</strong> — confirmed live 2026-09-12 that Meta's pagination endpoint (not the first page) hard rate-limits datacenter-tier IPs with a 200 OK carrying a GraphQL error, which silently caps every run at page 1 on a cheaper tier. Switching to datacenter will save cost but caps you at ~30 ads/target.

## Actor input object example

```json
{
  "queries": [
    "nike",
    "adidas"
  ],
  "pageIds": [],
  "country": "US",
  "activeStatus": "active",
  "adType": "all",
  "maxResults": 300,
  "maxPages": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

Full set of scraped ads — one item per ad. Streamed JSON via the Apify dataset API.

## `datasetItemsCsv` (type: `string`):

Same data, CSV export. Drop straight into a spreadsheet or warehouse loader.

## `datasetView` (type: `string`):

Open the run's dataset in the Console with the Overview view applied.

# 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 = {
    "queries": [
        "nike",
        "adidas"
    ],
    "pageIds": [],
    "country": "US",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/meta-ad-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 = {
    "queries": [
        "nike",
        "adidas",
    ],
    "pageIds": [],
    "country": "US",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/meta-ad-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 '{
  "queries": [
    "nike",
    "adidas"
  ],
  "pageIds": [],
  "country": "US",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call devilscrapes/meta-ad-library-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devilscrapes/meta-ad-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/5QzCmdah5gmtv1qwV/builds/HdL2OhCUNcfyUXVAq/openapi.json
