# Facebook Ads Library Scraper \[Reach Included] (Richest output) (`memo23/facebook-ads-library-scraper-cheerio`) Actor

Facebook Ads Library Scraper — search the Meta Ad Library by keyword or page URL and export every ad: creatives, copy, spend & impressions, EU reach & transparency, advertiser pages, run dates. Richest schema on the Store. Filter by country, status & media type. Free trial.

- **URL**: https://apify.com/memo23/facebook-ads-library-scraper-cheerio.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Social media, Agents, AI
- **Stats:** 871 total users, 56 monthly users, 99.9% runs succeeded, 51 bookmarks
- **User rating**: 4.79 out of 5 stars

## Pricing

from $0.30 / 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.
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

**Scrape active Facebook ads with filters like keywords, dates, media type, and country — and, when you want it, every video ad's spoken words and opening hook as text**

> 💡 **Two editions, one scraper.** [**Pay-per-event**](https://apify.com/memo23/facebook-ads-library-scraper-ppe) bills **per ad from $0.3 / 1,000**, reach included, no subscription. [**Monthly rental**](https://apify.com/memo23/facebook-ads-library-scraper-cheerio) bills a flat rate and is being retired by Apify. If you are reading this on the rental edition, the pay-per-event one is the cheaper way to run the exact same job.

***

### Overview

This actor scrapes Facebook's Ads Library to extract detailed information about active advertising campaigns. Ideal for marketers, competitors, and researchers analyzing ad strategies. Supports filtering by:

- Keywords/phrases (e.g., "free shipping")
- Date ranges (up to 2025-02-15)
- Media types (video/image/text)
- Countries (e.g., GB for UK-targeted ads)
- Ad status (active/inactive)

Switch on **`transcribeVideos`** and every video ad also comes back as text: the full speech-to-text transcript, its language, and the **spoken first-3-seconds hook** on the same row as the creative, copy, CTA and advertiser. See [Video ad transcripts](#video-ad-transcripts-opt-in).

***

### Features

- **🔎 Keyword Search (no URL needed)**: Just enter search terms, countries, and status — the actor builds the Meta Ad Library query for you. No hand-crafted URLs.
- **🔁 Scheduled monitoring bills only new ads**: name a `watchlistId` and switch on `newAdsOnly`; ads delivered on an earlier run of the same watchlist are left out of the dataset and not charged, so a daily run on a competitor's page pays only for what they launched since.
- **🎙️ Video ad transcripts + spoken hook (opt-in)**: `transcribeVideos: true` transcribes each video ad and adds `transcript`, `transcriptLanguage` and `hookSpoken` (the first 3 seconds of speech) to the ad row. Image-only ads are untouched and cost nothing extra.
- **Flexible URL Input**: Supports both Ads Library URLs and direct Facebook page URLs (e.g., `https://www.facebook.com/SHEINOFFICIAL`)
- **Automatic Page ID Extraction**: Automatically extracts page IDs from Facebook page URLs and converts them to Ads Library format
- **Precision Filtering**: Target ads by 10+ criteria including media type, country, and exact keywords
- **Dynamic Delays**: Randomized delays (5-10s) between API calls to avoid detection
- **Residential Proxies**: Built-in residential proxy, included at no extra cost
- **Retry Resilience**: 100 retries per failed request
- **Concurrency Control**: Process 1-10 pages simultaneously
- **Historical Range**: Scrape ads up to future dates (2025-02-15 supported)

***

### How to Use

1. **Configure Input**: Set your filters in the input JSON (see configuration below)
2. **Run via Apify**: Execute the actor on Apify platform
3. **Export Data**: Download results as JSON, CSV, or Excel
4. **Analyze**: Track ad creatives, sponsors, and run dates

***

### Input Configuration

#### Easiest — Search by keyword (no URL needed)

Just type what you want and the actor builds the Meta Ad Library query for you — no need to hand-craft a URL.

```json
{
    "searchTerms": ["nike", "weight loss"],
    "searchCountries": ["US", "GB"],
    "adActiveStatus": "active",
    "mediaType": "all",
    "searchMode": "keyword_unordered",
    "maxItems": 100
}
```

| Field | What it does |
|---|---|
| `searchTerms` | Keywords or brand names — each term is searched separately |
| `searchCountries` | ISO 2-letter codes (`US`, `GB`, …) or `ALL`; each is queried per term |
| `adActiveStatus` | `active`, `inactive`, or `all` |
| `mediaType` | `all`, `image`, or `video` |
| `searchMode` | `keyword_unordered` (broad — any word) or `keyword_exact_phrase` (auto-quoted) |
| `startDateMin` / `startDateMax` | Only ads that started in this date window (`YYYY-MM-DD`) |

> Need political/housing/employment ad categories, a specific publisher platform, or a content language? Paste a full Ads Library URL via `startUrls` instead (see the advanced examples below).

#### By advertiser — Page IDs (no URL needed)

Have a competitor's Facebook Page ID, or a list of them? Pass them in `advertisers` and the actor pulls every ad from each page, with no URL to build.

```json
{
    "advertisers": ["15087023444", "103598131567301"],
    "adActiveStatus": "all",
    "maxItems": 100
}
```

| Field | What it does |
|---|---|
| `advertisers` | Facebook Page IDs (the numeric id), or a pasted Ads Library page URL containing `view_all_page_id=…`. Each advertiser's ads are returned in full. |

The Page ID is the `view_all_page_id` value in the URL when you open an advertiser's ads in the Ads Library. Country follows `searchCountries` (its first entry) or worldwide when unset. Works on its own or alongside `searchTerms` and `startUrls`.

#### Example 1: Using Facebook Page URLs (Direct)

```json
{
    "startUrls": [
        "https://www.facebook.com/SHEINOFFICIAL",
        "https://www.facebook.com/Nike",
        "https://www.facebook.com/usemotionapp"
    ],
    "maxItems": 100,
    "minDelay": 5,
    "maxDelay": 10
}
```

#### Example 2: Using Ads Library URLs (Advanced Filtering)

```json
{
    "startUrls": [
        "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=GB&is_targeted_country=false&media_type=video&q=%22free%20shipping%22&search_type=keyword_exact_phrase&start_date[min]&start_date[max]=2025-02-15",
        "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&view_all_page_id=103598131567301"
    ],
    "maxItems": 100,
    "minDelay": 5,
    "maxDelay": 10,
    "maxConcurrency": 10,
    "minConcurrency": 1,
    "maxRequestRetries": 100
}
```

### Scheduled monitoring: an ad is never billed twice

Give the run a `watchlistId` (letters, numbers, hyphens, underscores; for example `nike-daily`) and switch on `newAdsOnly`. The actor keeps the `ad_archive_id` of every ad it delivered under that id in a named key-value store; every later run with the same id runs the search in full but leaves those ads out of the dataset and out of the bill. Put the same search on an Apify Schedule and the run delivers exactly the ads that appeared since the last one; the status line says how many were left out.

```json
{
    "searchTerms": ["nike"],
    "searchCountries": ["US"],
    "watchlistId": "nike-daily",
    "newAdsOnly": true
}
```

### Video ad transcripts (opt-in)

Competitor research on video ads stops at the thumbnail unless someone watches every creative. Turn on `transcribeVideos` and the actor does the watching: each ad that carries a video is sent through the [Video & Audio Transcriber](https://apify.com/memo23/video-audio-transcriber) and comes back with its speech as text, on the same row as the ad's copy, CTA, advertiser and run dates.

```json
{
    "searchTerms": ["protein powder"],
    "searchCountries": ["US"],
    "mediaType": "video",
    "transcribeVideos": true,
    "maxItems": 50
}
```

What lands on every ad row when the toggle is on:

| Field | Type | Meaning |
|---|---|---|
| `transcript` | String | null | Full speech-to-text of the ad's video |
| `transcriptLanguage` | String | null | Detected language code (`en`, `de`, …) |
| `hookSpoken` | String | null | The words spoken in the first 3 seconds — the hook, ready to compare across an advertiser's whole library |
| `transcriptError` | String | null | `"no-video"` for image-only ads; otherwise why a video could not be transcribed. `null` on success |

Sample (the ad fields are the usual ones, trimmed here):

```json
{
    "ad_archive_id": "554396010944539",
    "page_name": "Motion",
    "snapshot": { "cta_text": "Learn more", "display_format": "VIDEO", "videos": [ { "video_hd_url": "https://video.xx.fbcdn.net/…" } ] },
    "transcript": "Stop planning your day by hand. Motion builds your schedule for you, moves tasks when meetings land, and …",
    "transcriptLanguage": "en",
    "hookSpoken": "Stop planning your day by hand.",
    "transcriptError": null
}
```

How it behaves:

- **Off by default.** Existing scrape-only runs, tasks and integrations are unchanged.
- **Only videos are transcribed.** Image ads are saved as before with `transcriptError: "no-video"` and no transcription charge.
- **The ad is never lost to a transcript failure.** A video that cannot be fetched or decoded is still saved as an ad row; the reason sits in `transcriptError`.
- **Cost.** The transcriber bills the run that calls it: **$0.05 per started minute of audio**, up to 3 minutes per video, plus $0.01 per transcriber batch. A typical 15–45 second ad is one started minute, so **about $0.05 per video ad** on top of the ad's own $0.0003–0.0005. Failed videos are not charged.
- **Set `mediaType: "video"`** when transcripts are the point of the run, so you are not paying to page through image ads.

### Supported URL Formats

The scraper accepts two types of URLs:

#### 1. Direct Facebook Page URLs

```
https://www.facebook.com/SHEINOFFICIAL
https://www.facebook.com/Nike
https://www.facebook.com/usemotionapp
```

**How it works:**

- The scraper automatically fetches the page with authentication
- Extracts the page ID (e.g., `100064280965738` for SHEIN)
- Converts to Ads Library URL format
- Fetches all ads for that page

#### 2. Facebook Ads Library URLs (with filters)

```
https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=GB&media_type=video&q=keyword
https://www.facebook.com/ads/library/?active_status=all&search_type=page&view_all_page_id=123456
```

**Benefits:**

- Apply advanced filters (country, media type, keywords, date ranges)
- Search by page ID directly
- Filter by active/inactive status

***

#### Input Parameters

| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| **startUrls** | Array | Facebook page URLs (e.g., `https://www.facebook.com/SHEINOFFICIAL`) or Ads Library URLs with filters | One of startUrls / searchTerms / advertisers |
| **advertisers** | Array | Facebook Page IDs (numeric), or pasted Ads Library page URLs with `view_all_page_id`; scrapes every ad from each advertiser | Optional |
| **maxItems** | Integer | Max ads to scrape per run. On a free Apify plan a run is capped at 30 ads and to ONE search target — the first start URL, search-term/country pair or advertiser the input builds, whichever comes first — so a free run with 2 terms x 2 countries returns only the first combination. Paying Apify accounts remove both caps. | 10000 |
| **minDelay** | Integer | Minimum delay (seconds) between API calls | 5 |
| **maxDelay** | Integer | Maximum delay (seconds) between API calls | 10 |
| **maxConcurrency** | Integer | Max parallel page processing threads | 10 |
| **minConcurrency** | Integer | Minimum parallel threads | 1 |
| **maxRequestRetries** | Integer | Retry attempts for failed requests | 100 |
| **proxy** | Object | Leave empty. The actor routes every run through its own built-in residential proxy at no extra cost. Set this only to use proxies you pay for yourself. | built-in residential |
| **watchlistId** | String | Named memory for scheduled runs; ads delivered under this id are remembered by `ad_archive_id`. | — |
| **newAdsOnly** | Boolean | Leave out ads already on the watchlist; they are not written and not charged. Needs `watchlistId`. | false |
| **transcribeVideos** | Boolean | Transcribe every video ad (speech-to-text + spoken first-3-seconds hook) and add `transcript`, `transcriptLanguage`, `hookSpoken` to the row. Billed per started minute of audio by the transcriber; image ads free. | false |

***

### Output Structure

```json
{
    "FB AD Link": "https://www.facebook.com/ads/library/?id=554396010944539",
    "FB Link": "https://www.facebook.com/usemotionapp/",
    "Company Name": "Motion",
    "Website": "usemotion.com",
    "ad_archive_id": "554396010944539",
    "ad_id": null,
    "archive_types": [],
    "categories": [
        "UNKNOWN"
    ],
    "collation_count": 3,
    "collation_id": "693850463625155",
    "contains_digital_created_media": false,
    "contains_sensitive_content": false,
    "currency": "",
    "end_date": 1750921200,
    "entity_type": "PERSON_PROFILE",
    "fev_info": null,
    "finserv_ad_data": null,
    "gated_type": "ELIGIBLE",
    "has_user_reported": false,
    "hidden_safety_data": false,
    "hide_data_status": "NONE",
    "impressions_with_index": {
        "impressions_text": null,
        "impressions_index": -1
    },
    "is_aaa_eligible": true,
    "is_active": true,
    "is_profile_page": false,
    "menu_items": [],
    "page_id": "103598131567301",
    "page_is_deleted": false,
    "page_name": "Motion",
    "political_countries": [],
    "publisher_platform": [
        "FACEBOOK",
        "INSTAGRAM"
    ],
    "reach_estimate": null,
    "regional_regulation_data": {
        "finserv": {
            "is_deemed_finserv": false,
            "is_limited_delivery": false
        },
        "tw_anti_scam": {
            "is_limited_delivery": false
        }
    },
    "report_count": null,
    "snapshot": {
        "body": {
            "text": "Be 137% more productive."
        },
        "branded_content": null,
        "brazil_tax_id": null,
        "byline": null,
        "caption": "usemotion.com",
        "cards": [],
        "cta_text": "Saznajte više",
        "cta_type": "LEARN_MORE",
        "country_iso_code": null,
        "current_page_name": "Motion",
        "disclaimer_label": null,
        "display_format": "IMAGE",
        "event": null,
        "images": [
            {
                "original_image_url": "https://scontent.fhan3-3.fna.fbcdn.net/v/t39.35426-6/502492793_1054293803317783_201280583862847808_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=c53f8f&_nc_ohc=79MpJoaVUG4Q7kNvwGzgwMb&_nc_oc=AdmiCbM4h7hHcjzS78hBXhh-kqbqOiyZbu5YcnjTmYi8fJyxiRCghxaZKOkyGakvKAg&_nc_zt=14&_nc_ht=scontent.fhan3-3.fna&_nc_gid=NsTWAzJgoGosAcGMska1iA&oh=00_AfOBxg0zBZE5I8h5tQ7QlqjO3BHFUqgBjNGOIwlhhhXnmQ&oe=6863D5BA",
                "resized_image_url": "https://scontent.fhan3-3.fna.fbcdn.net/v/t39.35426-6/502652905_549954111382058_5494539714617214349_n.jpg?stp=dst-jpg_s600x600_tt6&_nc_cat=101&ccb=1-7&_nc_sid=c53f8f&_nc_ohc=11YsJKhHL9gQ7kNvwFQyF27&_nc_oc=AdmwIyTGYmabIQ3WHIslhtAT6bmK-pIkXvG6fem73EUvNaeqpRlhhRrs6w-ml-ufqI8&_nc_zt=14&_nc_ht=scontent.fhan3-3.fna&_nc_gid=NsTWAzJgoGosAcGMska1iA&oh=00_AfN41oyHr6YPW3Oq9j2P4jT45pOn01uFJHcFHeXyg51a7A&oe=6863D380",
                "watermarked_resized_image_url": "",
                "image_crops": []
            }
        ],
        "is_reshared": false,
        "link_description": "Motion Teams | Automatically prioritize tasks, schedule meetings, and resolve calendar conflicts. Used by over 10k CEOs and professionals to improve focus, get more done, and streamline workday.",
        "link_url": "https://www.usemotion.com/teams",
        "page_categories": [
            "Software"
        ],
        "page_entity_type": "PERSON_PROFILE",
        "page_id": "103598131567301",
        "page_is_deleted": false,
        "page_is_profile_page": false,
        "page_like_count": 41998,
        "page_name": "Motion",
        "page_profile_picture_url": "https://scontent.fhan4-5.fna.fbcdn.net/v/t39.35426-6/508816737_586512144117458_1442892662474165395_n.jpg?stp=dst-jpg_s60x60_tt6&_nc_cat=102&ccb=1-7&_nc_sid=c53f8f&_nc_ohc=pC-2Jifxs-kQ7kNvwHYxKPV&_nc_oc=AdlONKKEnKB8H5DdW-UAjQiF6v_oiF5LX2Nu7VArJ_JATWPzqPzqwORlya5VUYCpafM&_nc_zt=14&_nc_ht=scontent.fhan4-5.fna&_nc_gid=NsTWAzJgoGosAcGMska1iA&oh=00_AfPizUrkt_Gl03E8xTDQCz3GBlbeCfXWC_Mkr_YRLNHjBQ&oe=6863E1F2",
        "page_profile_uri": "https://www.facebook.com/usemotionapp/",
        "root_reshared_post": null,
        "title": "AI Project Management",
        "videos": [],
        "additional_info": null,
        "ec_certificates": [],
        "extra_images": [],
        "extra_links": [],
        "extra_texts": [],
        "extra_videos": []
    },
    "spend": null,
    "start_date": 1750921200,
    "state_media_run_label": null,
    "targeted_or_reached_countries": [],
    "total_active_time": 71587,
    "data_reach": {
        "advertiser": {
            "page": {
                "about": {
                    "text": "Motion increases productivity by 137%\n\nWith automation and AI that intelligently plan your day, sched"
                },
                "is_delegate_page_with_linked_primary_profile": false,
                "id": "103598131567301"
            },
            "ad_library_page_info": null
        },
        "aaa_info": {
            "targets_eu": true,
            "location_audience": [
                {
                    "name": "Nizozemska",
                    "num_obfuscated": 0,
                    "type": "countries",
                    "excluded": false
                },
                {
                    "name": "Njemačka",
                    "num_obfuscated": 0,
                    "type": "countries",
                    "excluded": false
                }
            ],
            "gender_audience": "Svi",
            "age_audience": {
                "min": 18,
                "max": 65
            },
            "eu_total_reach": 16,
            "age_country_gender_reach_breakdown": [
                {
                    "country": "NL",
                    "age_gender_breakdowns": [
                        {
                            "age_range": "35-44",
                            "male": 1,
                            "female": 1,
                            "unknown": null
                        },
                        {
                            "age_range": "55-64",
                            "male": 1,
                            "female": null,
                            "unknown": null
                        },
                        {
                            "age_range": "18-24",
                            "male": 1,
                            "female": null,
                            "unknown": null
                        },
                        {
                            "age_range": "25-34",
                            "male": 1,
                            "female": 1,
                            "unknown": null
                        }
                    ]
                },
                {
                    "country": "DE",
                    "age_gender_breakdowns": [
                        {
                            "age_range": "55-64",
                            "male": 2,
                            "female": null,
                            "unknown": null
                        },
                        {
                            "age_range": "18-24",
                            "male": 1,
                            "female": 3,
                            "unknown": null
                        },
                        {
                            "age_range": "35-44",
                            "male": null,
                            "female": 4,
                            "unknown": null
                        }
                    ]
                }
            ],
            "payer_beneficiary_data": [
                {
                    "payer": "Motion",
                    "beneficiary": "Motion"
                }
            ],
            "has_violating_payer_beneficiary": false,
            "is_ad_taken_down": false
        },
        "violation_types": [],
        "verified_voice_context": null,
        "transparency_by_location": null
    }
}
```

#### **Basic Ad Information**

| Field | Type | Description |
|-------|------|-------------|
| `FB AD Link` | String | Direct URL to ad in Facebook's Transparency Library |
| `FB Link` | String | Permanent link to advertiser's Facebook profile/page |
| `Company Name` | String | Registered name of advertising entity (matches page_name) |
| `Website` | String | Main website domain from the ad's link (e.g., usemotion.com) |

***

#### **Ad Identification**

| Field | Type | Description |
|-------|------|-------------|
| `ad_archive_id` | String | Facebook's internal archive identifier (unique per ad) |
| `ad_id` | Null | Legacy field (deprecated by Facebook) |
| `archive_types` | Array | Empty array in sample, used for ad classification |
| `categories` | Array | Facebook's classification (e.g., `["UNKNOWN"]`) |
| `collation_id` | String | Group ID for related ads in same campaign |
| `collation_count` | Integer | Number of ads in this campaign group |
| `page_id` | String | Unique Facebook ID of advertiser's page |

***

#### **Ad Status & Timing**

| Field | Type | Description |
|-------|------|-------------|
| `is_active` | Boolean | `true` = Currently running, `false` = Inactive |
| `start_date` | Integer | Unix timestamp (seconds) of ad start date |
| `end_date` | Integer | Unix timestamp (seconds) of planned end date |
| `total_active_time` | Integer | Duration in seconds the ad has been active |
| `is_aaa_eligible` | Boolean | Indicates if ad is eligible for automated ads |

***

#### **Ad Content & Format**

| Field | Type | Description |
|-------|------|-------------|
| `snapshot.body.text` | String | Full text content of ad |
| `snapshot.title` | String | Headline/title of ad |
| `snapshot.display_format` | String | `IMAGE` for static image ads |
| `snapshot.cta_text` | String | Call-to-action button text (e.g., "Saznajte više") |
| `snapshot.cta_type` | String | Type of CTA (e.g., "LEARN_MORE") |
| `snapshot.link_url` | String | Destination URL when clicking ad |
| `snapshot.link_description` | String | Description text shown in link preview |
| `snapshot.images` | Array | Array containing image objects with URLs |
| `snapshot.images[].original_image_url` | String | Full resolution image URL |
| `snapshot.images[].resized_image_url` | String | Resized version of the image |
| `snapshot.videos` | Array | Empty array for non-video ads |

***

#### **Video Transcript** (only when `transcribeVideos` is on)

| Field | Type | Description |
|-------|------|-------------|
| `transcript` | String | Full speech-to-text of the video ad |
| `transcriptLanguage` | String | Detected language of the speech |
| `hookSpoken` | String | Words spoken in the first 3 seconds of the ad |
| `transcriptError` | String | `no-video` for image ads, otherwise the reason a video was not transcribed; `null` on success |

***

#### **Page/Advertiser Insights**

| Field | Type | Description |
|-------|------|-------------|
| `page_name` | String | Name of Facebook page running ad |
| `page_like_count` | Integer | Total page followers/likes |
| `page_profile_picture_url` | String | Current profile image URL |
| `page_profile_uri` | String | Direct link to page profile |
| `page_categories` | Array | Facebook's business classification (e.g., `["Software"]`) |
| `page_entity_type` | String | Type of page (e.g., `PERSON_PROFILE`) |
| `page_is_deleted` | Boolean | Indicates if the page has been deleted |
| `page_is_profile_page` | Boolean | Indicates if this is a personal profile page |

***

#### **Technical Metadata**

| Field | Type | Description |
|-------|------|-------------|
| `entity_type` | String | `PERSON_PROFILE` (individual) or `BUSINESS` |
| `publisher_platform` | Array | Platforms where ad appears (e.g., `["FACEBOOK", "INSTAGRAM"]`) |
| `contains_digital_created_media` | Boolean | `false` if contains only original media |
| `gated_type` | String | Age/gender gating status (e.g., `ELIGIBLE`) |
| `hide_data_status` | String | `NONE` = Full visibility |
| `is_reshared` | Boolean | `false` if this is an original post |

***

#### **Compliance & Regulations**

| Field | Type | Description |
|-------|------|-------------|
| `political_countries` | Array | Empty array in sample, indicates countries where ad is political |
| `regional_regulation_data` | Object | Contains compliance flags |
| `regional_regulation_data.finserv` | Object | Financial services compliance data |
| `regional_regulation_data.finserv.is_deemed_finserv` | Boolean | If ad is considered financial service |
| `regional_regulation_data.finserv.is_limited_delivery` | Boolean | If ad has delivery restrictions |
| `regional_regulation_data.tw_anti_scam` | Object | Taiwan anti-scam regulations |
| `state_media_run_label` | Null | For government-affiliated ads |

***

#### **User Interaction Data**

| Field | Type | Description |
|-------|------|-------------|
| `report_count` | Null | Number of user reports (restricted) |
| `has_user_reported` | Boolean | `false` if current user hasn't reported the ad |
| `finserv_ad_data` | Null | Financial service ad data (restricted) |
| `fev_info` | Null | Fact-checking verification data |

***

#### **Monetization & Reach**

| Field | Type | Description |
|-------|------|-------------|
| `spend` | Null | Ad budget (restricted) |
| `reach_estimate` | Null | Potential audience size (restricted) |
| `currency` | String | Empty string when spend data is restricted |
| `impressions_with_index` | Object | Contains impression data |
| `impressions_with_index.impressions_text` | Null | Formatted impressions text |
| `impressions_with_index.impressions_index` | Integer | -1 when data is restricted |

***

#### **System Flags**

| Field | Type | Description |
|-------|------|-------------|
| `contains_sensitive_content` | Boolean | `false` if content is not flagged as sensitive |
| `hidden_safety_data` | Boolean | `false` if no safety restrictions are applied |
| `is_profile_page` | Boolean | `false` if not a profile page ad |
| `menu_items` | Array | Empty array in sample |

***

#### **Audience & Targeting**

| Field | Type | Description |
|-------|------|-------------|
| `targeted_or_reached_countries` | Array | Empty array in sample |
| `data_reach` | Object | Detailed audience reach data |
| `data_reach.advertiser` | Object | Information about the advertiser |
| `data_reach.advertiser.page.about.text` | String | Page description text |
| `data_reach.aaa_info` | Object | Detailed audience analytics |
| `data_reach.aaa_info.targets_eu` | Boolean | `true` if targeting EU countries |
| `data_reach.aaa_info.location_audience` | Array | Targeted locations |
| `data_reach.aaa_info.gender_audience` | String | Targeted gender (e.g., "Svi" for all) |
| `data_reach.aaa_info.age_audience` | Object | `{min, max}` age range |
| `data_reach.aaa_info.eu_total_reach` | Integer | Estimated reach in EU |
| `data_reach.aaa_info.age_country_gender_reach_breakdown` | Array | Detailed breakdown by country, age, and gender |

***

### Key Notes

1. **Timestamps**: Convert Unix timestamps (e.g., `1750921200` → `2025-02-24T23:00:00Z`)
2. **Null Fields**: Indicate restricted data per Facebook's policies
3. **Media URLs**: May expire - download promptly
4. **Audience Data**: Includes detailed demographic breakdowns when available
5. **Compliance**: Contains regulatory information for different regions

Key fields include:

- Ad creative (text/images/video URLs)
- Sponsor page details and metrics
- Detailed audience targeting information
- Active dates and duration
- Compliance and regulation data
- Call-to-action configuration

***

### Why This Scraper?

- **Compliance**: Follows Facebook's rate limits and robots.txt
- **Stealth**: Residential proxies + random delays mimic human behavior
- **Depth**: Captures 30+ data points per ad including edits history
- **Future-Proof**: Handles Facebook's frequent UI/API changes

***

### Use Cases

- Track competitor ad strategies in real-time
- Pull every video ad an advertiser runs as text and compare their opening hooks side by side
- Analyze seasonal campaign patterns
- Archive political ads for transparency
- Monitor trademark violations

***

### Explore More Scrapers

Monitor every major ad platform with the full ads-transparency suite:

- [LinkedIn Ad Library Scraper — Ads, Impressions & Targeting](https://apify.com/memo23/linkedin-ads-scraper) — B2B competitor ads with impressions and targeting data
- [TikTok Ads Scraper](https://apify.com/memo23/tiktok-ads-scraper-cheerio) — TikTok creative center & ads
- [X (Twitter) Ads Transparency Scraper](https://apify.com/memo23/x-ads-transparency-scraper) — X's ad repository
- [Google Ads Transparency Scraper](https://apify.com/memo23/google-ad-transparency-scraper-cheerio) — Google's ads transparency center

If you found this Facebook Ads Library Scraper useful, check out the rest of my scrapers and actors at [memo23's Apify profile](https://apify.com/memo23).

### Free tier

You can run this scraper on a free Apify account before paying anything:

| | Free account |
|---|---|
| Results per run | up to **30** |
| Start URLs per run | up to **1** |
| Cost | nothing — the free plan includes monthly platform credit |

That is enough to check the field list and the data quality against your own URLs. Paid plans lift both caps; see [Apify pricing](https://apify.com/pricing).

### Support

- For issues or feature requests, please use the [Issues](https://console.apify.com/actors/3853UUZQG6pjjdw11/issues) section of this actor.
- If you need customization or have questions, feel free to contact the author:
  - Author's website: <https://muhamed-didovic.github.io/>
  - Email: <muhamed.didovic@gmail.com>

### Additional Services

- Request customization or whole dataset: <muhamed.didovic@gmail.com>
- If you need anything else scraped, or this actor customized, email: <muhamed.didovic@gmail.com>
- For API services of this scraper (no Apify fee, just usage fee for the API), contact: <muhamed.didovic@gmail.com>
- Email: muhamed.didovic@gmail.com

***

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/facebook-ads-library-scraper-ppe`).

**Purpose:** Scrapes Meta's Ad Library — from keyword search terms or from pasted Ad Library / Facebook Page URLs — returning full ad records (creative, copy, advertiser page, run dates, and EU reach/transparency) with the richest schema on the Store.

**Minimal input:**

```json
{
  "searchTerms": ["nike"],
  "searchCountries": ["US"],
  "maxItems": 25
}
```

URL variant: `{ "startUrls": ["https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&media_type=all&q=nike&search_type=keyword_unordered"], "maxItems": 25 }`

**Output:** one row per ad — FB AD Link, FB Link, Company Name, Website, ad_archive_id, page_id, page_name, is_active, start_date, end_date, total_active_time, publisher_platform, categories, collation_count, snapshot {body.text, title, cta_text, cta_type, link_url, link_description, display_format, images\[].original_image_url, videos, page_like_count, page_categories}, data_reach {aaa_info: targets_eu, eu_total_reach, age_country_gender_reach_breakdown}.

**Behaviors an agent should know:**

- Provide either `searchTerms` (with `searchCountries`, `adActiveStatus`, `mediaType`, `searchMode`) or `startUrls` — URLs support every Ad Library filter (dates, political/housing/employment/credit category, publisher platform, language). Each search term × country is queried separately.
- Always set `maxItems` (default 10000). `startUrls` accepts at most 5 URLs.
- `startDateMin` / `startDateMax` filter by ad start date (`YYYY-MM-DD`); `filterDuplicatePageIds: true` keeps one ad per page; `includeAdReach` / `includeTotalActiveAds` add extra output fields.
- Spend, reach_estimate, currency, and impressions are `null`/empty for standard commercial ads — populated mainly for regulated categories (`adType` political/housing/employment/credit) and EU-transparency ads.
- Media URLs (images/videos) are time-limited CDN links — download promptly.
- `watchlistId` + `newAdsOnly: true` make a repeated search deliver and bill only ads not delivered on an earlier run of that watchlist — the right setting for scheduled monitoring.
- `transcribeVideos: true` adds `transcript`, `transcriptLanguage` and `hookSpoken` (first 3 seconds of speech) to every video ad row; image ads get `transcriptError: "no-video"`. Costs about $0.05 per video ad on top of the ad price; pair it with `mediaType: "video"`.
- Pay-per-event: billed per ad emitted to the dataset (from $0.3 per 1,000 ads; reach included at no extra charge). See the Pricing tab on the actor page.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Meta Platforms, Inc. or any of its subsidiaries. All trademarks mentioned are the property of their respective owners.

The scraper accesses only publicly available Facebook Ad Library pages — no authenticated endpoints, paid features, or content behind the Facebook login wall. Users are responsible for ensuring their use complies with Meta's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organization.

***

### SEO Keywords

Facebook Ads Library scraper, scrape Facebook Ads Library, Meta Ad Library scraper, Facebook Ads API, Apify Facebook ads, ad creative scraper, competitor ads scraper, ad transparency data, eu ad reach data, advertiser page data, ad copy extraction, ad spend research, political ads scraper, ad monitoring scraper, marketing intelligence data, competitor ad analysis, ad campaign research, social media ad data, brand advertising intelligence, facebook ad transcript, video ad transcription, ad hook analysis, facebook video ads to text

# Actor input Schema

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

Paste full Meta Ad Library URLs (with any filters) or Facebook Page URLs. This is the most powerful mode — it supports every Ad Library filter (country, media type, status, keyword/phrase, date range, political/housing/employment categories, publisher platform, language). Example: <code>https://www.facebook.com/ads/library/?active_status=active\&ad_type=all\&country=US\&is_targeted_country=false\&media_type=all\&q=nike\&search_type=keyword_unordered</code> — or a page URL like <code>https://www.facebook.com/Nike</code>.

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

Scrape every ad from specific advertisers. Enter one or more Facebook <b>Page IDs</b> (the numeric id, e.g. <code>103582046420186</code>), or paste an Ad Library page URL that contains <code>view_all_page_id=…</code>. Each advertiser's ads are returned in full. Works on its own or alongside the URL and keyword inputs. Country follows the Countries filter below (or worldwide if unset).

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

Keywords or brand names to look up in the Meta Ad Library — e.g. <code>nike</code>, <code>weight loss</code>, <code>black friday</code>. Each term is searched separately. Leave empty if you're pasting URLs above. The fields below apply to these search terms.

## `searchCountries` (type: `array`):

Two-letter country codes (<code>US</code>, <code>GB</code>, <code>DE</code> …) or <code>ALL</code> for worldwide. Each country is queried separately per search term. Defaults to <code>US</code>.

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

Filter by whether ads are currently running.

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

Filter ads by creative media type.

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

Filter by ad category. <b>All ads</b> covers every commercial ad. The special categories (Political & issue, Housing, Employment, Credit) are the regulated transparency categories — these carry impression/reach data.

## `publisherPlatforms` (type: `array`):

Only return ads shown on these platforms. Valid values: <code>facebook</code>, <code>instagram</code>, <code>messenger</code>, <code>audience_network</code>. Leave empty for all platforms.

## `contentLanguages` (type: `array`):

Only return ads whose creative is in these languages. Use ISO 639-1 two-letter codes (<code>en</code>, <code>es</code>, <code>de</code> …). Leave empty for all languages.

## `searchMode` (type: `string`):

<b>Broad</b> matches any of the words; <b>Exact phrase</b> matches the words together (auto-wrapped in quotes).

## `sortBy` (type: `string`):

How to order the ads returned. <b>Default</b> keeps Facebook's own ranking. <b>Most impressions first</b> sorts by total impressions (works best for Facebook Page ad searches / EU-transparency ads; commercial ads without impression data may be unaffected).

## `startDateMin` (type: `string`):

Only include ads that started running on or after this date. Format <code>YYYY-MM-DD</code>. Leave empty to skip.

## `startDateMax` (type: `string`):

Only include ads that started running on or before this date. Format <code>YYYY-MM-DD</code>. Leave empty to skip.

## `includeAdReach` (type: `boolean`):

Include Ad Reach and EU transparency in output.

## `includeTotalActiveAds` (type: `boolean`):

Include total active ads count per page in output.

## `filterDuplicatePageIds` (type: `boolean`):

If enabled, only one ad per unique page_id will be kept (first occurrence). This removes duplicate ads from the same page.

## `categories` (type: `array`):

You can add one more categories to filter ads by their categories like: brand, etc. Write one category per input/line.

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

Maximum number of items that will be scraped per crawl. Example: `50`. Default `10000`. On a free Apify plan a run is capped at 30 ads and to ONE search target — the first start URL, search-term/country pair or advertiser the input builds, whichever comes first — so a free run with 2 terms x 2 countries returns only the first combination. Paying Apify accounts remove both caps.

## `minDelay` (type: `integer`):

Minimum delay to wait before calling the new items from API.

## `maxDelay` (type: `integer`):

Maximum delay to wait before calling the new items from API

## `maxConcurrency` (type: `integer`):

Maximum number of pages that can be processed at the same time.

## `minConcurrency` (type: `integer`):

Minimum number of pages that will be processed at the same time.

## `maxRequestRetries` (type: `integer`):

Number of times the crawler will retry a failed request before giving up.

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

Leave empty — the actor already routes all traffic through its own built-in residential proxy at no extra cost to you. Only set this if you want to use your own proxies.

## `watchlistId` (type: `string`):

Named key-value store that remembers which ads (by ad_archive_id) this run already delivered. Use the same id on a schedule (for example <code>nike-daily</code>) and later runs leave them out, so a monitored advertiser or keyword bills only the ads that are new. Letters, numbers, hyphens, underscores.

## `newAdsOnly` (type: `boolean`):

When enabled, ads already stored on the Watchlist ID are left out of the dataset and not charged. The search itself still runs in full. Requires <code>watchlistId</code>. Default: false.

## `transcribeVideos` (type: `boolean`):

When on, each ad that has a video is transcribed via the Video & Audio Transcriber and the row gains `transcript`, `transcriptLanguage` and `hookSpoken` (the first 3 seconds of speech). Image-only ads are saved as usual with no transcription charge. Off by default — existing scrape-only runs are unchanged. Billed by the transcriber at $0.05 per started minute of audio (up to 3 minutes per video, about $0.05 for a typical ad); failed videos are free. Pair with mediaType = video.

## `debug` (type: `boolean`):

If enabled, the actor will output detailed debug logs including proxy info, session details, response headers, and content structure analysis. Useful for troubleshooting.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&is_targeted_country=false&media_type=all&q=nike&search_type=keyword_unordered"
  ],
  "advertisers": [],
  "searchTerms": [],
  "searchCountries": [
    "US"
  ],
  "adActiveStatus": "active",
  "mediaType": "all",
  "adType": "all",
  "publisherPlatforms": [],
  "contentLanguages": [],
  "searchMode": "keyword_unordered",
  "sortBy": "",
  "startDateMin": "",
  "startDateMax": "",
  "includeAdReach": false,
  "includeTotalActiveAds": false,
  "filterDuplicatePageIds": false,
  "categories": [],
  "maxItems": 10000,
  "minDelay": 5,
  "maxDelay": 10,
  "maxConcurrency": 10,
  "minConcurrency": 1,
  "maxRequestRetries": 100,
  "newAdsOnly": false,
  "transcribeVideos": false,
  "debug": false
}
```

# 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 = {
    "startUrls": [
        "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&is_targeted_country=false&media_type=all&q=nike&search_type=keyword_unordered"
    ],
    "advertisers": [],
    "searchTerms": [],
    "searchCountries": [
        "US"
    ],
    "publisherPlatforms": [],
    "contentLanguages": [],
    "categories": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/facebook-ads-library-scraper-cheerio").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": ["https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&is_targeted_country=false&media_type=all&q=nike&search_type=keyword_unordered"],
    "advertisers": [],
    "searchTerms": [],
    "searchCountries": ["US"],
    "publisherPlatforms": [],
    "contentLanguages": [],
    "categories": [],
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/facebook-ads-library-scraper-cheerio").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": [
    "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&is_targeted_country=false&media_type=all&q=nike&search_type=keyword_unordered"
  ],
  "advertisers": [],
  "searchTerms": [],
  "searchCountries": [
    "US"
  ],
  "publisherPlatforms": [],
  "contentLanguages": [],
  "categories": []
}' |
apify call memo23/facebook-ads-library-scraper-cheerio --silent --output-dataset

```

## MCP server setup

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

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/3853UUZQG6pjjdw11/builds/vkPEe9qOD5QW0y73Y/openapi.json
