# Facebook (Meta) Ads Library Scraper (`kalilfagundes.w/facebook-instagram-meta-ads-library-scraper`) Actor

\[Limited Launch Offer $0.25/1K] Extract powerful ad intelligence insights from Meta Ads Library. Scrape all ads by keyword, page or Ad Library URL, with days running on every ad so the long-lasting winners surface first. Ad copy, creatives, CTAs, links, EU reach & demographics.

- **URL**: https://apify.com/kalilfagundes.w/facebook-instagram-meta-ads-library-scraper.md
- **Developed by:** [Kalil Fagundes](https://apify.com/kalilfagundes.w) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.18 / 1,000 ad scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

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 (Meta) Ads Library Scraper

### What does Meta Ads Library Scraper do?

Meta Ads Library Scraper extracts ad intelligence from the [Meta Ad Library](https://www.facebook.com/ads/library/): ad copy, images, videos, CTAs, landing page links, page details, run dates and **how many days each ad has been running**. To get that data, type in a keyword and a country and click "Save & Start". Alternatively, give it one or more Facebook page IDs to pull every ad those pages are running, or paste an Ad Library search URL straight from your browser.

No Facebook login, no cookies, no Meta API key.

### Why days running matters

You can't see a competitor's spend, clicks or conversions. You can see how long each ad has been live. Nobody keeps paying to run an ad for 60 days by accident, so **time on air is the best public signal that an ad is working**.

Every item comes with a `daysRunning` field. Sort your dataset by it and your competitors' proven creatives come to the top, while this week's tests sit at the bottom.

### What ad data can I extract?

| | |
|---|---|
| 📝 Ad copy, title and caption | 📅 Start and end dates |
| 🖼 Images (original and resized) | ⏳ **Days running** |
| 🎬 Videos (HD, SD and thumbnails) | 🟢 Active / inactive status |
| 🎠 Carousel cards | 📱 Platforms (Facebook, Instagram, Messenger, Threads…) |
| 🔘 CTA text and type | 🏷 Page name, ID, categories and likes |
| 🔗 Landing page URLs | 🇪🇺 EU reach, age and gender breakdown\* |
| 🆔 Ad archive ID and link | 📸 Instagram handle and followers\* |

\* With `fetchDetails` enabled.

### Can I use the official Meta Ad Library API instead?

You can, but it only covers political and social-issue ads, and it needs a Meta developer account plus identity verification by physical mail. Commercial ads — the ones you want for competitor research — are not in it.

This scraper reads the same public Ad Library the website shows, with no account or key, and returns every ad category.

### Why scrape the Meta Ad Library?

🕵️ Find the creatives competitors keep paying to run

✨ Track what a brand is testing this week, and what it dropped

🎨 Build a swipe file of ad copy, images and videos

🛬 See which landing pages get the most ads pointed at them

📊 Research angles and offers across a whole niche by keyword

🇪🇺 Pull EU reach and demographics for ads that ran in Europe

### How do I use Meta Ads Library Scraper?

1. Create a free Apify account using your email.
2. Open Meta Ads Library Scraper.
3. Type a keyword, or a page ID, or paste an Ad Library URL.
4. Click "Start" and wait for the data to be extracted.
5. Download your data in JSON, JSONL, CSV, Excel, HTML or XML.

### Input

The input is a keyword, a page ID or an Ad Library URL, plus the filters you want. For a fuller explanation, head over to the input tab.

```json
{
  "query": "pizza",
  "country": "US",
  "sortBy": "mostRecent",
  "activeStatus": "active",
  "mediaType": "all",
  "maxItems": 100,
  "fetchDetails": false
}
```

| Field | Type | Description |
|---|---|---|
| `maxItems` | integer | Maximum number of ads to scrape. Empty or `0` means unlimited, so the run stops at your maximum cost per run or at the run timeout. |
| `query` | string | Keywords to search for, one per line, up to 5. Each one runs as its own search, in parallel. Ignored when `pageId` is set. |
| `pageId` | string | All ads from these pages. Up to 5 numeric page IDs, one per line or comma separated. Ignores the search query. |
| `country` | string | ISO country code (`US`, `BR`, `GB`…) or `ALL`. |
| `category` | string | `all`, `political_and_issue_ads`, `housing_ads`, `employment_ads`, `credit_ads`. |
| `mediaType` | string | `all`, `image`, `meme`, `image_and_meme`, `video`, `none`. |
| `sortBy` | string | `mostRecent` or `impressions`. |
| `activeStatus` | string | `active`, `inactive` or `all`. |
| `minDate` / `maxDate` | string | Delivery date range, `YYYY-MM-DD`. |
| `fetchDetails` | boolean | Adds advertiser and transparency data under `_details`. One extra request per ad, so runs are slower. |
| `maxConcurrency` | integer | Searches running in parallel, each on its own proxy IP. Default `3`, up to `5`. |
| `urls` | array | Up to 5 Ad Library search URLs copied from your browser. Filters in the URL override the fields above. |
| `searchType` | string | `keyword` (default) or `exactPhrase`. |
| `languages` | array | Ad language codes (`en`, `pt`, `es`…). |
| `proxyConfiguration` | object | **Residential proxies are required** — see below. Used by default, leave as is. |

You need at least one of `query`, `pageId` or `urls`, with at most 5 entries each; runs over the limit are refused.

`query` and `pageId` both take several entries, one per line, and each entry runs as its own search. Meta's API only honours one page ID per request, which is why the Actor splits them.

### Proxies: residential is required

Meta blocks datacenter IP ranges. A residential proxy is needed for running, and `proxyConfiguration` already defaults to one.

### Output sample

The results are wrapped into a dataset you can find in the Storage tab. Here's an excerpt of what the input above returns. You can download it as JSON, JSONL, Excel, HTML, CSV or XML.

```json
[{
  "ad_archive_id": "574945338232404",
  "page_id": "194210777106441",
  "page_name": "Muppet Mania",
  "is_active": true,
  "start_date": 1732089600,
  "end_date": 1741334400,
  "daysRunning": 108,
  "publisher_platform": ["FACEBOOK", "INSTAGRAM"],
  "url": "https://www.facebook.com/ads/library/?id=574945338232404",
  "snapshot": {
    "body": { "text": "Limited Edition - The Muppets. Order here https://instock.click/uFO7UYBo" },
    "title": "Only available for a limited time!",
    "caption": "instock.click",
    "cta_text": "Shop now",
    "cta_type": "SHOP_NOW",
    "display_format": "IMAGE",
    "link_url": "https://instock.click/uFO7UYBo",
    "link_description": "Made In USA - Shipping WORLDWIDE !!!",
    "images": [{
      "original_image_url": "https://scontent-det1-1.xx.fbcdn.net/v/t39.35426-6/466796040_966955675270816.jpg",
      "resized_image_url": "https://scontent-det1-1.xx.fbcdn.net/v/t39.35426-6/467006778_3951980601753306.jpg"
    }],
    "videos": [],
    "cards": [],
    "page_categories": ["Clothing (Brand)"],
    "page_like_count": 284,
    "page_profile_uri": "https://www.facebook.com/61554673251967/"
  }
}]
```

With `fetchDetails` enabled, each ad also carries an `_details` object with advertiser and transparency data: Instagram handle and followers, page likes and category, and — for ads that ran in Europe — EU reach with an age × gender × country breakdown.

Every item is the raw ad record from the Ad Library, plus two fields this Actor adds: `daysRunning` and `url`.

### How many ads can you scrape?

| Input | Speed |
|---|---|
| One search | about 150 ads/minute |
| Four searches, `maxConcurrency` 1 | about 200 ads/minute |
| Four searches, `maxConcurrency` 4 | about 430 ads/minute |

Paging through one search is sequential — each page needs the cursor from the previous one — so `maxConcurrency` only changes anything when you pass several queries, page IDs or URLs. With a single search it makes no difference.

### How much will scraping the Meta Ad Library cost you?

This Actor charges per ad saved, so the price is easy to predict: 1,000 ads cost you the per-result price times 1,000, and nothing is charged for ads you don't get. You can cap any run with the maximum cost per run setting, and the Actor stops as soon as it hits it.

If you turn on `fetchDetails`, each ad detail is charged as a separate event, so a run with details on costs more than the same run without it.

### Run summary

Besides the ads, every run stores a `RUN_SUMMARY` record in its key-value store with the stats for that run: ads scraped, searches used, pages fetched, details fetched or failed, proxy sessions used, retried requests and rate limits hit. It is written as the run goes, so even a run that times out keeps its stats.

### Want a frontend to store and visualize the ads?

The Ad Library only shows what is live **today**. When a competitor switches an ad off it disappears, and the image and video links expire, so the ad and everything you could have learned from it is gone.

[Meta Ads Competitor Tracker](https://github.com/kalilfagundes/meta-ads-competitor-tracker) is a free, self-hosted companion for that: it collects ads on a schedule, mirrors every creative into your own storage, keeps the history after ads go off air, and gives you a searchable library ranked by time on air.

### Integrations

Meta Ads Library Scraper can be connected with almost any cloud service or web app thanks to integrations on the Apify platform. You can integrate with Make, Zapier, Slack, Airbyte, GitHub, Google Sheets, Google Drive and more.

You can also use webhooks to carry out an action whenever an event occurs, e.g. get a notification whenever a run finishes successfully.

### Using Meta Ads Library Scraper with the Apify API

The Apify API gives you programmatic access to the Apify platform. The API is organized around RESTful HTTP endpoints that let you manage, schedule and run Apify Actors, access datasets, monitor performance and fetch results.

Use the `apify-client` NPM package for Node.js or the `apify-client` PyPI package for Python. Check the Apify API reference docs for full details, or click the API tab for code examples.

### Is it legal to scrape the Meta Ad Library?

The Ad Library is a public transparency tool published by Meta, and this Actor only reads what is already shown there. Still, ad records name the pages and profiles that ran the ads, so results can contain personal data. You should not scrape personal data unless you have a legitimate reason to do so.

When using Facebook Ad Library Scraper, please consider the following:

- **Respect the Platform's Terms:** Ensure your use case complies with Facebook's terms of service and policies.
- **Avoid Overloading Servers:** Implement reasonable request rates to avoid overwhelming the website.
- **Data Privacy Regulations:** Be aware of and comply with personal data protection laws like GDPR.
- **Legal Advice:** If uncertain about the legal implications, consult with a legal professional.

For more insights on web scraping legality, please refer to our blog post: [is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

### Notes and limitations

- **Date filters** work on delivery, not on the start date: an ad that started before `minDate` can still show up if it was still running inside your range.
- **Impressions and spend** are only published by Meta for political and social-issue ads. For commercial ads those fields come back empty, whatever scraper you use.
- **Meta changes its internal API from time to time.** If searches start failing, the Actor's GraphQL document IDs need updating — open an issue and it gets fixed.

### Your feedback

We're always working on improving this Actor. If you have technical feedback or found a bug, please create an issue on the Actor's Issues tab in Apify Console.

# Actor input Schema

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

Maximum number of ads to scrape. Leave empty or 0 for unlimited (the run then stops at your maximum cost per run or at the run timeout).

## `query` (type: `string`):

Keywords to search ads for, one per line, maximum 5 — each one runs as its own search, in parallel. Runs with more than 5 are refused. Ignored when Page IDs is set.

## `pageId` (type: `string`):

Up to 5 numeric page IDs, one per line or separated by commas. Runs with more than 5 are refused. Returns every ad these pages are running and ignores the Search query.

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

Country code to filter ads

## `category` (type: `string`):

Category to filter ads

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

Media type to filter ads

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

Sort order for results

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

Active status to filter ads

## `minDate` (type: `string`):

Select min date in format YYYY-MM-DD

## `maxDate` (type: `string`):

Select max date in format YYYY-MM-DD

## `fetchDetails` (type: `boolean`):

When enabled, fetch advertiser and transparency details for each ad.

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

How many searches run in parallel, each on its own proxy IP. Only speeds things up with several queries or page IDs.

## `urls` (type: `array`):

Up to 5 Ad Library search URLs copied from the browser (e.g. https://www.facebook.com/ads/library/?active\_status=active\&ad\_type=all\&country=US\&q=pizza). Filters in the URL override the fields below.

## `searchType` (type: `string`):

Match any of the keywords, or the exact phrase.

## `languages` (type: `array`):

Ad content language codes (e.g. "en", "pt", "es").

## `proxyConfiguration` (type: `object`):

Meta blocks datacenter IPs, so residential proxies are required — runs without them are refused on the first request. Keep the default.

## Actor input object example

```json
{
  "maxItems": 0,
  "query": "pizza",
  "country": "ALL",
  "category": "all",
  "mediaType": "all",
  "sortBy": "mostRecent",
  "activeStatus": "active",
  "fetchDetails": false,
  "maxConcurrency": 3,
  "searchType": "keyword",
  "languages": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Every scraped ad: ad ID, page, ad text, images, videos, links, CTA, run dates, daysRunning and, with fetchDetails enabled, advertiser and transparency data under \_details.

## `overview` (type: `string`):

The same ads reduced to the main columns: ad ID, page, status, dates, days running, format, ad text, CTA and links.

## `runSummary` (type: `string`):

Stats for the run: ads scraped, searches, pages fetched, sessions used, failed requests and rate limits hit.

# 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 = {
    "maxItems": 0,
    "query": "pizza",
    "maxConcurrency": 3,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "maxItems": 0,
    "query": "pizza",
    "maxConcurrency": 3,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

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

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

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

```

## CLI example

```bash
echo '{
  "maxItems": 0,
  "query": "pizza",
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call kalilfagundes.w/facebook-instagram-meta-ads-library-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

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