# Meta Ad Library Advertiser Disclosure Scraper (`jungle_synthesizer/meta-ad-library-advertiser-disclosure-scraper`) Actor

Search Meta Ad Library ads by keyword, advertiser page, country and ad type. Returns spend and impressions ranges, reach estimates, per-country reach, payer byline, state-media and AI-media disclosure flags, and creative-variant collation counts the standard scrapers omit.

- **URL**: https://apify.com/jungle\_synthesizer/meta-ad-library-advertiser-disclosure-scraper.md
- **Developed by:** [BowTiedRaccoon](https://apify.com/jungle_synthesizer) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 88.5% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 record scrapeds

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?

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

## Meta Ad Library Advertiser Disclosure Scraper

Scrape the [Meta Ad Library](https://www.facebook.com/ads/library/) by keyword, advertiser page, country, and ad type. Returns the usual creative fields plus the disclosure layer most scrapers skip — spend and impressions ranges, reach estimates, per-country reach, payer byline, state-media and AI-generated-media flags, and creative-variant collation counts.

***

### Meta Ad Library Advertiser Disclosure Scraper Features

- Searches by free-text keyword or by a specific advertiser's Page ID
- Filters by country, ad type (all ads, political and issue, housing, employment, financial products), and active status
- Returns EU/political disclosure fields — spend range, impressions range, reach estimate, and per-country targeted/reached breakdown
- Surfaces the payer byline on political and issue ads, plus state-media and AI-generated-media disclosure flags
- Reports `collation_count` — how many near-identical creative variants Meta collapsed into one card, which most competing scrapers silently drop
- Optionally pulls each advertiser's Page creation date from their public transparency panel, deduplicated across the run so you don't pay for the same lookup twice
- Runs on any of Meta's 200+ Ad Library country scopes, not just the US

***

### Who Uses Meta Ad Library Data?

- **Compliance and brand-safety teams** — track political and issue ad spend, payer disclosures, and state-media labels across a watchlist of advertisers.
- **Competitive intelligence analysts** — pull a competitor's full creative rotation, including how many variants Meta grouped under one ad.
- **Political and campaign researchers** — monitor spend ranges, reach estimates, and per-country targeting on issue ads without manually clicking through the Ad Library UI.
- **Ad agencies** — benchmark creative formats, CTAs, and landing pages across an industry, or check what a specific brand is currently running.
- **Trust and safety researchers** — flag ads with report counts, gated types, and AI-generated-media disclosures.

***

### How Meta Ad Library Advertiser Disclosure Scraper Works

1. Give it one or more keywords, one or more advertiser Page IDs, or both, plus the countries and ad type you want.
2. It runs one search per keyword/page × country combination and pulls every ad record from that search page.
3. Each record is normalized into a flat row — dates, spend ranges, and reach estimates parsed out of Meta's raw text into usable numbers.
4. If Page Transparency lookup is enabled, it adds one extra row per unique advertiser found, carrying their Page creation date.

***

### Input

```json
{
  "keywords": ["nike"],
  "countries": ["US"],
  "adType": "all",
  "activeStatus": "active",
  "fetchPageTransparency": false,
  "maxItems": 30
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `keywords` | array | `["nike"]` | Free-text keyword searches, matched against ad creative and advertiser name. Each keyword runs as a separate query per selected country. Provide this, `pageIds`, or both. |
| `pageIds` | array | `[]` | Meta advertiser Page IDs to look up directly — every ad that page is running, instead of or alongside keyword search. |
| `countries` | array | `["US"]` | Ad Library country scope(s) to search, from Meta's own 200+ country list (or `ALL`). Each selected country is a separate query. |
| `adType` | string | `all` | `all`, `political_and_issue_ads`, `housing_ads`, `employment_ads`, or `financial_products_and_services_ads`. |
| `activeStatus` | string | `active` | `active`, `inactive`, or `all`. |
| `fetchPageTransparency` | boolean | `false` | When enabled, adds one extra row per unique advertiser carrying their Page creation date (see Output below). |
| `maxItems` | integer | `30` | Maximum number of records to return across all query combinations. |

Political/issue ad search example — pulls the full EU/US disclosure layer:

```json
{
  "keywords": ["senate"],
  "countries": ["US"],
  "adType": "political_and_issue_ads",
  "activeStatus": "all",
  "maxItems": 100
}
```

Direct advertiser lookup with Page Transparency enrichment:

```json
{
  "pageIds": ["15087023444"],
  "countries": ["US"],
  "fetchPageTransparency": true,
  "maxItems": 50
}
```

***

### Meta Ad Library Advertiser Disclosure Scraper Output Fields

#### Ad records

```json
{
  "ad_archive_id": "1509773093567021",
  "page_id": "112727500143177",
  "page_name": "Whatnot",
  "advertiser_byline": null,
  "is_active": true,
  "start_date": "2025-11-14T08:00:00.000Z",
  "end_date": "2026-08-09T07:00:00.000Z",
  "total_active_time_seconds": null,
  "publisher_platforms": ["FACEBOOK", "INSTAGRAM", "MESSENGER"],
  "display_format": "VIDEO",
  "ad_creative_bodies": ["Yup, you can buy new outfits without breaking the bank. Save on fashion. Shop on Whatnot!"],
  "ad_creative_link_titles": [],
  "ad_creative_link_descriptions": [],
  "link_url": "http://itunes.apple.com/app/id1488269261",
  "cta_type": "INSTALL_MOBILE_APP",
  "image_urls": [],
  "video_urls": ["https://video-ord5-2.xx.fbcdn.net/..."],
  "categories": ["UNKNOWN"],
  "spend_lower_bound": null,
  "spend_upper_bound": null,
  "currency": null,
  "impressions_lower_bound": null,
  "impressions_upper_bound": null,
  "reach_estimate": null,
  "targeted_or_reached_countries": [],
  "state_media_run_label": null,
  "contains_digital_created_media": false,
  "collation_id": "849763014216568",
  "collation_count": null,
  "gated_type": "ELIGIBLE",
  "report_count": null,
  "has_user_reported": false,
  "page_profile_url": "https://www.facebook.com/RealWhatnot/",
  "page_transparency_creation_date": null,
  "page_transparency_admin_countries": [],
  "page_transparency_previous_names": [],
  "search_country": "US",
  "search_query": "nike",
  "scraped_at": "2026-08-10T04:31:56.126Z"
}
```

| Field | Type | Description |
|-------|------|--------------|
| `ad_archive_id` | string | Meta's Ad Library ID — the record's primary key. Null on advertiser-summary rows. |
| `page_id` | string | Advertiser's Facebook Page ID |
| `page_name` | string | Advertiser's Facebook Page name |
| `advertiser_byline` | string | Disclosed payer string ("Paid for by...") on political/issue ads |
| `is_active` | boolean | Whether the ad is currently running |
| `start_date` / `end_date` | string | ISO date the ad began / stopped running |
| `total_active_time_seconds` | integer | Total time the ad has been active, when disclosed |
| `publisher_platforms` | array | facebook, instagram, audience\_network, messenger, threads |
| `display_format` | string | VIDEO, IMAGE, DCO, CAROUSEL, etc. |
| `ad_creative_bodies` / `ad_creative_link_titles` / `ad_creative_link_descriptions` | array | Ad copy text |
| `link_url` | string | The ad's landing page |
| `cta_type` | string | Call-to-action type (SHOP\_NOW, LEARN\_MORE, etc.) |
| `image_urls` / `video_urls` | array | Creative media URLs |
| `categories` | array | Meta's ad category taxonomy, including political/issue flags |
| `spend_lower_bound` / `spend_upper_bound` | number | EU/political disclosure spend range |
| `currency` | string | Currency code for the spend range (political/issue ads) |
| `impressions_lower_bound` / `impressions_upper_bound` | integer | Impressions range (political/issue ads) |
| `reach_estimate` | integer | Estimated reach (political/issue ads) |
| `targeted_or_reached_countries` | array | EU DSA per-country reach/targeting disclosure |
| `state_media_run_label` | string | State-controlled-media disclosure label, when present |
| `contains_digital_created_media` | boolean | AI/synthetic-media disclosure flag |
| `collation_id` / `collation_count` | string / integer | Ad-set grouping key and how many near-identical variants Meta collapsed into this record |
| `gated_type` | string | Meta's eligibility/gating classification |
| `report_count` / `has_user_reported` | integer / boolean | User report disclosure, when Meta discloses it |
| `page_profile_url` | string | URL of the advertiser's Facebook Page |
| `page_transparency_creation_date` | string | Only populated on advertiser-summary rows — see below |
| `page_transparency_admin_countries` / `page_transparency_previous_names` | array | Reserved fields — Meta discloses these only for a subset of pages, usually empty |
| `search_country` / `search_query` | string | The query this record came from |
| `scraped_at` | string | ISO timestamp when this record was scraped |

#### Advertiser-summary rows (`fetchPageTransparency: true`)

When enabled, the run adds one extra row per unique advertiser, join it to the ad rows above on `page_id`:

```json
{
  "ad_archive_id": null,
  "page_id": "15087023444",
  "page_profile_url": "https://www.facebook.com/15087023444/",
  "page_transparency_creation_date": "2008-05-21",
  "search_country": "US",
  "scraped_at": "2026-08-10T04:36:13.960Z"
}
```

Every other field on this row is `null` or an empty array — `ad_archive_id` being null is how you tell an advertiser-summary row apart from an ad row.

***

### Resuming a large crawl

Every run emits a `resumeCursor` in its Output. If a large crawl stops before it finishes — because it hit `maxItems`, your spend cap (`maxTotalChargeUsd`), or was aborted — start a new run with **the same input** plus that `resumeCursor` to continue from where it left off. The crawl resumes from the queued work the previous run didn't reach.

- You are **not re-charged** for records the earlier run already delivered.
- Resume within your account's run-retention window — on the free tier, roughly your 10 most recent runs. Once the source run is pruned, its `resumeCursor` is no longer valid.
- `resumeCursor` is opaque — supply it unmodified.

***

### FAQ

#### How do I scrape the Meta Ad Library?

Meta Ad Library Advertiser Disclosure Scraper needs no account, login, or API key. Give it a keyword or advertiser Page ID and a country, and it returns every matching ad with the disclosure fields Meta attaches to it.

#### What data can I get from Meta's Ad Library?

Every field on an ad's card, plus the fields most scrapers leave behind — spend and impressions ranges, reach estimates, per-country targeting, payer byline, and creative-variant collation counts. Political and issue ads carry the full disclosure set; other ad types return the fields Meta discloses for them.

#### Can I filter by ad type or country?

Yes. `adType` filters to all ads, political and issue ads, housing ads, employment ads, or financial products ads. `countries` accepts any of Meta's 200+ Ad Library country scopes, and you can select more than one — each runs as its own query.

#### How much does Meta Ad Library Advertiser Disclosure Scraper cost to run?

Pay-per-result pricing, billed per ad record returned. `maxItems` caps total spend per run.

#### Do I need an advertiser's Page ID to search?

No. Keyword search alone works fine. A Page ID gets you every ad a specific advertiser is running, which is faster than guessing keywords when you already know who you're watching.

***

### Need More Features?

Need custom fields, filters, or a different target site? [File an issue](https://console.apify.com/actors/issues) or get in touch.

### Why Use Meta Ad Library Advertiser Disclosure Scraper?

- **Disclosure fields included** — spend ranges, impressions, reach, byline, and collation counts come standard, not as an add-on.
- **Honest about collation** — reports how many creative variants Meta grouped under one card, so you know when a single result is hiding a bigger rotation.
- **Country-flexible** — search any of Meta's 200+ Ad Library country scopes in one run, not just the US.

# Actor input Schema

## `sp_intended_usage` (type: `string`):

What will this data feed? E.g. lead lists, KYB checks, price tracking.

## `sp_improvement_suggestions` (type: `string`):

Provide any feedback or suggestions for improvements.

## `sp_contact` (type: `string`):

We'll personally help with your use case. No spam.

## `resumeCursor` (type: `string`):

Leave empty for a fresh crawl. To CONTINUE a previous run where it stopped — without paying again for records you already received — paste the `resumeCursor` value from that run's Output (the run's OUTPUT key). Resume promptly: the previous run's data expires with your account's retention window (free tier: your ~10 most recent runs).

## `keywords` (type: `array`):

Free-text keyword searches, matched against ad creative/advertiser name (Meta's own Ad Library search box). Each keyword is a separate query per selected country. Provide this, `pageIds`, or both.

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

Meta advertiser Page IDs to look up directly (all ads run by that page), instead of or alongside keyword search. Find a Page ID from an ad's advertiser link or Meta's own Ad Library UI.

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

Ad Library country scope(s) to search. Each selected country runs as a separate query. Defaults to United States to keep a first run cheap — select more (or every country) for full worldwide coverage; each additional country is one more Bright Data Web Unlocker request per keyword/page.

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

Ad Library category filter. "All ads" includes political/issue ads; the three special categories return only ads Meta classifies under that disclosure regime.

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

Filter by whether the ad is currently running.

## `fetchPageTransparency` (type: `boolean`):

When enabled, fetches each unique advertiser's public Page Transparency panel once per run (deduplicated) and adds a separate advertiser-summary row per advertiser (ad\_archive\_id null, page\_id set) carrying the page creation date — join client-side on page\_id. Adds roughly one extra request per unique advertiser found, on top of the search requests.

## `maxItems` (type: `integer`):

Maximum number of ad records to return across all keyword/page x country queries.

## Actor input object example

```json
{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "keywords": [
    "nike"
  ],
  "pageIds": [],
  "countries": [
    "US"
  ],
  "adType": "all",
  "activeStatus": "active",
  "fetchPageTransparency": false,
  "maxItems": 30
}
```

# Actor output Schema

## `results` (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 = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "keywords": [
        "nike"
    ],
    "pageIds": [],
    "countries": [
        "US"
    ],
    "adType": "all",
    "activeStatus": "active",
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("jungle_synthesizer/meta-ad-library-advertiser-disclosure-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 = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "keywords": ["nike"],
    "pageIds": [],
    "countries": ["US"],
    "adType": "all",
    "activeStatus": "active",
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("jungle_synthesizer/meta-ad-library-advertiser-disclosure-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 '{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "keywords": [
    "nike"
  ],
  "pageIds": [],
  "countries": [
    "US"
  ],
  "adType": "all",
  "activeStatus": "active",
  "maxItems": 30
}' |
apify call jungle_synthesizer/meta-ad-library-advertiser-disclosure-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jungle_synthesizer/meta-ad-library-advertiser-disclosure-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/aGiyn62h98xfnMwC6/builds/bGb5h38glFoPLDPSO/openapi.json
