# Meta Ad Library Scraper: Facebook & Instagram Ads (`humble-echidna/meta-ad-library`) Actor

Scrape Meta's Ad Library by keyword, advertiser Page or Ad Library URL: ad text, headline, CTA, link, image and video URLs, start date, days running and platforms (Facebook, Instagram, Messenger, Threads), plus EU reach and payer on request. Only-new mode for competitor monitoring. No login.

- **URL**: https://apify.com/humble-echidna/meta-ad-library.md
- **Developed by:** [Michael Costa](https://apify.com/humble-echidna) (community)
- **Categories:** Marketing, Social media, Lead generation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 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

### What does Meta Ad Library Scraper: Facebook & Instagram Ads do?

**Meta Ad Library Scraper** pulls ads from **Meta's Ad Library** (facebook.com/ads/library) for any **search
term, advertiser Page or Ad Library URL**: the ad's **text, headline, button, link, image and video URLs**, start
date, days running and the platforms it ran on (Facebook, Instagram, Messenger, Threads, WhatsApp, Audience Network).
On request it adds **EU reach**, targeting and who paid. Live, logged out; no Facebook account needed.

[Input fields](https://apify.com/humble-echidna/meta-ad-library/input-schema) ·
[API](https://apify.com/humble-echidna/meta-ad-library/api) ·
[Use it from Claude, ChatGPT or Cursor (MCP)](#can-i-use-meta-ad-library-scraper-from-an-ai-agent-mcp)

**Jump to:** [Monitor](#monitor-a-competitors-ads-get-only-new-ones-in-slack-email-or-a-webhook) ·
[Fields](#what-data-does-meta-ad-library-scraper-return) ·
[Price](#how-much-does-it-cost-to-scrape-the-facebook-ad-library) ·
[How to use](#how-to-scrape-the-facebook-ad-library) ·
[Input](#input) · [Output](#output) · [Switching](#switching-from-another-ad-library-actor) ·
[AI agents (MCP)](#can-i-use-meta-ad-library-scraper-from-an-ai-agent-mcp) · [Limits](#limits) · [FAQ](#faq)

**Try it in one click:** the input comes pre-filled with the search `running shoes` in every country, 50 ads. That's
50 ads, or about $0.035 (50 × $0.0007, plus $0.00005 for the run start). **Then replace it with what you actually
want.**

### Monitor a competitor's ads: get only new ones, in Slack, email or a webhook

With **Only new ads** on, each run returns (and charges for) only the ads that search hasn't returned before. A run
with nothing new costs only the $0.00005 start fee.

1. Put the competitor's Page in **Advertiser Pages** (`"pages": ["https://www.facebook.com/nike"]`), or a keyword in
   **Search terms**, turn on **Only new ads** (`"onlyNew": true`), and click **Start**. This first run returns the
   current ads (up to **Max ads per search**) and is the baseline.
2. Click **Save as a new task** (top right of the actor page). The memory is kept per input line and filter set (the
   same Page with another country is a separate search), in the key-value store `meta-ad-library-seen` in your
   account, so the task keeps comparing with its own earlier runs.
3. In Apify Console, open **Schedules**, click **Create new**, set how often in **Schedule setup** (for example daily
   at 07:00), then **Add** your task.
4. On the task, open the **Integrations** tab and pick where the new ads go:
   - **Slack**: click **Configure**, sign in, pick the workspace and channel, and the "run succeeded" event. A
     useful message: `{{resource.statusMessage}}` and a link,
     `<https://console.apify.com/storage/datasets/{{resource.defaultDatasetId}}|new ads>`.
   - **Gmail**: click **Connect with Google**, set the subject and body, and attach the dataset. It sends after each
     successful run.
   - **HTTP webhook**: event `ACTOR.RUN.SUCCEEDED`, your URL. Apify POSTs `{"eventType": ..., "resource": {...}}`;
     `resource.defaultDatasetId` is the run's dataset, and
     `GET https://api.apify.com/v2/datasets/<defaultDatasetId>/items?format=json` (with your API token) returns the
     new ads.

Apify's integrations fire after every successful run, including quiet ones: a quiet run's dataset is empty.

### What data does Meta Ad Library Scraper return?

One row per ad:

| Field | Example | Notes |
|---|---|---|
| `id`, `url` | `3897259703744748`, `https://www.facebook.com/ads/library/?id=3897259703744748` | The ad's Library ID and its own Library page |
| `pageName`, `pageId`, `pageUrl` | `On`, `238939146624`, `https://www.facebook.com/On/` | The advertiser |
| `text`, `title` | `Discover our selection of running shoes...`, `Running` | Primary text and headline (`null` when the ad has none) |
| `ctaText`, `ctaType`, `linkUrl` | `Shop now`, `SHOP_NOW`, `http://www.on.com/` | The button and where it goes |
| `imageUrls`, `videoUrls` | `["https://scontent...jpg"]` | Every image and video, carousel and catalogue cards included |
| `cards` | `[{"title": ..., "linkUrl": ..., "imageUrl": ...}]` | Each card of a carousel, dynamic or catalogue ad |
| `startDate`, `endDate`, `daysRunning` | `2026-05-06`, `2026-10-14`, `162` | Long-running ads are usually the ones that work |
| `isActive`, `platforms` | `true`, `["facebook", "instagram", "threads"]` | |
| `displayFormat` | `IMAGE`, `VIDEO`, `CAROUSEL`, `DCO`, `DPA` | DCO is dynamic creative, DPA a catalogue ad |
| `versions` | `3` | How many versions of the same creative Meta groups together |
| `euTotalReach`, `euReachBreakdown` | `1808715`, `[{"country": "DE", "ageRange": "25-34", "male": ..., "female": ...}]` | With **Ad details** on, for ads shown in the EU |
| `payers`, `advertiser` | `[{"payer": "...", "beneficiary": "..."}]` | With **Ad details** on |
| `spend`, `impressions`, `reachEstimate` | `$600K - $700K`, `>1M` | Political and issue ads only; `null` otherwise. Always US English, whatever country the request comes from |
| `spendMin`, `spendMax`, `currency` | `600000`, `700000`, `USD` | The spend range as numbers, ready to sort and sum; `impressionsMin`/`Max` and `reachEstimateMin`/`Max` likewise (`>1M` gives a min of `1000000` and no max) |

Every result has the same fields; the full list is under [Output](#output).

### How much does it cost to scrape the Facebook Ad Library?

You pay per ad: **$0.70 per 1,000 ads** on every Apify plan, plus $0.00005 each time a run starts.

- **The example below:** 50 ads × $0.70/1,000 = $0.035, plus the start fee.
- **A month, for example:** watching 20 competitor Pages daily with **Only new ads**, about 5 new ads per Page a day:
  20 × 5 × 30 = 3,000 ads ≈ **$2.10**, plus 30 run starts ($0.0015) for one daily task.
- **Ad details** (EU reach, payer, advertiser) cost nothing extra; they make a run slower.
- **Caps:** **Max ads per search** and **Max results per run** in the input, and **Maximum cost per run** in the run
  options. The run stops cleanly at whichever comes first, and stops fetching as soon as the limit is covered: the
  next page is never requested, so a capped run is also a fast one.

**Never charged:** searches that fail, ads already returned with **Only new ads**, and rows without an ad ID, Page or
link (they're dropped, not written).

### How to scrape the Facebook Ad Library

1. Open Meta Ad Library Scraper and click **Try for free** (or **Start** if you're signed in).
2. Fill in **Search terms**, **Advertiser Pages** or **Ad Library URLs** (any mix, one per line).
3. Optionally set **Country**, **Active status**, **Media type**, **Platforms**, the **Shown from / until** dates, and
   **Ad details** for EU reach.
4. Click **Start**, then open the **Output** tab and export as JSON, CSV or Excel. Ads appear in batches as they're
   found, not only at the end.

### Example: running shoes ads, every country

The pre-filled input:

```json
{"searchTerms": ["running shoes"], "maxAdsPerQuery": 50}
```

It returned 50 ads, every one with an image or video (local run, 2026-10-14). One of them, a catalogue ad with 6
cards (long values and lists shortened):

```json
{
  "id": "3897259703744748",
  "url": "https://www.facebook.com/ads/library/?id=3897259703744748",
  "query": "running shoes",
  "queryType": "keyword",
  "pageId": "238939146624",
  "pageName": "On",
  "pageUrl": "https://www.facebook.com/On/",
  "pageLikes": 1272558,
  "isActive": true,
  "startDate": "2026-05-06",
  "endDate": "2026-10-14",
  "daysRunning": 162,
  "platforms": ["facebook", "instagram", "audience_network", "messenger", "threads"],
  "displayFormat": "DPA",
  "title": "Running",
  "text": "Discover our selection of running shoes and apparel built to go the distance.",
  "caption": "on.com",
  "linkUrl": "http://www.on.com/",
  "ctaText": "Shop now",
  "ctaType": "SHOP_NOW",
  "imageUrls": ["https://scontent-phl2-1.xx.fbcdn.net/v/t39.35426-6/672676316...", "..."],
  "videoUrls": [],
  "cards": [
    {
      "title": "Running",
      "text": "Discover our selection of running shoes and apparel built to...",
      "linkUrl": "https://www.on.com/en-ph/products/weather-jacket-m-1me1031/m...",
      "ctaText": "Shop Now",
      "imageUrl": "https://scontent-phl2-1.xx.fbcdn.net/v/t39.35426-6/672676316..."
    },
    "..."
  ],
  "versions": null,
  "euTotalReach": null,
  "scrapedAt": "2026-10-14T22:34:43.441630Z"
}
```

On Apify, the same input took 11 s.

### Input

| Field | What it does |
|---|---|
| **Search terms** (`searchTerms`) | Words to search ads for, one search per line. An ad's Library ID finds that ad. |
| **Advertiser Pages** (`pages`) | Page address (`https://www.facebook.com/nike`), Page ID (`15087023444`) or an Ad Library address with `view_all_page_id`. |
| **Ad Library URLs** (`adLibraryUrls`) | Addresses copied from the Ad Library after searching there, or a single ad's (`?id=...`). Their filters win; the input fills in what they leave out. |
| Country (`country`) | Two-letter code (`US`, `GB`, `DE`...) or `ALL` (the default). `UK` is read as `GB`; the Ad Library has no EU-wide filter. |
| Active status (`activeStatus`) | `active` (the default), `inactive` or `all`. |
| Ad category (`adType`) | `all` (the default), `political_and_issue_ads`, `housing_ads`, `employment_ads`, `financial_products_and_services_ads`. |
| Media type (`mediaType`) | `all`, `image`, `video`, `meme`, `image_and_meme`, `none`. |
| Platforms (`platforms`) | Any of `facebook`, `instagram`, `messenger`, `audience_network`, `threads`, `whatsapp`. |
| Search terms match (`searchMode`) | `any_words` (the default) or `exact_phrase`. |
| Shown from / until (`dateFrom`, `dateTo`) | Ads shown in that window (YYYY-MM-DD), Meta's own date filter. |
| Max ads per search (`maxAdsPerQuery`) | Per input line; default 50. Meta lists the most recent ads first. |
| Max results per run (`maxResults`) | Cap across every search. |
| Ad details (`includeDetails`) | EU reach, EU targeting, payer and beneficiary, advertiser info. One extra request per ad. |
| Only new ads (`onlyNew`) | Only ads this search hasn't returned before ([how](#monitor-a-competitors-ads-get-only-new-ones-in-slack-email-or-a-webhook)). |

```json
{
  "searchTerms": ["meal kit"],
  "pages": ["https://www.facebook.com/HelloFresh.de", "15087023444"],
  "adLibraryUrls": ["https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=US&q=protein%20bar&search_type=keyword_unordered&media_type=video"],
  "country": "DE",
  "activeStatus": "active",
  "platforms": ["instagram"],
  "dateFrom": "2026-09-01",
  "maxAdsPerQuery": 200,
  "includeDetails": true
}
```

### Output

Every result has the same fields. Fields the Ad Library doesn't show for an ad are `null` (or an empty list). A real
video ad from a German search URL with **Ad details** on (local run, 2026-10-14; long links shortened):

```json
{
  "id": "2186988382220454",
  "adArchiveId": "2186988382220454",
  "url": "https://www.facebook.com/ads/library/?id=2186988382220454",
  "query": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=DE&media_type=video&q=hellofresh&search_type=keyword_unordered",
  "queryType": "url",
  "pageId": "161205527314747",
  "pageName": "HelloFresh",
  "pageUrl": "https://www.facebook.com/HelloFresh.de/",
  "pageProfilePictureUrl": "https://scontent-phl2-1.xx.fbcdn.net/v/t39.35426-6/825286349...",
  "pageLikes": 2519793,
  "pageCategories": ["Food Service"],
  "isActive": true,
  "startDate": "2026-10-02",
  "endDate": "2026-10-14",
  "daysRunning": 13,
  "platforms": ["facebook", "instagram", "threads"],
  "displayFormat": "VIDEO",
  "title": "Spare bis zu 120 €",
  "text": "Gewöhnlicher Feierabend, genialer Geschmack. Spar bis zu 120 € & lass uns wieder Abendessen!",
  "caption": "https://www.hellofresh.de",
  "linkUrl": "https://www.hellofresh.de/pages/cook-this-big-idea",
  "linkDescription": "Mit dem Code DEB2S131",
  "ctaText": "Order now",
  "ctaType": "ORDER_NOW",
  "imageUrls": [],
  "videoUrls": ["https://video-phl2-1.xx.fbcdn.net/o1/v/t2/f2/m366/AQP6PFoMbUN9...mp4"],
  "videoPreviewImageUrls": ["https://scontent-phl2-1.xx.fbcdn.net/v/t39.35426-6/825286412...jpg"],
  "cards": [],
  "versions": 1,
  "collationId": "28744967888449010",
  "categories": [],
  "containsAiContent": false,
  "spend": null,
  "spendMin": null,
  "spendMax": null,
  "reachEstimate": null,
  "reachEstimateMin": null,
  "reachEstimateMax": null,
  "impressions": null,
  "impressionsMin": null,
  "impressionsMax": null,
  "currency": null,
  "targetedCountries": [],
  "euTotalReach": 1808715,
  "euTargeting": {"locations": [{"name": "Germany", "type": "countries", "excluded": false}], "ageMin": 18, "ageMax": 65, "gender": "All"},
  "euReachBreakdown": [{"country": "DE", "ageRange": "18-24", "male": 21629, "female": 122222, "unknown": 2674}, "..."],
  "payers": [{"payer": "HelloFresh SE", "beneficiary": "HelloFresh SE"}],
  "advertiser": {"pageAlias": "HelloFresh.de", "pageCategory": "Food delivery service", "likes": 2519793, "verification": "BLUE_VERIFIED", "instagramUsername": "hellofreshde", "instagramFollowers": 215056, "instagramVerified": true, "about": "HelloFresh liefert Dir Kochboxen mit kreativen Rezepten und saisonalen Zutaten frisch zu Dir nach Hau..."},
  "scrapedAt": "2026-10-14T22:21:51.291350Z"
}
```

- Image and video links are Meta's CDN addresses, which expire after some days: download what you want to keep (for
  example with [Bulk Image Downloader](https://apify.com/humble-echidna/bulk-image-downloader)).
- `startDate` and `endDate` are Meta's days in Pacific time, as the Ad Library shows them. For a running ad,
  `endDate` is Meta's latest day (usually today) and `daysRunning` counts to it.
- `id` is stable across runs, so you can use it to deduplicate. The run's `RUN_STATS` record lists every input line,
  how many ads it returned, the Ad Library address of that search (`searchUrl`) and anything that failed, with the
  reason.

### Switching from another Ad Library actor?

Most Ad Library tools take the same things the Ad Library's own page does, under other names. Here's where each one
goes:

| What you had | Put it here |
|---|---|
| A list of Ad Library search URLs (`facebook.com/ads/library/?...`) | **Ad Library URLs** (`adLibraryUrls`), as they are; their country, status, media, platform and date filters are kept |
| Facebook Page URLs, or "page ads" | **Advertiser Pages** (`pages`): the address, the numeric Page ID, or an Ad Library `view_all_page_id` address |
| Keywords | **Search terms** (`searchTerms`); exact phrase with **Search terms match** (`searchMode: "exact_phrase"`) |
| A single ad's link (`?id=...`) or ad ID | **Ad Library URLs**, or the ID as a **Search terms** line |
| A limit per URL or per Page | **Max ads per search** (`maxAdsPerQuery`) |
| An overall count or "max items" | **Max results per run** (`maxResults`); the maximum cost per run in the run options holds too |
| Country code or `ALL` | **Country** (`country`) |
| Active / inactive / all | **Active status** (`activeStatus`) |
| A date period, or "newer than" / "older than" | **Shown from** / **Shown until** (`dateFrom`, `dateTo`), as dates |
| "Scrape ad details", EU reach, transparency | **Ad details** (`includeDetails: true`) |
| Proxy settings | Nothing to set: the actor picks its own connection |

What's different in the output: one flat row per ad with camelCase fields (`pageName`, `text`, `ctaText`,
`linkUrl`, `imageUrls`, `videoUrls`, `startDate` as a date) instead of Meta's raw `snapshot` object; card media is
listed with the ad's own; `daysRunning` is computed for you. To reshape rows or rename columns for a sheet, run the
dataset through [Dataset Transformer](https://apify.com/humble-echidna/dataset-transform).

### Run it on a schedule, or from your own code

1. Save your input as a [task](https://docs.apify.com/platform/actors/running/tasks) (**Save as a new task**, top right
   of the actor page) and add it to a [schedule](https://docs.apify.com/platform/schedules) (Console → Schedules →
   **Create new**).
2. Collect results: download the dataset as JSON, CSV or Excel; fetch the latest run's results from the
   [API](https://apify.com/humble-echidna/meta-ad-library/api)
   (`GET https://api.apify.com/v2/actor-tasks/<task id>/runs/last/dataset/items?status=SUCCEEDED&format=csv`, with
   your API token); let a [webhook](https://docs.apify.com/platform/integrations/webhooks) tell your system when a
   run succeeds; or connect it to Make, Zapier or n8n through
   [Apify's integrations](https://docs.apify.com/platform/integrations).

#### Can I use Meta Ad Library Scraper from an AI agent (MCP)?

Yes, through [Apify's MCP server](https://docs.apify.com/platform/integrations/mcp), from Claude, ChatGPT, Cursor or
any other MCP client. Add this to your client's MCP configuration (or let the agent find it with the server's actor
search); your client signs you in to Apify:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=humble-echidna/meta-ad-library"
    }
  }
}
```

To use an [Apify API token](https://console.apify.com/settings/integrations) instead of signing in, add
`"headers": {"Authorization": "Bearer <APIFY_TOKEN>"}` next to `url`.

An agent should pass what it's looking for and a small cap, e.g.
`{"pages": ["https://www.facebook.com/allbirds"], "country": "US", "maxAdsPerQuery": 20}` or
`{"searchTerms": ["protein powder"], "mediaType": "video", "maxAdsPerQuery": 30}`.

### Who it's for

Performance marketers and agencies studying competitors' creatives and offers, media buyers looking for long-running
(proven) ads in a niche, e-commerce and DTC brands watching rivals launch new ads, researchers and journalists
tracking political, housing or employment ads, and AI agents that need ad copy and creatives on demand.

### Why this one?

- **Limits that hold.** Max ads per search, Max results per run and your maximum cost per run all stop the run before
  the next page is fetched, so a run never bills past what you set.
- **Media for every kind of ad.** Carousel, dynamic-creative and catalogue ads come with their card images and videos
  in `imageUrls` / `videoUrls`, not just single-image ads. In the example above, all 50 ads had media.
- **EU reach and payer included in the price.** Turn on **Ad details** for EU reach by country, age and gender, EU
  targeting, payer and beneficiary, and the advertiser's Page and Instagram figures, at no extra charge.
- **Filters apply everywhere.** A pasted Ad Library URL keeps its own filters and takes your date range, platforms and
  the rest where it says nothing.
- **Only new ads** for monitoring, remembered per search, and `daysRunning` to spot ads that have run for months.
- **Fast, no browser.** It reads the data the Ad Library page loads: 500 ads took 111 s on Apify (2026-10-14).
- **Reliable.** One failing search never affects the others in your run. The run log and the `RUN_STATS` record say
  exactly which input line had a problem and why.

### Limits

- **What the Ad Library shows logged out, nothing more.** Spend, impressions and reach estimates exist only for
  political and issue ads; EU reach and targeting only for ads shown in the EU. No interest or custom-audience
  targeting (Meta doesn't publish it).
- **Page addresses are matched through the Page's ads.** Logged out, Facebook shows no Page ID, so a Page address
  (`facebook.com/somebrand`) is turned into its ID by finding the Page among the Ad Library's advertisers: by its
  address, or, when the address has changed, by its exact name if only one advertiser has that name (`RUN_STATS`
  says which, as `pageMatchedBy`). If that fails (two Pages with the name, Pages with no ads), the line fails with
  the reason: paste the numeric Page ID instead (on the Page: **About > Page transparency**).
- **Media links expire** after some days (Meta's CDN). Download what you need to keep.
- **Only new ads** stops a search after 3 pages in a row with nothing new, since Meta lists the most recent ads first;
  a new ad Meta ranks below that isn't returned until it moves up.
- If Meta changes how the Ad Library page loads its data, searches fail with a message saying so until the actor is
  updated.

### FAQ

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

The Ad Library is Meta's public transparency archive: anyone can open it without an account. This actor reads the
same public data the Ad Library page loads in a browser, logged out: it never signs in, uses no account, cookie or
session, and solves no captcha or bot check. Please note: **facebook.com's robots.txt disallows automated access for
every crawler**, and Meta's automated-data-collection terms ask for its written permission; those terms bind people
who agree to them as Facebook users, and in *Meta v. Bright Data* (N.D. Cal., January 2024) the court held that
Meta's terms didn't cover logged-out collection of public data. The actor identifies itself honestly (User-Agent
`HumbleEchidnaApify`) and keeps a modest pace. The data is about ads and the Pages that run them; political ads can
name the people who paid for them. How you use the data, including any personal data, is up to you and the laws that
apply to you (GDPR in the EU). If you're Meta or an advertiser and want something taken down, open an issue.

#### Is there an official Meta Ad Library API?

Yes, but it needs a Facebook account that has passed Meta's identity confirmation, plus a developer app, and it
covers political and issue ads worldwide and ads shown in the EU and UK: ordinary commercial ads that ran only
elsewhere (most US ads) aren't in it. This actor covers every ad the Ad Library website shows, with no account.

#### Why did a search return nothing, or fewer ads than expected?

Usually the filters leave nothing (an inactive status with a recent date range, a platform the advertiser doesn't use)
or the Page address couldn't be matched to a Page ID (the reason is in the log; paste the ID). If Meta rate-limits
a connection, the search moves to a fresh one before giving up. The run log and the `RUN_STATS` record name the input
line and what happened; every other search in the run is unaffected and failed searches aren't charged. Please open
an issue with the input you used if it keeps happening.

#### Can I get only new results since my last run?

Yes: turn on **Only new ads** ([how](#monitor-a-competitors-ads-get-only-new-ones-in-slack-email-or-a-webhook)).

#### Can I download the ad images and videos?

The rows carry the image and video addresses. To save the files, send the dataset to
[Bulk Image Downloader](https://apify.com/humble-echidna/bulk-image-downloader), or download them in your own code
before the links expire.

### Related actors

| Actor | Use it when |
|---|---|
| [Bulk Image Downloader](https://apify.com/humble-echidna/bulk-image-downloader) | You want the ad images saved before Meta's links expire |
| [Website Change Monitor](https://apify.com/humble-echidna/website-change-monitor) | You also want to know when a competitor's landing page changes |
| [Google Trends Scraper](https://apify.com/humble-echidna/google-trends) | You want to see whether interest in what they advertise is rising |
| [Dataset Diff](https://apify.com/humble-echidna/dataset-diff) | You want ads added and removed between two runs, not just new ones |
| [Dataset Transformer](https://apify.com/humble-echidna/dataset-transform) | You want the results flattened into a CSV or Excel sheet your way |

### Feedback and support

Found a bug, or need a field or filter that isn't here? Open an issue on the **Issues** tab with the input you used.

### Versions

Current version: **0.1**. See the Changelog tab for what changed in each version.

# Changelog

This Actor's version history is a separate document: https://apify.com/humble-echidna/meta-ad-library/changelog.md

# Actor input Schema

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

Words to search ads for, one search per line (e.g. running shoes, or a brand name). Each line is searched in the Ad Library with the filters below, like typing it into the Ad Library search box. A line that is an ad's library ID (e.g. 1870984290951787) finds that ad. Leave empty if you only use Pages or Ad Library URLs.

## `pages` (type: `array`):

Facebook Pages whose ads you want, one per line: the Page address (https://www.facebook.com/nike), its numeric Page ID (15087023444, shown on the Page under About > Page transparency) or an Ad Library address with view_all_page_id. A Page address is matched to its ID through the Page's own ads; if that fails, paste the Page ID. Every ad the Page runs is returned, filtered by the options below.

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

Addresses copied from facebook.com/ads/library after searching there, one per line, e.g. https://www.facebook.com/ads/library/?active_status=active\&ad_type=all\&country=US\&q=coffee\&search_type=keyword_unordered. Filters in the address (country, active status, media type, platforms, dates) win; filters it leaves out come from the options below. A single-ad address (?id=...) returns that ad.

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

Where the ads ran: a two-letter country code (US, GB, DE, FR, BR...) or ALL for every country. Ads that ran in the EU come with EU reach figures when Ad details is on.

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

`active` (running now, the default), `inactive` (stopped) or `all`.

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

`all` ads (the default), or only one of Meta's special categories: `political_and_issue_ads`, `housing_ads`, `employment_ads`, `financial_products_and_services_ads`.

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

Only ads with this kind of media: `all` (the default), `image`, `video`, `meme`, `image_and_meme`, or `none` (text only).

## `platforms` (type: `array`):

Only ads shown on these Meta platforms: facebook, instagram, messenger, audience_network, threads, whatsapp. Leave empty for all.

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

`any_words` (the default) finds ads with the words in any order; `exact_phrase` only ads with the exact phrase. Applies to Search terms.

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

Only ads shown on or after this day (YYYY-MM-DD), Meta's own date filter: an ad started earlier that was still shown on this day counts. Leave empty for no start.

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

Only ads shown on or before this day (YYYY-MM-DD). Leave empty for no end.

## `maxAdsPerQuery` (type: `integer`):

Stop each search term, Page or URL after this many ads, e.g. 50. Meta lists the most recent ads first. Minimum 1.

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

Stop after this many ads in total, across every search, e.g. 500. Minimum 1; leave empty (the default) for no limit. The run also stops cleanly at the maximum cost per run you set in the run options, whichever comes first. Set it to keep a run small and quick.

## `includeDetails` (type: `boolean`):

Also read each ad's details panel: EU reach (total and by country, age and gender, for ads shown in the EU), EU targeting (locations, ages, gender), who paid for the ad and who it benefits, and the advertiser's Page category, likes and Instagram account. One extra request per ad, so runs take longer; no extra charge. Off by default.

## `onlyNew` (type: `boolean`):

Return only ads this search hasn't returned in an earlier run, for monitoring a competitor's or a keyword's new ads on a schedule. Remembered per search line and filter set in a key-value store in your account (meta-ad-library-seen). A search stops after 3 pages in a row with nothing new, since Meta lists the most recent ads first.

## Actor input object example

```json
{
  "searchTerms": [
    "running shoes"
  ],
  "country": "ALL",
  "activeStatus": "active",
  "adType": "all",
  "mediaType": "all",
  "searchMode": "any_words",
  "maxAdsPerQuery": 50,
  "includeDetails": false,
  "onlyNew": false
}
```

# Actor output Schema

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

No description

## `runStats` (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 = {
    "searchTerms": [
        "running shoes"
    ],
    "country": "ALL",
    "maxAdsPerQuery": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("humble-echidna/meta-ad-library").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 = {
    "searchTerms": ["running shoes"],
    "country": "ALL",
    "maxAdsPerQuery": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("humble-echidna/meta-ad-library").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 '{
  "searchTerms": [
    "running shoes"
  ],
  "country": "ALL",
  "maxAdsPerQuery": 50
}' |
apify call humble-echidna/meta-ad-library --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,humble-echidna/meta-ad-library"
        }
    }
}
```

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/qdnOXFahZV1pxlleh/builds/mgIdNWOUM7WsdRxsz/openapi.json
