# Facebook Ads Library Scraper: Meta Ad Library API (`meridianlabs/facebook-ads-library-scraper`) Actor

Scrape Meta's Facebook Ad Library by keyword or advertiser in any country: ad text, images, videos, start date, days running, platforms, EU reach and age/gender data, plus a plain-English summary per advertiser. $0.50 per 1,000 ads.

- **URL**: https://apify.com/meridianlabs/facebook-ads-library-scraper.md
- **Developed by:** [Meridian Labs](https://apify.com/meridianlabs) (community)
- **Categories:** Social media, Marketing, Developer tools
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.50 / 1,000 ads

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** shows you **every ad a competitor is running on Facebook and Instagram**, how long each one has been live, and a plain-English summary of their strategy. Enter a brand name ("Nike"), a page link, a keyword or a pasted Ad Library link, pick the countries, and you get each ad's text, images and videos, **start date and days running**, active status, platforms, button and landing site. Switch on **EU transparency data** for reach in the EU and the age, gender and country breakdown. It costs **$0.50 per 1,000 ads, everything included**, and searches that fail or find nothing are free. The form is pre-filled with Nike's US ads, so the quickest way to see it is to click **Start** (about $0.02).

No Facebook account, cookies or login are needed: it reads the same public pages anyone can open at facebook.com/ads/library.

### Why use this Facebook Ads Library scraper?

- **Everything in one price.** Days running, EU reach and age/gender data, advertiser summaries and advertiser lookup by name all cost the same $0.50 per 1,000 ads. No start fee, no extra charge for details.
- **The ads that work, first.** An ad that has run for 200 days is usually one that pays for itself. Sort by `days_running` and you have a competitor's proven creatives.
- **A summary per advertiser.** How many ads they run, how many are active, their longest-running ad, formats, platforms, landing sites and the wording they keep repeating, in one row and one paragraph.
- **Fair billing.** Meta's pages repeat ads; you pay once per ad per run. Failed searches, repeats and anything past your spending limit are free.
- **Search by name.** Type "Nike" instead of hunting for a page ID.

### What do the output fields mean?

Every row has a `record_type`:

- **`ad`** (charged): one ad.
  - `ad_id` and `ad_library_url`: the ad's Library ID and its page in the Ad Library.
  - `page_name`, `page_id`, `page_url`, `page_likes`: the advertiser.
  - `start_date`, `end_date` (empty while it's still running), `is_active`, `days_running` (calendar days from the first to the last day, today for active ads).
  - `ad_text`, `title`, `link_description`, `cta_text` (the button), `link_url` and `landing_domain`.
  - `display_format` (IMAGE, VIDEO, CAROUSEL, DPA = catalogue ad, DCO = dynamic creative), `image_urls`, `video_urls`, `video_preview_urls`.
  - `platforms`: facebook, instagram, messenger, threads, whatsapp, audience_network.
  - `versions`: how many versions of this ad the advertiser runs. `contains_ai_media`: Meta's AI-generated media label.
  - `eu`: EU transparency data (option): `total_reach`, `top_countries`, `targeting_locations`, `targeting_age_min`/`max`, `targeting_gender`, `payer`, `beneficiary`, `reach_breakdown` (by country, age range and gender).
  - `political`: spend, impressions and audience ranges, for ads about social issues, elections or politics.
- **`advertiser`** (free): one summary per advertiser you searched for, and per advertiser with 3 or more ads in the run.
- **`search`** (free): one line per search and country: what was found, saved and charged, or why not.

### What can you use it for?

- **Competitor research:** see every ad a brand is running in a country, and which ones have run the longest. An ad that has been live for 200 days is usually one that works.
- **Creative inspiration:** pull the ads for a keyword ("protein powder", "car insurance") with their images, videos and copy, then sort by `days_running`.
- **Agency reporting and monitoring:** schedule a weekly run for your clients' competitors and track new and stopped ads.
- **EU research:** reach, age and gender breakdowns and payer details for ads shown in the EU, from Meta's Digital Services Act disclosures.
- **AI agents and pipelines:** stable field names, a documented schema and table views.

### How do I scrape the Facebook Ads Library?

1. Add **search terms**, **advertisers** or **Ad Library links** (any mix).
2. Pick **countries** (two-letter codes such as US, GB, DE, AU, or ALL). Each search runs once per country.
3. Set **Max ads per search**. You pay per ad, so this is also your cost cap.
4. Optional filters: **active status**, **media type**, **ad category**, and **shown from / shown until** dates.
5. Click **Start**, then open the **Ads**, **Advertisers** or **Searches** view, or export JSON, CSV or Excel.

#### How do I see all of a competitor's ads?

Put the brand in **Advertisers**: a name ("Nike"), their Facebook page link (facebook.com/nike), their numeric page ID, or a link to their Ad Library page. Names are matched the way the Ad Library search box matches them, and the `search` row tells you which page was used (`advertiser_name`, `advertiser_page_id`), so you can check it's the right one. The advertiser's summary row comes with it.

#### Can I paste a search from the Ad Library website?

Yes. Set up a search on facebook.com/ads/library, copy the address bar into **Ad Library links**, and the Actor runs that exact search with its country, status, media type and date filters.

#### What does the advertiser summary look like?

A real one, from a US run in October 2026:

> Nike has 34 ads in this run (US), all active. The longest has been running 486 days (since 20 Jun 2025). Formats: catalogue (product feed) 100%. Shown on Facebook, Instagram. Main landing site: nike.com. Recurring wording: "goes hard" (10 ads), "never misses" (9 ads), "gear" (34 ads), "field" (10 ads).

The same row has the numbers as separate fields: `ads_in_run`, `active_ads`, `ads_in_library`, `oldest_start_date`, `newest_start_date`, `longest_running_days` and a link to that ad, `median_days_running`, `formats`, `platforms`, `top_landing_domains`, `top_ctas` and `top_phrases`. It's worked out from the ads themselves, with no AI service involved, so it costs nothing extra.

#### What is the EU transparency data?

For ads shown in the EU, Meta publishes how many people saw the ad in the EU, a breakdown by country, age range and gender, the targeting (locations, ages, gender), and who paid for the ad and who it benefits. Switch on **EU transparency data** to add these to each ad's `eu` field. It takes one extra lookup per ad, so runs take about twice as long; the price per ad doesn't change. In our October 2026 test, 120 of 120 ads from German and French searches came back with it.

### How much does it cost?

**$0.50 per 1,000 ads ($0.0005 per ad). Searches that fail, find nothing or get skipped are free.**

| Event | Price | Charged when |
|---|---|---|
| Ad | $0.0005 | An ad is saved to your dataset. Each ad is charged once per run, even if several of your searches find it. |

- **Free:** `search` status rows, `advertiser` summaries, searches that fail or return no ads, ads already returned by an earlier search in the same run, and anything skipped because of your spending limit or the run's time limit.
- **Examples:** 50 ads = $0.025. One brand's 400 ads = $0.20. 20 searches of 500 ads = 10,000 ads = $5.
- **Free to try:** Apify's free plan includes $5 of usage each month, which is about **10,000 ads**. Platform usage is included in the price.
- Set a **maximum cost per run** in the run options and the Actor stops exactly there.

### How fast is it?

The first 30 ads of a search arrive in a few seconds. After that, Meta serves ads 10 at a time, so a 500-ad search takes about two minutes. Five searches run at once by default, so in our test 20 searches of 500 ads (10,000 ads) took about 8 minutes. The EU option roughly doubles that.

### Output example

One `ad` row (image links shortened):

```json
{
  "record_type": "ad",
  "ad_id": "1547214436957116",
  "ad_library_url": "https://www.facebook.com/ads/library/?id=1547214436957116",
  "page_id": "6053772414",
  "page_name": "Pizza Hut",
  "page_url": "https://www.facebook.com/pizzahutus/",
  "page_likes": 32565142,
  "is_active": false,
  "start_date": "2026-07-16",
  "end_date": "2026-08-31",
  "days_running": 47,
  "ad_text": "Get the $10 Medium Stuffed Crust you love plus MORE starting at just $3 on the Throwback Value Menu.",
  "cta_text": "Order now",
  "link_url": "https://www.pizzahut.com/menu/throwbackmenu?utm_source=meta&utm_campaign=tbvmcap",
  "landing_domain": "pizzahut.com",
  "display_format": "DCO",
  "image_urls": ["https://scontent-dfw5-1.xx.fbcdn.net/v/t39.35426-6/744198685_..._n.jpg?..."],
  "platforms": ["facebook", "instagram", "threads"],
  "versions": 1,
  "contains_ai_media": false,
  "political": null,
  "eu": null,
  "search_input": "pizza",
  "search_country": "GB",
  "scraped_at": "2026-10-18T11:22:47Z"
}
```

### How do I track a competitor's ads every week?

1. Fill in the input (their name under **Advertisers**, your countries, **Active and inactive**) and click **Save as a new task**.
2. Open **Schedules**, then **Create new**, pick the task and choose **weekly**.
3. Compare runs on `ad_id`: new IDs are new ads, and an ad whose `is_active` turned false has stopped.

### How do I send the data to Google Sheets, Make, Zapier, n8n or an AI agent?

- **Google Sheets:** schedule a saved task, then put this in cell A1 and allow access when Sheets asks:
  `=IMPORTDATA("https://api.apify.com/v2/acts/meridianlabs~facebook-ads-library-scraper/runs/last/dataset/items?status=SUCCEEDED&format=csv&view=ads&token=YOUR_TOKEN")`
  Use `view=advertisers` for the summaries. Anyone who can open the sheet can see the token, so make one with limited permissions in Console, Settings, API & Integrations.
- **Make, Zapier, n8n:** use the Apify "Run an Actor" or "Actor run finished" step, then "Get dataset items".
- **AI agents (MCP):** add `https://mcp.apify.com?tools=meridianlabs/facebook-ads-library-scraper` to your MCP client.
- **Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("meridianlabs/facebook-ads-library-scraper").call(run_input={
    "advertisers": ["Nike"], "countries": ["US", "GB"], "maxAdsPerSearch": 200, "activeStatus": "all",
})
for row in client.dataset(run.default_dataset_id).iterate_items():
    if row["record_type"] == "ad":
        print(row["page_name"], row["start_date"], row["days_running"], row["landing_domain"])
```

### Limitations

- **Only what the public Ad Library shows.** Spend and impressions are published for ads about social issues, elections or politics, not for ordinary commercial ads. Reach and age/gender data exist only for ads shown in the EU.
- **Image and video links expire** after a few days (Meta signs them). Download the files you want to keep.
- **The Ad Library caps its own counts at about 50,000** per search (`ads_in_library` shows 50,001 for "more than 50,000"), and its ordering is its own. Narrow a big search with dates, media type or a single advertiser.
- **Dates are Meta's.** `start_date` is the day the ad started running; for active ads `days_running` counts to today.
- Up to 10,000 ads per search and 500 searches per run.

### FAQ

#### Is it legal to scrape the Facebook Ads Library?

The Actor reads the public Ad Library pages that anyone can open without logging in. It never logs in and doesn't use accounts, cookies or tokens. In 2024 a US court found that Meta's terms didn't stop the scraping of public, logged-out data (Meta v. Bright Data), and Meta dropped the case. You are responsible for how you use the data, especially any personal data, under the laws that apply to you. This isn't legal advice.

#### Do I need a Facebook account or cookies?

No.

#### Does it collect personal data?

Ads are published by businesses and pages. Some advertisers are individuals (creators, small sellers), and the EU disclosures can name a person as payer or beneficiary, so treat those fields carefully under GDPR and similar laws. The Actor doesn't collect personal contact details and keeps nothing beyond your run's dataset.

#### Why did a search return fewer ads than the Ad Library says?

Usually because of **Max ads per search**. If a search stopped early, its `search` row says why in `reason` (for example your spending limit, the run's time limit, or Meta refusing a page after several retries). You are only charged for the ads you got.

#### Why does an ad appear under one search and not another?

Each ad is returned once per run. If a later search finds the same ad, it's counted in that search's `duplicates_skipped` and not charged again.

#### What happens if a run times out or restarts?

Searches that finished aren't repeated, and ads already saved are never charged twice. Searches the run didn't reach are listed as `skipped` and are free.

#### What happens when Meta changes the Ad Library?

Meta changes its internal query IDs often. The Actor detects this and picks up the new ones from Meta's public page on its own, and we run a daily test that tells us when anything else changes.

### More from Meridian Labs

- **[Competitor Keyword Research](https://apify.com/meridianlabs/competitor-keywords)**: every keyword a website ranks for in Google, with position, ranking page, search volume and estimated traffic, plus a plain-English summary per domain. $0.03 per domain + $2 per 1,000 keywords.
- **[Keyword Search Volume & CPC API + Google Trends](https://apify.com/meridianlabs/keyword-search-volume)**: monthly search volume, CPC, competition, difficulty and intent for keyword lists, plus a Google Trends summary per keyword. From $3 per 1,000 keywords.
- **[Google Trends Scraper & API](https://apify.com/meridianlabs/google-trends-scraper)**: interest over time, by region and rising related searches for hundreds of keywords. $5 per 1,000 keyword lookups.
- **[Backlink Checker](https://apify.com/meridianlabs/backlink-checker)**: referring domains, backlinks, domain rank and spam score. $0.03 per domain.
- **[Greenhouse, Lever & Ashby Jobs Scraper](https://apify.com/meridianlabs/greenhouse-lever-ashby-jobs-scraper)**: open jobs from companies on six hiring systems, with salary normalised. $2 per 1,000 jobs.
- **[Startup Funding Rounds & Founders API](https://apify.com/meridianlabs/sec-form-d-funding-rounds)**: US startups that just raised, from SEC Form D filings. $10 per 1,000 filings.
- **[EU & UK Tenders Scraper](https://apify.com/meridianlabs/eu-uk-tenders-scraper)**: public tenders and contract awards from TED and Find a Tender. $3 per 1,000 notices.
- **[YouTube Transcript API & Scraper](https://apify.com/meridianlabs/youtube-transcripts)**: full transcripts with timestamps in the video's original language, for videos and Shorts. $3 per 1,000 transcripts; videos without captions are free.

### Support

Something looks wrong, or you need a field we don't return? Open an issue on the **Issues** tab with the input you used. We read every one.

# Actor input Schema

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

Brands whose ads you want: a name (Nike), a Facebook page link (facebook.com/nike), a numeric page ID, or an Ad Library link with view_all_page_id. Names are matched the way the Ad Library search box does; the status row shows which page was used. Each advertiser also gets a free summary row.

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

Words to search ad text for, like the Ad Library search box (one search per term and country).

## `adLibraryUrls` (type: `array`):

Paste searches from facebook.com/ads/library: each keeps its own country, active status, media type and date filters.

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

Two-letter country codes where the ads were shown: US, GB, DE, FR, AU... (UK also works), or ALL for every country. Each search runs once per country.

## `maxAdsPerSearch` (type: `integer`):

The most ads to return for each search term or advertiser in each country, in the order the Ad Library shows them. You pay per ad, so this is also your cost cap.

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

Inactive ads show how long an ad ran before it was switched off.

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

Only ads with this kind of creative, like the Ad Library's media type filter.

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

All ads, or only ads about social issues, elections or politics (these also carry Meta's spend and impressions ranges).

## `dateFrom` (type: `string`):

Only ads that were shown on or after this date (the Ad Library's "impressions by date" filter). An ad that started earlier but was still running counts.

## `dateTo` (type: `string`):

Only ads that were shown on or before this date.

## `exactPhrase` (type: `boolean`):

Match search terms as an exact phrase instead of any order.

## `includeAdvertiserSummary` (type: `boolean`):

Adds a free summary row per advertiser you searched for, and per advertiser with 3+ ads in the run: ad counts, active count, oldest and newest ads, longest-running ad, formats, platforms, landing sites, recurring wording, and a one-paragraph plain-English summary.

## `includeEuDetails` (type: `boolean`):

For ads shown in the EU: total EU reach, reach by country, age and gender, the targeting (locations, ages, gender) and who paid and who benefits. One extra lookup per ad, so runs take longer; same price per ad.

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

How many searches run in parallel.

## `failOnErrors` (type: `boolean`):

For schedules and monitoring: mark the run failed (so Apify alerts you) if any search fails or stops early. Ads already saved are kept.

## Actor input object example

```json
{
  "advertisers": [
    "Nike"
  ],
  "countries": [
    "US"
  ],
  "maxAdsPerSearch": 50,
  "activeStatus": "active",
  "mediaType": "all",
  "adType": "all",
  "exactPhrase": false,
  "includeAdvertiserSummary": true,
  "includeEuDetails": false,
  "maxConcurrency": 5,
  "failOnErrors": false
}
```

# Actor output Schema

## `ads` (type: `string`):

One row per ad (dataset view 'ads').

## `advertisers` (type: `string`):

One summary row per advertiser (dataset view 'advertisers').

## `euTransparency` (type: `string`):

EU reach and targeting per ad (dataset view 'euTransparency').

## `searches` (type: `string`):

One status row per search and country (dataset view 'searches').

## `results` (type: `string`):

Every field of every row.

# 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 = {
    "advertisers": [
        "Nike"
    ],
    "countries": [
        "US"
    ],
    "maxAdsPerSearch": 50
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "advertisers": ["Nike"],
    "countries": ["US"],
    "maxAdsPerSearch": 50,
}

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

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

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

```

## CLI example

```bash
echo '{
  "advertisers": [
    "Nike"
  ],
  "countries": [
    "US"
  ],
  "maxAdsPerSearch": 50
}' |
apify call meridianlabs/facebook-ads-library-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

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