# Facebook Ads Library Scraper: Meta Ad Spy Tool (`dltik/facebook-ads-scraper`) Actor

Scrape Facebook & Instagram ads from Meta Ad Library by keyword, page or URL. Get ad copy, creatives, carousel cards, CTA links, EU spend, impressions, age/gender audience reach and running-days per ad. Filter by country, language, status. For ad spy, dropshipping, agencies. $0.75/1K ads.

- **URL**: https://apify.com/dltik/facebook-ads-scraper.md
- **Developed by:** [Walid](https://apify.com/dltik) (community)
- **Categories:** Marketing, Social media, Lead generation
- **Features:** Limited permissions
- **Stats:** 613 total users, 126 monthly users, 99.8% runs succeeded, 10 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.005 / actor start

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 — Meta Ad Spy Tool

![Facebook Ads Library Scraper banner](https://api.apify.com/v2/key-value-stores/PqNgJprOMO7epUETC/records/banner.png)

**Facebook Ads Library Scraper** lets you **search and extract Facebook & Instagram ads** from the official Meta Ad Library — without any API token or developer account. Enter a keyword and country, get ad copy, page info, platforms, dates, creative URLs, **EU spend, impressions & age/gender audience reach**, and auto-enrichment (CTA detection, ad categorization, **running duration**).

> **No API token needed. No proxy needed.** Just enter a keyword and click Start. The fastest way to spy on competitor ads.

> 🎯 **Two signals most scrapers skip:** (1) **how long each ad has been running** (`duration_days`) — the #1 way to spot a competitor's winning, long-lived creatives, and (2) **EU age/gender audience reach** (`demographic_distribution`) — who an ad actually reaches, straight from Meta's DSA transparency data.

> ⭐ **Found this useful?** Click the **Bookmark** button at the top of [this page](https://apify.com/dltik/facebook-ads-scraper) — it helps the scraper stay visible to others who need it. Takes 1 click. No signup beyond your existing Apify account.

***

### What can Facebook Ads Library Scraper do?

- 🔍 **Search ads by keyword** — find all ads containing "fitness", "dropshipping", "skincare", etc.
- 📄 **Search ads by page** — get all ads from a specific Facebook page
- 🌍 **Filter by country** — target ads shown in any country or worldwide
- 📊 **Active/inactive filter** — see only running ads or recently stopped ones
- 🤖 **Auto-enrichment** — CTA detection, ad category (ecommerce/lead gen/app install), running duration, hashtags, emoji count
- 📱 **Platform detection** — which platforms the ad runs on (Facebook, Instagram, Messenger)
- ⚡ **No API token required** — works out of the box, zero configuration

***

### What data can you extract from Facebook Ads?

| Field | Description |
|-------|-------------|
| `page_name` | Advertiser page name |
| `ad_text` | Full ad copy / creative body |
| `start_date` | When the ad started running |
| `end_date` | When the ad stopped (null if still active) |
| `is_active` | Whether the ad is currently running |
| `platforms` | Facebook, Instagram, Messenger, Audience Network |
| `ad_snapshot_url` | Direct link to view the ad creative |
| `ad_id` | Meta Ad Library ID |
| `media_type` | Image, video, or carousel |
| `cards` | Carousel cards — each with its own copy, CTA, link & creative |
| `link_url` | Landing page URL the ad drives traffic to |
| `ad_category` | Auto-detected: ecommerce, lead_gen, app_install, video, brand_awareness |
| `detected_ctas` | Call-to-action phrases found (Shop Now, Sign Up, Learn More...) |
| `duration_days` | **How many days the ad has been running** — spot winning creatives |
| `spend` | EU spend range (DSA transparency) |
| `impressions` | EU impressions range (DSA transparency) |
| `eu_total_reach` | Total people reached in the EU |
| `demographic_distribution` | **Age × gender audience breakdown** (who the ad reaches) |
| `reach_by_country` | Reach split per EU country |
| `hashtags` | Hashtags used in the ad copy |
| `word_count` | Number of words in the ad text |

***

### How to search Facebook ads

1. **[Create a free Apify account](https://apify.com/sign-up)** — no credit card required
2. **Open [Facebook Ads Library Scraper](https://apify.com/dltik/facebook-ads-scraper)** in Apify Store
3. **Enter a keyword** (e.g. `fitness`) and **select a country** (e.g. `FR`)
4. **Choose filters** — active/inactive, max results
5. **Click Start** — ads start appearing in seconds
6. **Download your results** in JSON, CSV, or Excel

***

### How much does it cost to scrape Facebook ads?

**$0.00075 per ad extracted** ($0.75 per 1,000 ads) — up to 4× cheaper than comparable scrapers.

| Run size | Ads | Apify cost | Time |
|----------|-----|------------|------|
| Quick test (10 ads) | 10 | ~$0.0075 | ~15s |
| Small batch (50 ads) | 50 | ~$0.04 | ~30s |
| Standard (200 ads) | 200 | ~$0.15 | ~1min |
| Large (500 ads) | 500 | ~$0.38 | ~2min |

***

### Input

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `searchTerms` | string | ❌ | — | Keywords to search (e.g. `fitness`) |
| `pageId` | string | ❌ | — | Facebook Page ID to get all its ads |
| `country` | string | ❌ | `ALL` | Country code (FR, US, GB, DE...) or ALL |
| `activeStatus` | string | ❌ | `all` | `all`, `active`, or `inactive` |
| `maxResults` | integer | ❌ | `50` | Max ads to return (1-500) |
| `enrichAds` | boolean | ❌ | `true` | Add CTA detection, category, duration |
| `watch` | boolean | ❌ | `false` | Monitoring mode — return only new/stopped ads vs the last run ([see below](#competitor-watch--monitor-new-ads-automatically)) |

> Provide either `searchTerms` or `pageId` (or both).

***

### Output example

![Sample output — Facebook Ads Library Scraper](https://api.apify.com/v2/key-value-stores/PqNgJprOMO7epUETC/records/output.png)

Each ad returns the enriched fields below. The **`duration_days`** column is the fastest way to spot a competitor's proven, long-running creatives:

```json
{
  "ad_id": "1234567890",
  "page_name": "FitnessBrand",
  "ad_text": "Get 50% off our best-selling protein powder! Limited time offer. Shop now at fitnessbrand.com #fitness #protein",
  "start_date": "Mar 16, 2026",
  "end_date": null,
  "is_active": true,
  "platforms": ["facebook", "instagram"],
  "ad_snapshot_url": "https://www.facebook.com/ads/library/?id=1234567890",
  "media_type": "image",
  "link_url": "https://fitnessbrand.com/protein",
  "ad_category": "ecommerce",
  "detected_ctas": ["shop now"],
  "duration_days": 13,
  "spend": { "lower_bound": 1000, "upper_bound": 1499 },
  "impressions": { "lower_bound": 100000, "upper_bound": 149999 },
  "eu_total_reach": 84200,
  "demographic_distribution": {
    "25-34": { "male": 18200, "female": 21000, "unknown": 120 },
    "35-44": { "male": 14100, "female": 16800, "unknown": 80 }
  },
  "reach_by_country": { "FR": 51200, "DE": 33000 },
  "hashtags": ["#fitness", "#protein"],
  "emoji_count": 0,
  "word_count": 18
}
```

***

### Use cases

- **Competitor ad research** — see exactly what ads your competitors are running right now
- **Dropshipping product research** — find winning products by searching ad keywords
- **Agency reporting** — track client competitors' ad strategies over time
- **Creative inspiration** — browse thousands of ad copies in your niche
- **Market research** — understand what's being advertised in any country and industry

***

### Competitor Watch — monitor new ads automatically

Turn the scraper into a **Facebook ad monitor**. Set **`watch: true`**, point it at the pages, brands or Ad Library URLs you care about, and **[schedule it to run daily or weekly](https://docs.apify.com/platform/schedules)**. Instead of re-dumping the full ad list every run, it compares each target against the previous run and returns **only what changed**:

- **`new_ad`** — a creative the advertiser **just launched** (with the full ad data + the `winner_tier` signal, so you instantly see if it's a serious new angle)
- **`stopped`** — an ad the advertiser **pulled** (a strong hint about what stopped working — or seasonal rotation)

The **first run records a baseline** (it learns the current ads, reports no changes). Every run after that is a clean diff — perfect for a Slack/email alert the moment a competitor ships a new ad.

#### Why use ad monitoring instead of a one-off scrape?

- 📈 **Catch competitor launches in hours, not weeks** — know the instant a rival starts a new campaign
- 🏆 **See what they kill** — `stopped` ads reveal the losers; `new_ad` + long `duration_days` on the next run reveal the keepers
- 🔔 **Hands-off** — schedule it once; you only get records when something actually changes
- 🧠 **Built for growth & creative teams** — a living feed of a competitor's creative strategy

#### Watch output example

```json
{
  "watch_change": "new_ad",
  "watch_target": "page:15087023444",
  "detected_at": "2026-10-16T09:00:00Z",
  "ad_id": "1249046197407500",
  "page_name": "CompetitorBrand",
  "ad_text": "NEW: our biggest discount of the year — 40% off everything...",
  "ad_snapshot_url": "https://www.facebook.com/ads/library/?id=1249046197407500",
  "media_type": "video",
  "start_date": "2026-10-16T08:00:00.000Z",
  "is_active": true,
  "winner_tier": "new"
}
```

A `stopped` record is compact — the ad's library ID, how long it ran, and its last known winner tier:

```json
{
  "watch_change": "stopped",
  "watch_target": "page:15087023444",
  "detected_at": "2026-10-16T09:00:00Z",
  "ad_archive_id": "9988776655",
  "ad_snapshot_url": "https://www.facebook.com/ads/library/?id=9988776655",
  "ran_for_days": 142,
  "was_winner_tier": "strong"
}
```

> 💡 **Tip:** for reliable `stopped` detection, set **Max results** high enough to cover a target's whole active set — otherwise an ad beyond the cap could look "stopped" when it isn't (the actor automatically suppresses `stopped` on runs that hit the cap to avoid false alarms).

**Billing:** watch mode is charged per **advertiser watched per run** ($0.03) plus per **change detected** ($0.05) — unchanged ads and the baseline run are free. No AI/transcription is run in watch mode.

***

### Use Facebook Ads Scraper via API

**Python:**

```python
import requests

run = requests.post(
    "https://api.apify.com/v2/acts/dltik~facebook-ads-scraper/runs",
    headers={"Authorization": "Bearer YOUR_APIFY_TOKEN"},
    json={
        "searchTerms": "fitness",
        "country": "FR",
        "maxResults": 100,
        "enrichAds": True
    }
).json()

print(f"Run started: {run['data']['id']}")
```

**curl:**

```bash
curl -X POST "https://api.apify.com/v2/acts/dltik~facebook-ads-scraper/runs" \
  -H "Authorization: Bearer YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms": "fitness", "country": "FR", "maxResults": 50}'
```

***

### FAQ

**Do I need a Facebook account or API token?**
No. The Meta Ad Library is a public transparency tool. This actor scrapes it directly — no login, no API token, no developer account needed.

**What's the difference between this and the Meta Ad Library API?**
The official API requires a developer account, app review, and access token management. This actor gives you the same data with zero setup. Just enter a keyword and click Start.

**Can I search ads from a specific competitor?**
Yes. Use the `pageId` parameter with your competitor's Facebook Page ID. You can find the page ID in the page's URL or About section.

**Can I see who an ad targets — age and gender?**
Yes, for ads served in the EU. Meta publishes age × gender audience reach and total EU reach under its DSA transparency rules, and this scraper returns it as `demographic_distribution`, `eu_total_reach`, and `reach_by_country`. Most Facebook ad scrapers — including the most popular ones — don't expose this.

**How do I find a competitor's best-performing ads?**
Sort by `duration_days`. Ads that have been running for weeks or months are the ones converting — advertisers kill losers fast and keep winners alive. This running-duration signal is the fastest shortcut to a competitor's proven creatives.

**Why do some ads have no text?**
Some ads are image-only or video-only without body text. The `ad_text` field will be empty but you'll still get the page name, dates, platforms, and snapshot URL.

**How often does the Ad Library update?**
Meta updates the Ad Library in near-real-time. New ads appear within hours of going live. Stopped ads are marked inactive.

**I need help or a custom solution.**
Open an issue on the [Issues tab](https://apify.com/dltik/facebook-ads-scraper/issues) or contact us through Apify.

***

### Connect with Make, Zapier & n8n

This actor integrates with any automation platform via the Apify API.

#### Make (Integromat)

1. Add an **Apify module** in your Make scenario
2. Select **Run Actor** and choose this actor
3. Configure the input (paste your JSON)
4. Add a **Get Dataset Items** module to retrieve results
5. Connect to Google Sheets, HubSpot, Slack, or any other app

#### Zapier

1. Use the **Apify integration** on Zapier
2. Set trigger: **Actor Run Finished**
3. Action: **Get Dataset Items**
4. Send results to your CRM, email tool, or spreadsheet

#### n8n

1. Add an **HTTP Request** node to call the Apify API
2. POST to `https://api.apify.com/v2/acts/dltik~facebook-ads-scraper/runs`
3. Wait for completion, then fetch dataset items
4. Route results to any n8n node

#### Webhooks

Set up a webhook to get notified when a run finishes:

```python
run = client.actor("dltik/facebook-ads-scraper").call(
    run_input={...},
    webhooks=[{
        "eventTypes": ["ACTOR.RUN.SUCCEEDED"],
        "requestUrl": "https://your-webhook-url.com"
    }]
)
```

***

***

⭐ **Found this Facebook Ads Library Scraper useful? Bookmark it** — Apify ranks actors by bookmarks, so it's the strongest single signal for Store visibility. One click = directly helps this actor stay surfaced.

### Other scrapers by dltik

| Actor | What it does | Price |
|-------|-------------|-------|
| [Google Maps Email Extractor](https://apify.com/dltik/google-maps-email-extractor) | Extract emails, phones, WhatsApp from Google Maps businesses | $25/1K |
| [TikTok Scraper](https://apify.com/dltik/tiktok-scraper) | Scrape profiles, videos, hashtags, search, trending | $1/1K |
| [TikTok Video Downloader](https://apify.com/dltik/tiktok-video-downloader) | Download TikTok videos without watermark | $5/1K |
| [Trustpilot Scraper](https://apify.com/dltik/trustpilot-scraper) | Scrape reviews, ratings, company profiles with sentiment | $0.50/1K |
| [HackerNews MCP Server](https://apify.com/dltik/mcp-server-hackernews) | HN search for AI agents — tech audience research | $5/1K |

# Actor input Schema

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

List of Meta Ad Library URLs or Facebook page ad URLs. Supports search results URLs, page URLs, and filtered URLs. Process multiple URLs in one run.

## `searchTerms` (type: `string`):

Keywords to search in ad text (e.g. 'fitness', 'nike', 'skincare'). Alternative to pasting an Ad Library URL.

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

View all ads from a specific Facebook page. Get the page ID from the page URL or About section.

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

ISO 2-letter country code (US, FR, GB, DE, etc.). Use 'ALL' for worldwide.

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

Filter by active, inactive, or all ads.

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

Filter by media type: all, image, video, or meme.

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

Filter ads by language (ISO 639-1 codes, e.g. \['en', 'fr']). Leave empty for all languages.

## `sortMode` (type: `string`):

Preferred sort (relevance, total impressions, or start date). Best-effort: the Facebook Ad Library frequently overrides custom sort with its own relevance ranking, so results may not strictly follow the requested order. Sorting never changes WHICH ads are returned, only their (attempted) order.

## `sortDirection` (type: `string`):

Sort ascending or descending.

## `maxResults` (type: `integer`):

Maximum number of ads to extract per URL. Initial page load gives ~28 ads. More requires scrolling.

## `enrichAds` (type: `boolean`):

Add CTA detection, ad category (ecommerce/lead_gen/app_install), running duration, hashtag count, word count, and a winner_score (0-100) flagging proven performers by longevity + variants + still-active.

## `fetchEuReach` (type: `boolean`):

Open each ad's EU transparency details and add eu_total_reach (people reached in the EU), the age x gender split, reach per country, targeting (age range, gender, locations) and payer/beneficiary. Only for ads shown in the EU. Adds about 0.5 s per ad.

## `analyzeAds` (type: `boolean`):

Use AI to classify each ad's marketing angle, emotional hook, offer type and target persona. Paid Apify plan only. Billed per ad analyzed.

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

Transcribe the audio of each video ad so you can read the script/hook of winning video creatives. Paid Apify plan only. Billed per video transcribed.

## `watch` (type: `boolean`):

Turn this run into a MONITOR of your targets (pages, brands or URLs above). It compares the current live ads to the previous run and returns ONLY new creatives — plus ads the advertiser stopped — instead of the full list. Schedule it daily/weekly to track competitors' launches. The first run records a baseline (no changes yet). Billed per advertiser watched + per change detected. Tip: for reliable 'stopped' detection, set Max results high enough to cover a target's whole active set.

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

How many URLs to scrape in parallel. Higher = faster on multi-URL/multi-brand runs, each on its own residential IP. Kept to 8 max so the browser fallback stays within the 512MB memory limit.

## `useResidentialProxy` (type: `boolean`):

Route requests through residential IPs. Required to fetch more than ~30 ads per search (Facebook rate-limits pagination per IP). Disable to run free on datacenter IPs at ~30 ads/URL.

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

Advanced: override the proxy. Defaults to Apify residential proxy in the search country. Leave empty to use the recommended default.

## Actor input object example

```json
{
  "urls": [
    "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&media_type=all&search_type=keyword_unordered&q=fitness"
  ],
  "country": "ALL",
  "activeStatus": "all",
  "mediaType": "all",
  "sortMode": "relevance",
  "sortDirection": "desc",
  "maxResults": 50,
  "enrichAds": true,
  "fetchEuReach": false,
  "analyzeAds": false,
  "transcribeVideos": false,
  "watch": false,
  "maxConcurrency": 4,
  "useResidentialProxy": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

List of ads with text, page info, dates, platforms, and enrichment.

# 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 = {
    "urls": [
        "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&media_type=all&search_type=keyword_unordered&q=fitness"
    ],
    "country": "ALL"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dltik/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 = {
    "urls": ["https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&media_type=all&search_type=keyword_unordered&q=fitness"],
    "country": "ALL",
}

# Run the Actor and wait for it to finish
run = client.actor("dltik/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 '{
  "urls": [
    "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&media_type=all&search_type=keyword_unordered&q=fitness"
  ],
  "country": "ALL"
}' |
apify call dltik/facebook-ads-scraper --silent --output-dataset

```

## MCP server setup

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