# Meta Ad Library Scraper (`dami_studio/meta-ads-library-scraper`) Actor

Meta Ad Library scraper for Facebook and Instagram ads, by keyword, Page ID or URL. Each row has the ad copy, creatives, call to action, advertiser Page, run dates and platforms. Spend and impressions exist only on political and issue ads. $0.34 per 1,000 ads.

- **URL**: https://apify.com/dami_studio/meta-ads-library-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media
- **Features:** Limited permissions
- **Stats:** 23 total users, 8 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.33 / 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.
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

**Meta Ad Library scraper for Facebook and Instagram ads by keyword, Page, ad ID or URL**, and the
matching ads come back as rows: the ad copy, the creative URLs, the call to action, the advertiser
Page, the run dates and which platforms it ran on. No Facebook login, no Meta API token, nothing to
install.

Read this before you buy. Spend and impressions exist only on political and issue ads. A search for a commercial brand comes
back with them empty. If spend
is what you came for, set the ad category to political and issue ads.

| | |
|---|---|
| **Input** | Keywords, advertiser Pages (ID, link or exact name), single ad IDs, or Ad Library URLs |
| **Output** | One row per ad: copy, creatives, call to action, advertiser Page, run dates, platforms, and spend and impressions on political ads |
| **Ceiling** | 20,000 ads per search |
| **Account needed** | None, and no Meta API token |
| **Price** | $0.34 per 1,000 ads on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 14,000 |

### 🔍 What Meta Ad Library Scraper does

The inputs mix freely in one run. Keywords search the way the site's own box does. An advertiser's
Page, as an ID, a link or its exact name, pulls that advertiser's whole set, the better route for
watching a brand. Pasted Ad Library URLs keep their filters, and a single ad's ID returns that ad.

Narrow by country, status, ad category, media type, language, platforms, exact phrase and, for
keywords, advertiser. Sort by most recent, or by impressions on political ads.

The dates are an overlap window: ads running at any point in it come back, including ones that
started earlier. Either end works alone, and both take a span like "30 days".

`includeAdDetails` adds the EU transparency block, the advertiser's Page facts and political spend:
one extra request per ad, no extra charge. Two more switches read each
Page's About tab, its history and its confirmed owner. **Count only** returns ad counts, not ads.

### 📋 What data you get from each Meta ad

| What you get | Field |
|---|---|
| The ad's id, its Ad Library link, and whether it is still running | `adArchiveId`, `adLibraryUrl`, `isActive` |
| The advertiser Page | `pageId`, `pageName`, `pageUrl`, `pageProfilePictureUrl`, `pageCategories`, `pageLikeCount` |
| When it ran | `startDate`, `endDate`, `startDateTimestamp`, `endDateTimestamp` |
| Where it ran | `publisherPlatforms` |
| The ad copy | `adTitle`, `adBody`, `caption`, `linkDescription` |
| The link and the button | `linkUrl`, `ctaText`, `ctaType` |
| The creative | `displayFormat`, `images`, `videos`, `cards` |
| Spend, impressions and reach, on political and issue ads only | `impressions`, `spend`, `currency`, `reachEstimate`, `byline`, `disclaimerLabel`, `categories` |
| Variants of the same ad | `collationCount`, `collationId` |
| AI and sensitive-content flags | `containsDigitalCreatedMedia`, `containsSensitiveContent` |
| Which of your inputs found it | `searchTerm`, `country` |
| How long it ran, and how many ads Meta reports for your search | `daysRunning`, `searchMatchCount` |
| Spend, impressions and reach as numbers | `spendLower`, `spendUpper`, `impressionsLower`, `impressionsUpper`, `impressionsIndex`, `reachLower`, `reachUpper` |
| Carousel size and the variants Meta grouped into the ad | `cardCount`, `extraTexts`, `extraImages`, `extraLinks`, `extraVideos` |
| The partner Page on a paid partnership | `brandedContentPartner` |
| Meta's labels | `stateMediaLabel`, `pageIsDeleted` |
| The EU transparency block, when you switch it on | `ageGenderBreakdown`, `regionReach`, `advertiserAbout`, `advertiserInstagramUsername`, `advertiserInstagramFollowers`, `lifetimeSpendByDisclaimer`, `euTotalReach`, `euTargetedLocations`, `euTargetedGender`, `euTargetedAgeMin`, `euTargetedAgeMax`, `advertiserInstagramVerified`, `advertiserIsProfilePage`, `advertiserPageVerification`, `advertiserPageCoverPhotoUrl` |
| Page history and admin countries, when you switch it on | `pageCreatedDate`, `pageHistory`, `pageAdminCount`, `pageAdminCountries`, `pageStateMediaCountry` |
| The Page's confirmed owner, when you switch it on | `pageOwnerName`, `pageOwnerAddress`, `pageOwnerPhone` |
| Product details from the pages the ad links to, an optional add-on | `products` |
| Your own label, when you set one | `runLabel` |

### ▶️ How to scrape the Meta Ad Library

1. Open [Meta Ad Library Scraper](https://apify.com/dami_studio/meta-ads-library-scraper) and click
   **Try for free**.
2. Type a brand into **Search terms**, or paste an Ad Library link into **Ad Library URLs**.
3. Set **Country** and **Ad status**. Switch **Ad category** to political and issue ads if you need
   spend and impressions.
4. Set **Max ads per search**. Start around 25 to see what the rows look like.
5. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it from the Apify API.

Start it with nothing filled in and you get one labelled notice row, which is not charged.

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

**$0.34 per 1,000 ads** on the Free plan, a little less on paid plans. On the free plan, the $5
Apify gives you each month covers about 14,000 ads.

One billed row per ad delivered, and a count row bills like one. Notice rows, searches that find
nothing, `includeAdDetails` and the two Page switches add nothing. Product data is an optional
add-on, billed from 9 November 2026: each product listed on an ad row costs $0.34 per 1,000 on the
Free plan, a little less on paid plans. Before that day it is free.

### 📥 What you give it

```json
{
  "searchTerms": ["nike"],
  "country": "US",
  "activeStatus": "active",
  "adType": "all",
  "maxResults": 250
}
```

| Field | Default | What it is |
|---|---|---|
| `searchTerms` | none, the box starts at `nike` | Keywords to search, one per line. One scrape per term. |
| `advertiserPageIds` | none | Numeric Facebook Page IDs, to pull one advertiser's full set. |
| `advertiserPages` | none | Page links such as `facebook.com/nike`, or a Page's exact name. Matched exactly or not at all. |
| `adLibraryUrls` | none | `facebook.com/ads/library/` links pasted from the browser. A one-ad link (`?id=`) returns that ad. |
| `adIds` | none | Single ad IDs or one-ad links. One row each. |
| `country` | `US` | Two-letter code for the Ad Library edition, or `ALL`. A code the Ad Library does not offer stops the run with a free notice; `UK` is read as `GB`. |
| `activeStatus` | `active` | Also `inactive`, or `all` for both. |
| `adType` | `all` | `political_and_issue_ads` for the ads that carry spend and impressions; also `housing_ads`, `employment_ads`, `credit_ads`, which Meta offers in some countries only. |
| `mediaType` | `all` | Also `image`, `video`, `meme`, `image_and_meme` or `none`. |
| `searchType` | `keyword_unordered` | Words in any order, or `keyword_exact_phrase` for the phrase as typed. |
| `limitToPageIds` | none | Page IDs; your keywords then only return those advertisers' ads. |
| `publisherPlatforms` | all of them | Facebook, Instagram, Messenger, Audience Network, Threads, WhatsApp. |
| `startDate`, `endDate` | none | `YYYY-MM-DD`, or a span back from today such as `7 days` or `3 months`. An overlap window; either end works alone. |
| `sortBy` | `default` | `most_recent`, or `impressions_high`, which only reorders political and issue ads. |
| `adLanguage` | none | A two-letter code such as `es`: only ads Meta files under that language. |
| `includeAdDetails` | `false` | The EU transparency and advertiser block. No extra charge. |
| `includePageTransparency` | `false` | Page creation date, name changes, admin count and countries. One request per Page. No extra charge. |
| `includePageOwner` | `false` | The business Meta lists as the Page's confirmed owner, with its city and phone where shown. No extra charge. |
| `includeProductData` | `false` | Optional add-on: name, price, currency, brand, image and rating from up to 10 product pages per ad. |
| `countOnly` | `false` | One row per search or Page with Meta's ad count, no ad rows. |
| `dedupeAcrossSearches` | `false` | An ad two searches both find comes back once. |
| `maxResults` | `100` | Ads per search term, Page ID or URL, up to 20,000. |
| `maxResultsTotal` | none | A cap for the whole run, on top of the cap per search. |
| `runLabel` | none | Text copied into every row. |
| `useApifyDatacenterProxy` | `false` | Optional. |
| `proxyConfiguration` | leave it alone | Optional. Only if you want the run to go out through a particular network of your own. |

### 📤 What you get back

A real row from a run on 20 September 2026, with the long Meta media links cut short:

```json
{
  "adArchiveId": "925321173274919",
  "adLibraryUrl": "https://www.facebook.com/ads/library/?id=925321173274919",
  "isActive": true,
  "pageId": "89516513179",
  "pageName": "Nordstrom Rack",
  "pageUrl": "https://www.facebook.com/NordstromRack/",
  "pageProfilePictureUrl": "https://scontent-lga3-3.xx.fbcdn.net/v/t39.35426-6/764807066_...",
  "pageCategories": ["Clothing (Brand)"],
  "pageLikeCount": 2518019,
  "startDate": "2026-08-03",
  "endDate": "2026-09-19",
  "startDateTimestamp": 1785740400,
  "endDateTimestamp": 1789801200,
  "publisherPlatforms": ["FACEBOOK", "INSTAGRAM", "MESSENGER"],
  "adTitle": "Fast & Free Pickup",
  "adBody": "Buy online and pick up in store for free! Get up to 70% off Nike, Vince, Madewell, adidas and more.",
  "caption": "Nordstromrack.com",
  "linkUrl": "https://fb.com/canvas_doc/1072291375736811",
  "linkDescription": "Fast & Free Pickup",
  "ctaText": "Learn more",
  "ctaType": "LEARN_MORE",
  "displayFormat": "VIDEO",
  "images": [],
  "videos": [{ "videoHdUrl": "https://video-lga3-3.xx.fbcdn.net/o1/v/t2/f2/m366/...", "videoSdUrl": "...", "videoPreviewImageUrl": "..." }],
  "cards": [],
  "impressions": null,
  "spend": null,
  "currency": null,
  "reachEstimate": null,
  "byline": null,
  "disclaimerLabel": null,
  "categories": [],
  "collationCount": 2,
  "collationId": "1022906600523558",
  "containsDigitalCreatedMedia": false,
  "containsSensitiveContent": false,
  "searchTerm": "nike",
  "country": "US",
  "scrapedAt": "2026-09-20T07:04:56.805Z"
}
```

| Field | How to read it |
|---|---|
| `adBody`, `adTitle`, `caption`, `linkDescription` | The ad copy as Meta renders it. A plain image ad often has no `adTitle`. |
| `images`, `videos`, `cards` | Creative URLs on Meta's own media hosts. `cards` holds the slides of a carousel, each with its own copy and link. |
| `displayFormat` | `VIDEO`, `IMAGE`, `DPA` and so on, which is the quickest way to sort creative types. |
| `impressions`, `spend`, `reachEstimate` | Meta's own published transparency figures, only on political and issue ads. They come as bands, strings like `">1M"` and `"$450K - $500K"`, never an exact number. |
| `spendLower` to `reachUpper` | The two ends of each band as numbers, currency left in `currency`. `"<100"` gives 0 and 100; an open band like `">1M"` gives 1000000 and `null`. `impressionsIndex` is Meta's own band number, so it sorts. All `null` where Meta publishes no band. |
| `collationCount`, `collationId` | Meta groups near-identical variants of one ad. This says how many are in the group. |
| `extraTexts`, `extraImages`, `extraLinks`, `extraVideos` | The other copy, images, links and videos inside that group, where Meta lists them. Empty on most ads. |
| `daysRunning` | Whole days from the start date to the end date, or to the moment of the run for an ad still running. |
| `searchMatchCount` | How many ads Meta reports for the search. The count stops at 50,001. |
| `stateMediaLabel`, `pageStateMediaCountry` | Meta's state-controlled media label, such as `"China state-controlled media"`. `null` on almost every ad. |
| `pageAdminCountries` | Countries of the people who manage the Page, with a count each. Left empty when one person manages it, so only `pageAdminCount` shows. |
| `pageOwnerName`, `pageOwnerAddress`, `pageOwnerPhone` | The business Meta lists as the Page's confirmed owner, as shown in the Page's transparency panel. Each is empty when the panel does not show it. |
| `products` | One entry per product page that published product details: `url`, `name`, `price`, `currency`, `brand`, `imageUrl`, `rating`, `ratingCount`. |
| `searchTerm` | Which of your inputs produced this row: the term, `page:<id>`, the Page or ad link you gave, or the pasted URL. |

### 🧾 Reading the output

| Row | How to spot it | Billed |
|---|---|---|
| An ad | It has an `adArchiveId` and no `_sample` key | yes |
| A count, with **Count only** on | `rowType: "count"`, `searchTerm` and `searchMatchCount` | yes |
| A notice | `_sample: true`, `_charged: false` and a `_notice` line | no |
| The time-limit notice | A notice that also carries `_code: "TIME_LIMIT"` and `adsDelivered` | no |
| A stop on an unexpected error | A notice with `_code: "UNEXPECTED_ERROR"`. The ads before it are kept | no |
| A refused input | A notice with `_code: "BAD_INPUT"` and the `field` to fix. Nothing was searched | no |

Ad rows carry no `_charged` field. The flag is only on notice rows, to mark them as not real
results. Filter on `_sample` being absent.

Notices tell you something without billing you. They look alike, so read the `_notice` text.

A run near its time limit stops on its own and keeps what it delivered. It ends as succeeded even if no ad came back.

The Console has three dataset views: **overview**, **creatives** and **political**.

### 💡 What people use it for

- Watching a rival brand's creative: their Page ID on a schedule, diffing on `adArchiveId`.
- Building a swipe file of ad copy and creatives for one product category, by keyword.
- Election and issue research, where Meta's published spend and impression bands are the point.
- Checking which platforms a campaign actually ran on, from `publisherPlatforms`.

From an ad to the advertiser's front door, in three steps:

1. Search a product keyword here and group the rows on `pageId`.
2. Copy the `pageUrl` of the advertisers you care about.
3. Paste them into
   [Facebook Page Contact Info Scraper](https://apify.com/dami_studio/facebook-page-contact-info-scraper)
   for each Page's website, e-mail and phone.

### 🚧 What it does not do

- **No spend or impressions on commercial ads.**
- **No exact figures.** Even on political ads you get bands, not numbers.
- **No performance data.** No click-through rate, no conversions, no return on spend.
- **No creative files.** You get URLs on Meta's media hosts, and those expire. Fetch them soon.
- **A broad keyword will not hand you 50,000 ads.** Search by advertiser Page ID for depth.
- **No targeting data outside the EU.**
- **A Page link must match exactly.** A handle unlike the Page's name can miss. You then get a free
  notice, and the Page ID always works.
- **Product data only where shops publish it.** App store links, pages without product details and
  shops that refuse a plain request give nothing.
- **If the `includeAdDetails` request fails, the row still arrives** without those fields, and is
  still billed as an ad. Check for the field rather than assuming it is there.
- **Very large runs can hit the platform time limit.** Split a big job across several runs.
- It is not an official Meta tool. Dami's Studio is independent and is not affiliated with or endorsed by Meta, Facebook or Instagram, or by any other company named on this page.

### 🧭 Which ads scraper do you need?

| If you want | Use |
|---|---|
| Facebook and Instagram ads from Meta's Ad Library | This one |
| The advertiser's Page contact details: website, e-mail, phone | [Facebook Page Contact Info Scraper](https://apify.com/dami_studio/facebook-page-contact-info-scraper) |
| The advertiser's recent organic Facebook posts | [Facebook Posts Scraper](https://apify.com/dami_studio/facebook-posts-scraper) |
| The advertiser's Instagram profile and follower counts | [Instagram Profile Scraper](https://apify.com/dami_studio/instagram-profile-scraper) |
| Ads running on Google Search, YouTube and Display | [Google Ads Transparency Scraper](https://apify.com/dami_studio/google-ads-transparency-scraper) |
| Ads in TikTok's Ad Library, EEA and UK | [TikTok Ads Scraper](https://apify.com/dami_studio/tiktok-ads-scraper) |

### ❓ Questions people ask

#### Do I need a Meta API token or a Facebook login?

No. This reads the public archive.

#### How do I find an advertiser's Page ID?

You often don't need it: paste the Page's link into `advertiserPages`. Otherwise read the
`view_all_page_id=` value out of the Ad Library's address bar.

#### Why is spend empty?

Because the search covered all ads. Switch **Ad category** to political and issue ads and it fills
in.

#### Does it cover Instagram ads?

Yes, one archive covers every Meta surface. Set `publisherPlatforms` to Instagram to keep only
those.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/meta-ads-library-scraper/api/python) has code for Python, JavaScript and
the CLI. MCP clients like Claude and ChatGPT use
`https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/meta-ads-library-scraper`; coding assistants can load
[our skill file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-facebook-meta-SKILL.md).
All three bill your Apify account at the same price.

#### Is scraping the Meta Ad Library legal?

Meta publishes it as a public archive, and this reads it as a signed-out visitor sees it: no account,
nothing private. Personal data in it, such as a disclaimer name or a Page owner's phone, falls under
GDPR, PIPEDA and similar laws, so keep only what you need, and mind anti-spam laws before contacting
anyone. Meta's terms still apply to your use. Apify's
[write-up on scraping and the law](https://blog.apify.com/is-web-scraping-legal/) is a good starting
point, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page. Send the search term, Page ID or URL you used and the run
ID. The `_notice` line on the notice row usually names the problem on its own.

# Actor input Schema

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

Keywords to search the Ad Library for, e.g. "nike" or "solar panels". One scrape per term.

## `advertiserPageIds` (type: `array`):

Numeric Facebook Page IDs to pull every ad for, e.g. "15087023444" (Nike). Use this instead of a keyword to get one advertiser's full ad set.

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

Paste facebook.com/ads/library/ URLs straight from your browser. Filters in the URL (country, ad type, active status, platforms, dates, language) are read automatically. A link to one ad (with ?id= in it) returns that ad as one row, billed like any other ad.

## `advertiserPages` (type: `array`):

Facebook Page links such as facebook.com/nike, or a Page name exactly as Facebook shows it. Each one is matched to the Page in the Ad Library's own Page suggestions, by the handle in the link or by the exact name, and then searched like a Page ID. Nothing close-but-different is used: a Page that does not match exactly gets a free notice row instead.

## `adIds` (type: `array`):

Ad Library IDs such as 1550534860016399, or ad links with ?id= in them. Each one comes back as one row, billed like any other ad. An ad that is no longer in the Ad Library gets a free notice row.

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

Two-letter country code for the Ad Library edition to search, or ALL for every country. Only codes the Ad Library itself offers are accepted; a typo stops the run before anything is searched, with a free notice. UK is read as GB, the code Meta uses.

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

How many ads to return for each search term, page ID or URL. Meta serves 30 ads up front and 10 per page after that, so 1,000 ads takes about 100 requests and 2-3 minutes.

## `maxResultsTotal` (type: `integer`):

Optional. Stops the whole run at this many ads, on top of the cap per search. Ten search terms at 100 each is up to 1,000 ads; set this to 300 and the run stops at 300. Leave empty for no run-wide cap.

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

Return only ads currently running, only ads that have stopped, or both.

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

All ads, or only political & issue ads. Impressions, spend, currency, reach and byline are published by Meta for political & issue ads ONLY, pick "Political and issue ads" if you need them. On a general search they are empty on ~98% of rows. Housing, employment and credit ads are the categories Meta keeps for those ad types; Meta offers them in some countries only, and any row Meta sends outside the chosen category is left out and not charged.

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

Filter ads by the kind of creative they use.

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

Match the words in any order, or as an exact phrase.

## `limitToPageIds` (type: `array`):

Optional. Numeric Page IDs; your search terms then only return ads from these advertisers. Applies to Search terms, not to Page IDs or pasted URLs. Any row from another advertiser is left out and not charged.

## `publisherPlatforms` (type: `array`):

Only return ads shown on these Meta platforms. Leave empty for all of them.

## `startDate` (type: `string`):

YYYY-MM-DD, or a span back from today such as "7 days", "last 3 months" or "1 year" (counted in UTC each time the run starts, which suits a scheduled run). Meta treats this as an OVERLAP window: you get ads that were running at some point in it, not only ads that started in it. Can be set on its own: then you get ads running on or after this date. Measured: a Nike search went from 50,001 matches to 179 with a 2025-01-01 to 2025-03-31 window.

## `endDate` (type: `string`):

YYYY-MM-DD, or a span back from today such as "yesterday" or "30 days". The other end of the overlap window. Can be set on its own: then you get ads that started on or before this date.

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

Meta's own order by default. "Most recent" puts the newest ads first. "Most impressions" puts the most seen first, and works only where Meta publishes impressions, which means political and issue ads; on other ads the order stays Meta's own. With a cap per search, the order decides which ads you get.

## `adLanguage` (type: `string`):

Optional. A two-letter language code such as en, es or de: only ads Meta files under that language. Measured on a US Nike search: en kept 10,969 of 21,568 matches, es kept 342.

## `includeAdDetails` (type: `boolean`):

Adds age/gender reach breakdown, reach by region, advertiser page category, page likes, linked Instagram username and follower count, and lifetime political spend. For ads shown in the EU it also adds the total EU reach and who the ad targeted: locations, gender and ages. Also the Page's cover image, its verified status, whether it is a personal profile, and whether its Instagram account is verified. One extra request per ad, so a 1,000-ad run roughly doubles in length. No extra charge.

## `includePageTransparency` (type: `boolean`):

Adds the advertiser Page's transparency facts from the Ad Library: the date the Page was created, its name changes and merges, how many people manage it and in which countries (left out when one person manages it, so only the count shows), and a state-controlled media label when Meta gives one. One extra request per advertiser Page, not per ad. No extra charge.

## `includePageOwner` (type: `boolean`):

Adds the organisation Meta lists as the confirmed owner of the advertiser Page, as published in the Page's transparency panel: its name, and its city, state, postal code, country and phone where Meta shows them. Business details only, and often empty. Uses the same request as the Page history. No extra charge.

## `includeProductData` (type: `boolean`):

Optional add-on. Reads the product pages an ad links to (up to 10 per ad, on the advertiser's own site) and adds what each page publishes about the product: name, price, currency, brand, image link and rating. Only pages that publish product details are listed; app store links and Facebook links are skipped, and so is any shop that does not answer a plain request.

## `countOnly` (type: `boolean`):

Returns one row per search term, Page ID, Page or URL with the number of matching ads Meta reports (searchMatchCount), and no ad rows. Each count row is billed like one ad. Meta's count stops at 50,001.

## `dedupeAcrossSearches` (type: `boolean`):

When two of your searches find the same ad, it is delivered and charged once, under the first search that found it. Off by default, so each search returns its full set.

## `runLabel` (type: `string`):

Optional. Any text, copied into a runLabel column on every row, so batches from scheduled runs are easy to tell apart in one sheet.

## `useApifyDatacenterProxy` (type: `boolean`):

Optional. Adds Apify Proxy as an extra route for the run. Off by default.

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

Optional. Leave this alone unless you need the run to go out through a particular network. Your own proxy servers are used exactly as given.

## Actor input object example

```json
{
  "searchTerms": [
    "nike",
    "adidas"
  ],
  "country": "DE",
  "maxResults": 100,
  "activeStatus": "active",
  "adType": "all",
  "mediaType": "all",
  "searchType": "keyword_unordered",
  "publisherPlatforms": [],
  "sortBy": "default",
  "includeAdDetails": false,
  "includePageTransparency": false,
  "includePageOwner": false,
  "includeProductData": false,
  "countOnly": false,
  "dedupeAcrossSearches": false,
  "useApifyDatacenterProxy": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per ad from the Facebook and Instagram Ad Library: archive ID, advertiser page and page ID, whether it is still running, start and end dates, the headline and body copy, call to action, destination link, format, and which platforms it ran on. Creative fields carry the image, video and carousel card URLs. Political and issue ads add the byline, disclaimer, impression and spend ranges. A search Meta returns nothing for gets a labelled sample row instead, and nothing is charged for it.

# 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": [
        "nike"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/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 = { "searchTerms": ["nike"] }

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/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 '{
  "searchTerms": [
    "nike"
  ]
}' |
apify call dami_studio/meta-ads-library-scraper --silent --output-dataset

```

## MCP server setup

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