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

Extract ads from the Meta Ad Library by search URL or Facebook  Page URL: creatives, copy, CTAs, platforms, run dates and optional EU transparency details. $0.20 per 1,000 ads, first 30 ads of every URL free.

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

## Pricing

from $0.20 / 1,000 dataset items

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

## Facebook Ads Library Scraper

The Facebook Ads Library Scraper extracts ads from the Meta Ad Library by search URL or Facebook Page URL, with each ad's creative, copy, platforms and run dates. Ads cost **$0.0002 each, or $0.20 per 1,000 ads**, with the **first 30 ads of every URL free**.

The Facebook Ads Library Scraper accepts 2 source URL types, reads 12 Ad Library URL parameters, exposes 15 input fields, adds 9 derived fields to every ad, adds 7 more with ad details, and bills through 3 charge events. Every price below is a per-event charge on a paid Apify plan, with no subscription to this actor and no monthly minimum.

### What Does the Facebook Ads Library Scraper Extract?

The Facebook Ads Library Scraper extracts every ad that an Ad Library search or a Facebook Page returns, in 2 layers: the ad as the Facebook Ad Library returns it, plus an optional ad details layer.

The table below lists the 4 data groups the Facebook Ads Library Scraper returns and where each group lives in the output.

| Data group | Output location | Included |
|---|---|---|
| Ad and advertiser identity | `ad_archive_id`, `page_id`, `page_name`, `is_active`, `publisher_platform`, `start_date`, `end_date` | Every ad |
| Creative and copy | `snapshot.title`, `snapshot.body.text`, `snapshot.cta_text`, `snapshot.link_url`, `snapshot.display_format`, `snapshot.images`, `snapshot.videos`, `snapshot.cards` | Every ad |
| Derived fields | `url`, `total`, `position`, `ads_count`, `start_date_formatted`, `end_date_formatted`, `ad_library_url`, `runTag`, `inputSource` | Every ad |
| Ad details | `advertiser`, `aaa_info`, `violation_types`, `verified_voice_context`, `transparency_by_location`, `is_violating_eu_siep`, `is_siep_advertiser_eligible_for_ai_disclosure` | `isDetailsPerAd` set to `true` |

The ad details layer carries EU and UK transparency data (reach by country, age and gender), advertiser page info, page spend and payer and beneficiary data for each ad that has details in the Facebook Ad Library.

### Which URLs Does the Facebook Ads Library Scraper Accept?

The Facebook Ads Library Scraper accepts 2 URL types in `startUrls`: Ad Library search URLs and Facebook Page URLs. The 4 filter inputs, `activeStatus`, `sorting`, `onlyAdsNewerThan` and `onlyAdsOlderThan`, apply to both types, and a filter set in a search URL takes priority.

- **Ad Library search URLs**, such as `https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=US&q=nike&search_type=keyword_unordered&media_type=all`, run the search exactly as the URL describes it. Each search URL needs a keyword (`q`) or an advertiser page (`view_all_page_id`).
- **Facebook Page URLs**, in the 3 forms `https://www.facebook.com/nike`, `https://www.facebook.com/profile.php?id=...` and `https://www.facebook.com/p/Name-123...`, return the ads that Page has run. The Facebook Ads Library Scraper resolves the Page to its page ID, then searches that page's ads.

The Facebook Ads Library Scraper reads 12 parameters from an Ad Library search URL.

| URL parameter | What it filters |
|---|---|
| `q` | Search keyword |
| `search_type=keyword_exact_phrase` | Exact phrase search |
| `view_all_page_id` | One advertiser's ads |
| `country` | Country, or all countries when the parameter is absent |
| `ad_type` | Ad category |
| `active_status` | Active status |
| `media_type` | Media type |
| `publisher_platforms[n]` | Platforms |
| `content_languages[n]` | Languages |
| `sort_data[mode]` | Sorting: `total_impressions` or `relevancy_monthly_grouped` |
| `start_date[min]` | Earliest start date |
| `start_date[max]` | Latest start date |

### Who Uses the Facebook Ads Library Scraper?

Listed below are the 5 roles that run the Facebook Ads Library Scraper against Meta Ad Library data.

- **Marketing agencies and media buyers** track competitor creatives and landing pages through `snapshot.title`, `snapshot.body.text`, `snapshot.cta_text` and `snapshot.link_url`, and measure how long ads stay live from `start_date_formatted` and `end_date_formatted`.
- **Brands and e-commerce teams** monitor competitor promotions per Facebook Page with `onlyAdsNewerThan` and `activeStatus`, and read `publisher_platform` to see which of `FACEBOOK`, `INSTAGRAM`, `MESSENGER`, `THREADS` and `AUDIENCE_NETWORK` carry each ad.
- **Market research firms** analyze advertising by keyword, country, language and media type through search URL filters, and size each search with `total`.
- **Political and policy researchers** study political and issue ads through `spend`, `impressions_with_index`, `aaa_info.payer_beneficiary_data` and `advertiser.ad_library_page_info.page_spend`.
- **Compliance and brand protection teams** find ads that use a brand name through `q`, and review `transparency_by_location.eu_transparency` for reach by country, age and gender.

Each of these roles pays per ad saved, so run volume sets the bill.

### What Does the Facebook Ads Library Scraper Cost?

The Facebook Ads Library Scraper charges 3 event types: **$0.006 per input URL**, **$0.0002 per ad** and **$0.0004 per ad with details**. The first page of every input URL, **30 ads**, is free of `dataset-item` charges. This rate card applies to paid Apify plans. Free plan pricing differs and is listed on the [Facebook Ads Library Scraper pricing tab](https://apify.com/apidojo/facebook-ads-scraper/pricing?fpr=yhdrb).

| Event | Price | When it is charged |
|---|---|---|
| `get-ad` | $0.006 per input URL | Once for every input URL when the run starts, whatever the number of ads. URLs rejected by the input rules below, before any request is sent, are not charged. |
| `dataset-item` | $0.0002 per ad | Once for every ad saved to the dataset. The first page of each URL, **30 ads**, is free. |
| `get-ad-detail` | $0.0004 per ad | Only when `isDetailsPerAd` is `true` and Facebook returned details for that ad. Ads without details are not charged. |

Free plan users run the Facebook Ads Library Scraper in Demo Mode, with each run capped at **10 items**. [Apify paid plans](https://apify.com/pricing?fpr=yhdrb) remove the Demo Mode cap.

#### Facebook Ads Library Scraper cost examples

The table below prices 5 scenarios against the rate card above. Each scenario deducts the 30 free ads of every input URL's first page from the `dataset-item` count.

| Scenario | Charge events | Calculation | Total cost |
|---|---|---|---|
| 100 ads, 1 search URL | 1 `get-ad`, 70 `dataset-item` | $0.006 + 70 × $0.0002 | **$0.020** |
| 1,000 ads, 1 search URL | 1 `get-ad`, 970 `dataset-item` | $0.006 + 970 × $0.0002 | **$0.200** |
| 1,000 ads, 1 search URL, with details | 1 `get-ad`, 970 `dataset-item`, 1,000 `get-ad-detail` | $0.006 + 970 × $0.0002 + 1,000 × $0.0004 | **$0.600** |
| 2,000 ads, 10 Page URLs × 200 ads | 10 `get-ad`, 1,700 `dataset-item` | 10 × $0.006 + 1,700 × $0.0002 | **$0.400** |
| 10,000 ads, 1 search URL | 1 `get-ad`, 9,970 `dataset-item` | $0.006 + 9,970 × $0.0002 | **$2.000** |

#### Facebook Ads Library Scraper unit economics

The 7 figures below are derived from the rate card above. Per-ad figures include the `dataset-item` charge and exclude the $0.006 charge per input URL and the 30 free ads per URL.

| Metric | Value |
|---|---|
| Free ads per input URL | 30 |
| Cost per ad | $0.0002 |
| Cost per ad with details | $0.0006 |
| Cost per 1,000 ads | $0.20 |
| Cost per 1,000 ads with details | $0.60 |
| Ads per $1 | 5,000 |
| Ads with details per $1 | 1,666 |

### How Is the Facebook Ads Library Scraper Configured?

The Facebook Ads Library Scraper exposes 15 input fields covering sources, volume limits, ad details, filters, output shaping and delivery. `startUrls` is the only required field. Defaults are listed where the input schema sets one.

| Field | Type | Description | Default |
|---|---|---|---|
| `startUrls` | array | Facebook Ad Library search URLs or Facebook Page URLs to scrape ads from. Required. | not set |
| `resultsLimit` | integer | Maximum number of ads per input URL. Empty scrapes all available ads. | not set |
| `maxItems` | integer | Maximum number of items in the output across all input URLs. The run finishes once it is reached. | not set |
| `onlyTotal` | boolean | Saves 1 item per input URL with Facebook's total ad count, instead of the ads. Each item is charged as 1 `dataset-item`. | `false` |
| `isDetailsPerAd` | boolean | Adds each ad's details, such as EU reach and advertiser info. Charged as `get-ad-detail` for every ad that has details. | `false` |
| `customMapFunction` | string | JavaScript function that reshapes every item before it is saved. | not set |
| `runTag` | string | Value added to the `runTag` field of every item. | not set |
| `activeStatus` | string | `""` (active and inactive), `active` or `inactive`. | `""` |
| `sorting` | string | `""` (default order), `total_impressions` or `relevancy_monthly_grouped` (most recent). | `""` |
| `onlyAdsNewerThan` | string | Ads that ran on or after this date, as `YYYY-MM-DD` or a relative value such as `3 months`. | not set |
| `onlyAdsOlderThan` | string | Ads that ran on or before this date, as `YYYY-MM-DD` or a relative value such as `1 week`. | not set |
| `outputConnector` | string | MCP connector that receives the results. | not set |
| `deliverTarget` | string | URL or ID the connector writes to. | not set |
| `deliverMode` | string | `perPost` (one record per item) or `summary` (one roll-up per run). | `perPost` |
| `deliverMaxItems` | integer | Cap on records `perPost` delivers. Ignored in `summary`. | not set |

The `onlyAdsNewerThan` and `onlyAdsOlderThan` range returns the ads that ran during the range, including ads that started before it.

#### Facebook Ads Library Scraper custom map function

The Facebook Ads Library Scraper runs `customMapFunction` once per item, and its return value replaces that item in the output.

```js
(object) => { return { ...object } }
```

The Facebook Ads Library Scraper applies 5 rules to `customMapFunction`.

- **One synchronous function.** `async` functions, generator functions and functions returning a `Promise` are rejected.
- **Object return value.** A return of `null`, a string or a number is rejected.
- **No filtering.** Items returned as `null`, `{}` or `undefined` are saved as `{ noResults: true }`, and filtering through this function is treated as abuse of pay-per-result pricing, which results in a ban. Search URL filters and the filter inputs limit results instead.
- **No date logic.** Source code containing `new Date`, `1970` or `Date.now` is blocked. The search URL date range, `onlyAdsNewerThan` and `onlyAdsOlderThan` handle date filtering.
- **Safe fallback.** A function that fails to parse, throws or returns an invalid value is rejected for the rest of the run, a warning is logged, and the original items are saved. The run continues.

Not every item has every field, so optional chaining such as `object.snapshot?.title` keeps the function from throwing.

### Which Input Rules Does the Facebook Ads Library Scraper Enforce?

The Facebook Ads Library Scraper enforces 12 input rules covering required fields, URL validation, filters, limits and legacy fields.

| Rule | Behavior |
|---|---|
| `startUrls` contains at least 1 URL | The run fails when `startUrls` is empty. |
| `startUrls` entries are `{ "url": "..." }` objects or plain strings | Both forms are accepted. |
| The URL hostname contains `facebook` | Other hosts are logged as `Invalid URL` and skipped. |
| A search URL contains `q` or `view_all_page_id` | Other Ad Library URLs, such as single-ad `?id=` links, are logged and skipped. |
| A Page URL matches one of the 3 Page URL forms | Other URLs are logged as `Unsupported URL` and skipped. |
| `onlyAdsNewerThan` and `onlyAdsOlderThan` are `YYYY-MM-DD` or a number of days, weeks, months or years | Relative values count back from today in UTC. Any other value, or a start date later than the end date, fails the run. |
| A search URL's own filters take priority | `activeStatus`, `sorting` and the date range apply only where the search URL does not set them. |
| `resultsLimit`, `maxItems` and `deliverMaxItems` are integers of `1` or more | Empty fields apply no limit. |
| `resultsLimit` caps each URL, `maxItems` caps the run | The Facebook Ads Library Scraper stops paging a URL at its limit and stops the run at the run limit. |
| `onlyTotal` and `isDetailsPerAd` both set | `onlyTotal` wins and a warning is logged. |
| The legacy fields `urls`, `limitPerSource`, `scrapeAdDetails`, `count` and `scrapePageAds.*` are set over the API | They keep their previous behavior. When a run also sets the field that replaces one, the replacement wins, and `urls` is merged with `startUrls`. |
| `includeAboutPage` or `enrichWithEcommerceData` is set | Not supported. A warning is logged and the run continues. |

The Facebook Ad Library counts at most 50,001 results for a search, so `total` never exceeds `50001`.

### How Do You Search the Ad Library with the Facebook Ads Library Scraper?

The Facebook Ads Library Scraper runs a search URL copied from the Ad Library website with every filter in it. The example below returns up to 100 Nike ads in the US.

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

### How Do You Scrape a Facebook Page's Ads with the Facebook Ads Library Scraper?

The Facebook Ads Library Scraper filters Page URL ads with the 4 filter inputs. The example below returns up to 200 active ads that the Nike Page ran in the last 30 days, newest first.

```json
{
    "startUrls": [
        { "url": "https://www.facebook.com/nike" }
    ],
    "activeStatus": "active",
    "sorting": "relevancy_monthly_grouped",
    "onlyAdsNewerThan": "30 days",
    "resultsLimit": 200
}
```

### How Do You Count an Advertiser's Ads with the Facebook Ads Library Scraper?

The Facebook Ads Library Scraper saves only Facebook's ad count when `onlyTotal` is `true`, with 1 item per input URL and no ads. The example below counts the ads of the Nike Page.

```json
{
    "startUrls": [
        { "url": "https://www.facebook.com/nike" }
    ],
    "onlyTotal": true
}
```

The run saves the item below. `page_id` is `null` for keyword searches, and `total` is `null` when Facebook returns no count.

```json
{
    "url": "https://www.facebook.com/nike",
    "page_id": "15087023444",
    "total": 14443,
    "inputSource": "https://www.facebook.com/nike"
}
```

### How Do You Get EU Reach with the Facebook Ads Library Scraper?

The Facebook Ads Library Scraper adds EU reach and advertiser details when `isDetailsPerAd` is `true`. The example below returns 50 active French video ads with details and tags every item `france-video-ads`.

```json
{
    "startUrls": [
        { "url": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=FR&q=nike&search_type=keyword_unordered&media_type=video" }
    ],
    "isDetailsPerAd": true,
    "maxItems": 50,
    "runTag": "france-video-ads"
}
```

### How Do You Send Facebook Ads Library Scraper Results to Notion?

The Facebook Ads Library Scraper delivers results through [Apify MCP Connectors](https://apify.com/store/collections/mcp-connectors?fpr=yhdrb) to Notion, Todoist or any service whose MCP server offers non-destructive write tools. The 4 steps below set up delivery.

1. Authorize a connector once in [Console, Settings, API & Integrations, MCP connectors](https://console.apify.com/settings/integrations).
2. Select the connector in `outputConnector`.
3. Paste the destination URL or ID in `deliverTarget`, such as `https://www.notion.so/My-Posts-24f8a1b2c3d44e5f8a9b0c1d2e3f4a5b`, after sharing the Notion page with the connector through the Notion **Connections** menu.
4. Set `deliverMode` to `perPost` or `summary`, and set `deliverMaxItems` to cap `perPost` records.

A Notion **page** target produces a database with one typed column per field (URL, number, date, text). An existing database fills the columns it already has, matched by name, and an empty database receives only the title, with the rest of each record in the row body. Nested fields are flattened into columns, so `snapshot` becomes `snapshot.title` and `snapshot.link_url`, and `publisher_platform` becomes a comma-joined column. The full record is kept in each row's page body.

The Facebook Ads Library Scraper writes through the connector the user authorized and uses only non-destructive write tools, so it cannot delete or overwrite existing data. Delivery runs after scraping. A failed delivery is logged as `C201` and the run finishes with all results in the dataset. Delivery stops early when the run timeout is near, and the log reports how many records were delivered.

### Which Fields Does the Facebook Ads Library Scraper Output?

The Facebook Ads Library Scraper outputs each ad as the Facebook Ad Library returns it, adds 9 derived fields, and adds 7 ad details fields when `isDetailsPerAd` is `true`.

The table below lists the 9 derived fields.

| Field | Description |
|---|---|
| `url` | The input URL the ad was found with. |
| `total` | Facebook's result count for the input URL, capped by Facebook at 50,001. |
| `position` | The ad's position in the input URL's results, starting from 1. |
| `ads_count` | Number of ads that use this ad's creative. |
| `start_date_formatted` | `start_date` in UTC, as `YYYY-MM-DD HH:mm:ss`. |
| `end_date_formatted` | `end_date` in UTC, as `YYYY-MM-DD HH:mm:ss`. |
| `ad_library_url` | Link to the ad in the Facebook Ad Library. |
| `runTag` | The `runTag` input, present when it is set. |
| `inputSource` | The input URL the ad came from. |

The table below lists the 7 ad details fields.

| Field | Contents |
|---|---|
| `advertiser` | Advertiser page info and `ad_library_page_info`, including `page_spend` and `page_info` with Instagram followers, likes and verification. |
| `aaa_info` | `payer_beneficiary_data`, `targets_eu` and takedown status. |
| `violation_types` | Violation types listed for the ad. |
| `verified_voice_context` | Verified voice context, or `null`. |
| `transparency_by_location` | `eu_transparency`, `uk_transparency` and `br_transparency`, with location, age and gender audience and `eu_total_reach`. |
| `is_violating_eu_siep` | Boolean EU SIEP violation flag. |
| `is_siep_advertiser_eligible_for_ai_disclosure` | Boolean AI disclosure eligibility flag. |

#### Facebook Ads Library Scraper output example

The example below is 1 French video ad scraped with `isDetailsPerAd` set to `true`, shortened in the media URLs.

```json
{
    "ad_archive_id": "1936432480352751",
    "ad_id": null,
    "collation_count": null,
    "collation_id": null,
    "is_active": true,
    "page_id": "739783715881879",
    "page_is_deleted": false,
    "page_name": "BagIt",
    "has_user_reported": false,
    "report_count": null,
    "menu_items": [],
    "state_media_run_label": null,
    "impressions_with_index": {
        "impressions_text": null,
        "impressions_index": -1
    },
    "gated_type": "ELIGIBLE",
    "categories": [
        "UNKNOWN"
    ],
    "is_aaa_eligible": true,
    "contains_digital_created_media": false,
    "contains_sensitive_content": false,
    "reach_estimate": null,
    "currency": "",
    "spend": null,
    "start_date": 1783580400,
    "end_date": 1791961200,
    "publisher_platform": [
        "FACEBOOK",
        "INSTAGRAM",
        "AUDIENCE_NETWORK",
        "MESSENGER",
        "THREADS"
    ],
    "total_active_time": null,
    "regional_regulation_data": {
        "finserv": {
            "is_deemed_finserv": false,
            "is_limited_delivery": false
        },
        "tw_anti_scam": {
            "is_limited_delivery": false
        }
    },
    "hide_data_status": "NONE",
    "fev_info": null,
    "targeted_or_reached_countries": [],
    "snapshot": {
        "page_id": "739783715881879",
        "page_is_deleted": false,
        "page_name": "BagIt",
        "page_profile_uri": "https://www.facebook.com/61578816363458/",
        "page_profile_picture_url": "https://scontent-lga3-2.xx.fbcdn.net/.../742831932_1550070166817658_6198977506474300428_n.jpg",
        "page_like_count": 778,
        "page_categories": [
            "Home care"
        ],
        "byline": null,
        "title": "Des baskets propres. Sans effort.",
        "body": {
            "text": "Pourquoi tes baskets blanches ne sont jamais vraiment propres ?\n\nVoici ce que personne ne te dit :\n\n❌ Le frottage manuel crée des micro-déchirures.\n❌ Les sprays décolorent le tissu.\n❌ La machine sans protection déforme tes chaussures.\n\nBagIt résout tout ça :\n\n✅ Protège le revêtement blanc\n✅ Nettoie en profondeur (herbe, boue, graisse)\n✅ Marche sur On Cloud, Veja, Nike, Converse...\n✅ 187,000+ Français l'utilisent\n✅ Ta machine est aussi protégée\n\n30 minutes dans la machine. Blanches comme neuves. Même après 50 lavages.\n\n✔️ Essaye-le sans risque → garantie 60 jours satisfait ou remboursé."
        },
        "caption": "shopbagit.com",
        "cta_text": "Shop now",
        "cta_type": "SHOP_NOW",
        "link_url": "https://www.shopbagit.com/products/sac-de-lavage-bagit",
        "link_description": "Promotion -60% • Garantie 60 jours",
        "display_format": "VIDEO",
        "images": [],
        "videos": [
            {
                "video_hd_url": "https://video-lga3-3.xx.fbcdn.net/.../AQMXemPAFj5mTFSwCL7XjtULBeegjap0JbQUa0eRMqYxPuCUeK7VQjDPr8Z2wGfymae6UYctmfGDaLOuhewe64F9Y5YuY6adS0gw0N2TKifuQA.mp4",
                "video_preview_image_url": "https://scontent-lga3-2.xx.fbcdn.net/.../742437254_942574228862741_94592501591813558_n.jpg",
                "video_sd_url": "https://video-lga3-3.xx.fbcdn.net/.../AQPi0uubmDXa9rzNNBvYgjWSCy9OE0eE3TTYB1uEIbCRQeGDZeRvosxqJKsWJHpLxoaLi1RMx8vE5helJg1RTokkcC9q30j5xslGmiUuWg.mp4",
                "watermarked_video_hd_url": null,
                "watermarked_video_sd_url": null
            }
        ],
        "cards": [],
        "branded_content": null,
        "root_reshared_post": null,
        "disclaimer_label": null,
        "event": null,
        "is_reshared": false,
        "extra_links": [],
        "extra_texts": [],
        "extra_images": [],
        "extra_videos": [],
        "country_iso_code": null,
        "brazil_tax_id": null,
        "additional_info": null,
        "ec_certificates": []
    },
    "url": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=FR&q=nike&search_type=keyword_unordered&media_type=video",
    "total": 875,
    "position": 2,
    "ads_count": 1,
    "start_date_formatted": "2026-07-09 07:00:00",
    "end_date_formatted": "2026-10-14 07:00:00",
    "ad_library_url": "https://www.facebook.com/ads/library/?id=1936432480352751",
    "advertiser": {
        "page": {
            "about": {
                "text": ""
            },
            "is_delegate_page_with_linked_primary_profile": false,
            "id": "739783715881879"
        },
        "ad_library_page_info": {
            "page_spend": {
                "current_week": null,
                "lifetime_by_disclaimer": [],
                "weekly_by_disclaimer": [],
                "is_political_page": false
            },
            "page_info": {
                "entity_type": "PERSON_PROFILE",
                "ig_followers": 2252,
                "ig_username": "sacbagit",
                "ig_verification": false,
                "likes": 778,
                "page_alias": "",
                "page_category": "Cleaning Service",
                "page_cover_photo": "https://scontent-lga3-2.xx.fbcdn.net/.../611010143_122143091456960545_6902738384277385121_n.png",
                "page_id": "739783715881879",
                "page_is_deleted": false,
                "page_is_restricted": false,
                "page_name": "BagIt",
                "page_profile_uri": "https://facebook.com/61578816363458",
                "page_verification": "NOT_VERIFIED",
                "profile_photo": "https://scontent-lga3-2.xx.fbcdn.net/.../581460377_122133267434960545_679457567548373465_n.jpg",
                "is_profile_page": false
            }
        }
    },
    "aaa_info": {
        "payer_beneficiary_data": [
            {
                "payer": "BagIt",
                "beneficiary": "BagIt"
            }
        ],
        "targets_eu": true,
        "has_violating_payer_beneficiary": false,
        "is_ad_taken_down": false
    },
    "violation_types": [],
    "verified_voice_context": null,
    "transparency_by_location": {
        "br_transparency": null,
        "eu_transparency": {
            "targets_eu": true,
            "location_audience": [
                {
                    "name": "Belgium",
                    "num_obfuscated": 0,
                    "type": "countries",
                    "excluded": false
                },
                {
                    "name": "Luxembourg",
                    "num_obfuscated": 0,
                    "type": "countries",
                    "excluded": false
                },
                {
                    "name": "France",
                    "num_obfuscated": 0,
                    "type": "countries",
                    "excluded": false
                }
            ],
            "gender_audience": "All",
            "age_audience": {
                "min": 18,
                "max": 65
            },
            "eu_total_reach": 1487024,
            "age_country_gender_reach_breakdown": [
                {
                    "country": "BE",
                    "age_gender_breakdowns": [
                        {
                            "age_range": "18-24",
                            "male": 1749,
                            "female": 850,
                            "unknown": 35
                        },
                        {
                            "age_range": "55-64",
                            "male": 16776,
                            "female": 24428,
                            "unknown": 1028
                        }
                    ]
                }
            ]
        },
        "uk_transparency": null
    },
    "is_siep_advertiser_eligible_for_ai_disclosure": true,
    "is_violating_eu_siep": false,
    "inputSource": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=FR&q=nike&search_type=keyword_unordered&media_type=video"
}
```

### Which Errors Does the Facebook Ads Library Scraper Report?

The Facebook Ads Library Scraper reports 9 error codes. **6 of the 9 codes are non-terminal**, and the run continues with the other URLs. 3 codes terminate the run: `C098`, `C099` and `C100`.

The 6 non-terminal codes split into 3 recovery behaviors:

- **4 codes** (`C001`, `C002`, `C003`, `C004`) stop one URL and leave the other URLs running.
- **1 code** (`C019`) saves an error item, waits 1 second and retries the request.
- **1 code** (`C201`) skips the MCP connector delivery and keeps every result in the dataset.

| Code | Cause | Behavior |
|---|---|---|
| `C001` | A search or Page lookup request failed. | Stops that URL. |
| `C002` | A search returned an unexpected response. | Stops that URL at that page. |
| `C003` | A Page lookup returned an unexpected response. | Skips that Page URL. |
| `C004` | No Facebook Page exists for the URL. | Skips that Page URL. |
| `C019` | Rate limit exceeded for the account. | Saves an item with `noResults`, `error`, `code` and `message`, then retries after 1 second. |
| `C201` | MCP connector delivery failed. | Logs a warning. The run finishes with all results. |
| `C098` | Unhandled rejection. | Ends the run. |
| `C099` | Uncaught exception. | Ends the run. |
| `C100` | Run initialization failed. | Ends the run. |

The 3 terminal codes ask for the run ID to be sent to [apidojo support](https://apify.com/apidojo?fpr=yhdrb).

### Which Facebook Ads Library Scraper Questions Come Up Most Often?

#### Does the Facebook Ads Library Scraper need a Facebook account?

No. The Facebook Ads Library Scraper input has no login, cookie or proxy field, and takes only the URLs and options listed above.

#### Does the Facebook Ads Library Scraper scrape Instagram ads?

Yes. The Facebook Ads Library Scraper returns every platform an ad ran on in `publisher_platform`, including `INSTAGRAM`, and a search URL with `publisher_platforms[n]` limits the search to chosen platforms.

#### Can the Facebook Ads Library Scraper scrape all ads from one advertiser?

Yes. The Facebook Ads Library Scraper returns one advertiser's ads from a Facebook Page URL or from a search URL with `view_all_page_id`.

#### How many ads does the Facebook Ads Library Scraper return per search?

The Facebook Ads Library Scraper pages through each URL until the results end or `resultsLimit` or `maxItems` is reached. Facebook counts at most 50,001 results per search in `total`.

#### How does the Facebook Ads Library Scraper filter ads by date?

The Facebook Ads Library Scraper filters ads by `onlyAdsNewerThan` and `onlyAdsOlderThan`, and a search URL's `start_date[min]` and `start_date[max]` take priority over them. Date logic inside `customMapFunction` is blocked.

#### Does the Facebook Ads Library Scraper return EU reach data?

Yes. The Facebook Ads Library Scraper returns `transparency_by_location.eu_transparency`, with `eu_total_reach` and an age, gender and country breakdown, when `isDetailsPerAd` is `true`.

#### Are the first 30 ads free in the Facebook Ads Library Scraper?

Yes. The Facebook Ads Library Scraper does not charge `dataset-item` for the first page of every input URL, which is 30 ads, so 10 URLs return 300 ads for the 10 × $0.006 URL charges alone, $0.06 on a paid plan. `get-ad-detail` still applies to those ads when `isDetailsPerAd` is `true`.

#### Does the Facebook Ads Library Scraper charge for ads without details?

No. The Facebook Ads Library Scraper charges `get-ad-detail` only for ads that came back with details. Ads without details are saved without them and charged as `dataset-item` only.

#### Does the Facebook Ads Library Scraper return ad videos and images?

Yes. The Facebook Ads Library Scraper returns `snapshot.videos` with `video_hd_url`, `video_sd_url` and `video_preview_image_url`, plus `snapshot.images` and `snapshot.cards`.

#### What does the Facebook Ads Library Scraper do with an invalid URL?

The Facebook Ads Library Scraper logs the invalid URL and skips it, and the other URLs keep running. A Page that does not exist is reported as `C004`.

#### Which countries does the Facebook Ads Library Scraper support?

The Facebook Ads Library Scraper uses the `country` parameter of a search URL, or all countries when the URL has none. Page URLs search all countries, unless an API input sets a 2-letter code in `scrapePageAds.countryCode`.

#### Why does the Facebook Ads Library Scraper return fewer ads than expected?

The Facebook Ads Library Scraper stops at `maxItems`, `resultsLimit` or the run's maximum charge. Empty limit fields return every ad the URL has, and the Ad Library website shows whether the URL has ads at all.

#### Does the Facebook Ads Library Scraper work on the Apify free plan?

Yes. The Facebook Ads Library Scraper runs in Demo Mode on the free plan, with each run capped at 10 items.

#### Can the Facebook Ads Library Scraper send ads to Notion?

Yes. The Facebook Ads Library Scraper writes results to Notion, Todoist and other MCP connectors through `outputConnector`, `deliverTarget` and `deliverMode`.

### Where Do You Get Facebook Ads Library Scraper Support?

The Facebook Ads Library Scraper is published by apidojo, with support at <apidojo10@gmail.com>. The run log names each skipped URL and the reason.

apidojo also publishes these 7 actors:

- [Twitter Scraper Lite](https://apify.com/apidojo/twitter-scraper-lite?fpr=yhdrb)
- [Instagram Scraper](https://apify.com/apidojo/instagram-scraper?fpr=yhdrb)
- [TikTok Scraper](https://apify.com/apidojo/tiktok-scraper?fpr=yhdrb)
- [Youtube Scraper](https://apify.com/apidojo/youtube-scraper?fpr=yhdrb)
- [Tweet Scraper](https://apify.com/apidojo/tweet-scraper?fpr=yhdrb)
- [Instagram Comments Scraper](https://apify.com/apidojo/instagram-comments-scraper?fpr=yhdrb)
- [TikTok User Scraper](https://apify.com/apidojo/tiktok-user-scraper?fpr=yhdrb)

# Actor input Schema

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

List of Facebook Ad Library search URLs or Facebook Page URLs to scrape ads from.

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

Limit number of ads scraped per input URL. Leave it blank to scrape all available ads.

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

Maximum number of items in the output across all input URLs. The run finishes once it is reached.

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

Save one item per input URL with Facebook's total ad count, instead of the ads.

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

Enable this option to scrape ad details such as EU Reach info.

## `customMapFunction` (type: `string`):

Function that takes each of the objects as argument and returns data that will be mapped by the function itself. This function is not intended for filtering, please don't use it for filtering purposes or you will get banned automatically. Must be a single function, e.g. (object) => { return {...object} }. Leave empty if you do not need to transform the output - an unusable function is ignored and the original items are saved instead.

## `runTag` (type: `string`):

Add this value to the 'runTag' column in the output.

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

Filter ads by whether they are currently active.

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

Sort by impressions or date.

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

Only scrape ads that ran on or after this date. Use an absolute date (YYYY-MM-DD) or a relative value, such as 1 day, 2 months or 3 years.

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

Only scrape ads that ran on or before this date. Use an absolute date (YYYY-MM-DD) or a relative value, such as 1 day, 2 months or 3 years.

## `outputConnector` (type: `string`):

Pick an MCP connector you authorized in Console → Settings → API & Integrations → MCP connectors (e.g. Notion, Todoist — any service whose MCP server offers non-destructive write tools works best-effort). The scraped results are written through your own connector — the Actor never sees your third-party credentials. Only non-destructive write tools are used; it can never delete or overwrite your existing data.

## `deliverTarget` (type: `string`):

Where to write into the connector — paste the URL or ID. Notion example: https://www.notion.so/My-Posts-24f8a1b2c3d44e5f8a9b0c1d2e3f4a5b. Leave empty for services that need no target (e.g. Todoist Inbox, Slack). For Notion, share the page or database with your connector first (Notion's ••• → Connections menu), and point at a PAGE for structured columns (the Actor builds the database for you) — an empty database receives only the title (the rest of each record goes to the row's body).

## `deliverMode` (type: `string`):

perPost writes one record per scraped item; summary writes a single roll-up for the run.

## `deliverMaxItems` (type: `integer`):

Cap on how many records perPost mode writes to the connector; ignored in summary mode. Leave empty to deliver everything scraped.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=US&q=nike&search_type=keyword_unordered&media_type=all"
    }
  ],
  "resultsLimit": 10,
  "maxItems": 1000,
  "onlyTotal": false,
  "isDetailsPerAd": false,
  "customMapFunction": "(object) => { return {...object} }",
  "runTag": "nike-us-ads",
  "activeStatus": "active",
  "sorting": "total_impressions",
  "onlyAdsNewerThan": "2024-01-01",
  "onlyAdsOlderThan": "2024-12-31",
  "deliverTarget": "https://www.notion.so/My-Posts-24f8a1b2c3d44e5f8a9b0c1d2e3f4a5b",
  "deliverMode": "perPost",
  "deliverMaxItems": 100
}
```

# Actor output Schema

## `overview` (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": [
        {
            "url": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=US&q=nike&search_type=keyword_unordered&media_type=all"
        }
    ],
    "maxItems": 1000,
    "customMapFunction": (object) => { return {...object} }
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=US&q=nike&search_type=keyword_unordered&media_type=all" }],
    "maxItems": 1000,
    "customMapFunction": "(object) => { return {...object} }",
}

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

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

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

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=US&q=nike&search_type=keyword_unordered&media_type=all"
    }
  ],
  "maxItems": 1000,
  "customMapFunction": "(object) => { return {...object} }"
}' |
apify call apidojo/facebook-ads-scraper --silent --output-dataset

```

## MCP server setup

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