# Meta Ads Library Scraper (`aurenic/meta-ads-library-scraper`) Actor

Scrape Meta (Facebook/Instagram) Ad Library by keyword, advertiser, or Ad Library URL. Keyless GraphQL reverse-engineering — no Facebook login, no API key, no developer account.

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

## Pricing

from $0.25 / 1,000 results

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

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

## Meta Ads Library Scraper

Scrape Meta (Facebook and Instagram) Ad Library by keyword, advertiser page ID, or full Ad Library URL — keyless, no Facebook login, no API key, no developer account.

### What does Meta Ads Library Scraper do?

This actor reverse-engineers the internal GraphQL endpoint that powers Meta's public Ad Library web UI. It accepts keyword searches, numeric Facebook page IDs, or full Ad Library URLs, then paginates through results using Meta's own cursor-based GraphQL pagination. It returns structured ad records including ad copy, creative URLs, CTA text, landing pages, platform placement, active status, and — where Meta publishes it — spend and reach estimates for political/issue ads.

### Output fields

**Standard ad record**

| Field | Type | Description |
|---|---|---|
| ad_archive_id | string | Meta's archived ad identifier |
| page_id | string | Numeric Facebook page ID |
| page_name | string | Advertiser page display name |
| is_active | boolean | Whether the ad is currently running |
| start_date_formatted | string | Ad start date (YYYY-MM-DD) |
| end_date_formatted | string | Ad end date, if stopped |
| publisher_platform | array | Platforms (facebook, instagram, audience_network, messenger) |
| ad_creative_body | string | Primary ad copy text |
| cta_text | string | Call-to-action button label |
| cta_type | string | CTA enum (SHOP_NOW, LEARN_MORE, …) |
| link_url | string | Landing page URL |
| caption | string | Displayed domain caption |
| image_urls | array | Creative image URLs |
| video_urls | array | Creative video URLs |
| spend | object | Spend range (political/issue ads only) |
| reach_estimate | object | Reach range (political/issue ads only) |
| impressions | object | Impression range (political/issue ads only) |
| advertiser | object | Advertiser transparency data |
| collation_count | integer | Number of creative variants in this collation |
| source_query | string | Which input source produced this row |
| scraped_at | string | ISO timestamp |

### Who is it for?

- **Performance marketers** running competitive ad research and creative swipe-file workflows
- **Media buyers** monitoring competitor ad launches, rotation cadence, and landing page changes
- **Brand strategists** tracking share-of-voice and messaging shifts across Facebook and Instagram
- **Compliance teams** auditing political, housing, employment, and credit ad transparency data
- **Data teams** building ad intelligence pipelines and alerting on new creatives
- **Agencies** onboarding new clients by pulling their live and historical ad inventory

### Pricing

**Pay per result: $0.30 per 1,000 ads scraped.**

You are charged only for ads actually written to the dataset. A blocked or rate-limited run that returns no ads is not billed.

### How to use it

1. Open the actor in Apify Console.
2. Provide at least one input source: keyword queries, advertiser page IDs, or full Ad Library URLs.
3. Set your country filter (or leave as `ALL` for worldwide).
4. Choose ad status (active, inactive, or both), ad category, and media type filters.
5. Optionally set a max results per source cap to control cost.
6. Leave the proxy setting at the default (Apify residential proxy).
7. Click **Start** and export the dataset as JSON, CSV, or Excel.

### Output example

```json
{
  "ad_archive_id": "1599744897819025",
  "page_id": "122105379866001758",
  "page_name": "Cuidalia",
  "is_active": true,
  "start_date_formatted": "2026-09-01",
  "end_date_formatted": null,
  "publisher_platform": ["facebook", "instagram"],
  "ad_creative_body": "Tu descanso merece la mejor almohada...",
  "cta_text": "Comprar",
  "cta_type": "SHOP_NOW",
  "link_url": "https://cuidalia.org",
  "caption": "cuidalia.org",
  "image_urls": ["https://scontent-mad1-1.xx.fbcdn.net/..."],
  "video_urls": [],
  "spend": null,
  "reach_estimate": null,
  "impressions": null,
  "advertiser": null,
  "collation_count": 3,
  "source_query": "cuidalia",
  "scraped_at": "2026-09-28T12:00:00.000Z"
}
```

### Technical details

- **Stack:** Node.js 24, `apify` SDK, `impit` HTTP client with Chrome TLS impersonation.
- **Anti-bot:** The actor handles Meta's per-session `__rd_verify` challenge automatically (GET → 403 with 481-byte challenge body → POST verify path → re-GET → 200). The `doc_id` for `AdLibrarySearchPaginationQuery` is harvested from the live JS bundle on each run and falls back to a known-good value.
- **Proxy:** Residential proxy is required. Meta's GraphQL pagination endpoint returns HTTP 200 carrying a GraphQL rate-limit error envelope to datacenter IPs. The default proxy configuration is Apify residential.
- **Pagination:** Cursor-based via `page_info.end_cursor`, up to 60 pages per source. 1.2–3 second randomized delay between pages.
- **Extraction:** Direct GraphQL response parsing — no HTML scraping of rendered results.

### Known limits

- **Commercial ad spend and reach are not available.** Meta publishes spend, impressions, and audience demographics only for political/issue ads and EU DSA-covered ads. No scraper can retrieve these for standard commercial ads.
- **Residential proxy bandwidth is billed separately** by Apify at the platform's standard residential rate. This is in addition to the per-result actor price.
- **The 60-page cap per source** is imposed by this actor to bound run cost; Meta's own pagination may go deeper.
- **Sorting by true ad launch time is not reliable.** Meta's `start_date` field reflects impression delivery windows, not the actual date the ad first went live. The Ad Library API does not expose a sortable launch timestamp.
- **Meta rotates the GraphQL `doc_id` periodically.** The actor harvests it live, but a rotation between harvest and pagination can force a fallback value that may be stale.

### FAQ

**Do I need a Facebook account or API key?**
No. The actor uses the same public GraphQL endpoint the Ad Library web UI calls. No login, no token, no developer account, no App Review.

**Why is residential proxy required?**
Meta's `/api/graphql/` pagination endpoint applies a stricter per-IP rate limit than the public search page. A datacenter IP gets an HTTP 200 with a `Rate limit exceeded` GraphQL error envelope on the first pagination request. Residential exits clear this.

**Can I get spend and impressions for commercial ads?**
No — and no other scraper can either. Meta publishes those fields only for political/issue ads and EU DSA ads. Any actor claiming otherwise for commercial ads is fabricating data.

**How many ads can I get per run?**
Up to 60 pages × 30 ads = 1,800 ads per source, capped by `maxResultsPerSource` if set. Multiple sources run sequentially.

**Does it support Instagram ads?**
Yes. Meta's Ad Library covers Facebook, Instagram, Audience Network, and Messenger. The `publisher_platform` field on each ad tells you where it ran.

**What if the run returns zero ads?**
Check that your proxy is residential. If you overrode the proxy field, ensure `RESIDENTIAL` is still in the groups. Datacenter proxies will silently produce empty runs.

### Support

Open an issue on the Actor's page for bugs or feature requests.

### Changelog

#### 0.1 — 2026-09-28

- Initial build. Keyless GraphQL reverse-engineering of Meta Ad Library.
- `__rd_verify` challenge handling (GET → POST → re-GET).
- Live `doc_id` harvest with fallback.
- Cursor-based pagination via `AdLibrarySearchPaginationQuery`.
- Residential proxy default.
- Input modes: keyword queries, advertiser page IDs, full Ad Library URLs.
- Filters: country, active status, ad category, media type, sort mode.
- Output schema with 22 fields including creative URLs, CTA, platform placement, and political-ad spend/reach.

# Actor input Schema

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

Keyword searches for the Ad Library. Each query runs as its own search.

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

Numeric Facebook page IDs. Each ID runs a PAGE search.

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

Full Meta Ad Library URLs. Each URL is fetched as-is with its own filters.

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

Two-letter ISO country code (US, GB, DE, …) or ALL for worldwide.

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

Return only active ads, only inactive ads, or both.

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

Meta transparency category filter.

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

Restrict results by creative format.

## `sortByMode` (type: `string`):

How Meta orders search results.

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

Maximum ads to return per query, page ID, or URL. Leave empty for as many as possible.

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

Residential proxy required. Meta blocks datacenter IPs on the Ad Library GraphQL endpoint.

## Actor input object example

```json
{
  "queries": [
    "nike"
  ],
  "country": "ALL",
  "adActiveStatus": "ACTIVE",
  "adType": "ALL",
  "mediaType": "ALL",
  "sortByMode": "SORT_BY_TOTAL_IMPRESSIONS",
  "maxResultsPerSource": 50,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# 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 = {};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {}

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

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

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

```

## CLI example

```bash
echo '{}' |
apify call aurenic/meta-ads-library-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

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