# Meta (Facebook & Instagram) Ads Library Scraper (`jmlp/meta-ad-library-scraper`) Actor

Scrape all Facebook & Instagram ads of any page from the Meta Ad Library. Extract ad creatives, texts, titles, CTAs, image & HD video URLs, spend, reach and dates. Fast parallel scraping, multiple pages per run, date filters, no login or API key. Daily checks to ensure quality!

- **URL**: https://apify.com/jmlp/meta-ad-library-scraper.md
- **Developed by:** [Mary Lou](https://apify.com/jmlp) (community)
- **Categories:** Social media, Automation, Developer tools
- **Stats:** 10 total users, 7 monthly users, 99.9% runs succeeded, 5 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.15 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Meta (Facebook & Instagram) Ads Library Scraper

**See every ad your competitors are running — or every ad mentioning your
keyword — and download it all as a spreadsheet.**

Facebook's [Ad Library](https://www.facebook.com/ads/library/) shows every ad any
page is running on Facebook and Instagram. It's public, but you can only scroll it
one ad at a time in a browser, and you can't export anything.

This scraper does that scrolling for you. Give it a page (or ten), or just a
keyword to search across every advertiser, press start, and you get a clean table
of every ad: the ad copy, the headline, the button, the
landing page link, and direct links to every image and video — ready to open in
Excel, Google Sheets, or push into your own tools.

**No Facebook account. No API key. No coding.**

***

### What you can use it for

- 🔍 **Competitor research** — see exactly which ads a competitor is running right now, and which ones they've been running for months (long-running ads are usually their winners)
- 🎨 **Creative inspiration** — download competitor images and videos to study hooks, formats and angles before your next campaign
- 📈 **Track over time** — run it on a schedule and watch when competitors launch, pause, or scale creatives
- 📊 **Market research** — search a keyword and pull ads from an entire industry at once, whoever is running them, and analyse messaging trends
- 🧲 **Find advertisers you don't know yet** — a keyword search surfaces every page advertising on that topic, not just the brands you already track
- 🕵️ **Ad spying at scale** — thousands of ads across many brands in one run, instead of endless manual scrolling

***

### How to use it — step by step

**1. Find the brand in the Ad Library**

Go to [facebook.com/ads/library](https://www.facebook.com/ads/library/), pick your
country, choose **All ads**, and search for the brand name. Click the brand to open
its page of ads.

**2. Copy the URL from your browser's address bar**

It will look something like this:

```
https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&search_type=page&view_all_page_id=116700222203350
```

**3. Paste it into the "Ad Library URLs" field**

That's the only thing you have to fill in. You can paste several URLs — one per
line — to scrape multiple brands in the same run.

**4. (Optional) Set a limit while you're testing**

Put `50` in **Max ads** for your first run so you can see the output quickly and
cheaply. Remove it later to get everything.

**5. Click Start**

Results appear in the **Storage → Dataset** tab as they're scraped. When it's
finished, use the **Export** button for CSV, Excel, JSON, or copy the API link.

> **Don't want to bother with URLs?** Just put the numeric page ID into the
> **Page IDs** field instead. It's the `view_all_page_id=` number in the Ad
> Library URL.

#### Searching by keyword instead of by page

Don't have a specific brand in mind? Type a word or phrase into the **Keyword**
field (or several into **Keywords**) and the scraper searches the whole Ad
Library, returning matching ads from every advertiser — exactly like typing it
into the Ad Library search box. Pasting a keyword search URL from your browser
(the ones with `q=` in them) works too.

Two match types, set with **Keyword match type**:

- **Any order** (default) — the words can appear anywhere in the ad, in any order. Same as the Ad Library's own default.
- **Exact phrase** — the words must appear together, in that order.

Keyword searches can match *tens of thousands* of ads, so set **Max ads** and
narrow the **Country** and dates on your first run. And clear the **Page ID**
field if it's prefilled — otherwise that page gets scraped as well.

***

### Input options

Everything is optional except telling it *what* to scrape — a page or a keyword.

| Setting | What it does |
| --- | --- |
| **Ad Library URLs** | Paste one or more URLs copied from your browser — page URLs (`view_all_page_id=`) or keyword search URLs (`q=`). Each URL keeps its own filters (country, dates, active status). **This is the easiest way to use the scraper.** |
| **Ad Library URL** | Same thing, for a single URL. |
| **Page IDs** | Numeric page IDs instead of full URLs, if you already have them. |
| **Page ID** | A single numeric page ID. Clear it if you're searching by keyword. |
| **Keyword** | Search every advertiser's ads for this word or phrase, instead of scraping one page. |
| **Keywords** | Several keywords in one run, all using the settings below. |
| **Keyword match type** | `Any order` (default) or `Exact phrase`. Applies to keyword searches only. |
| **Active status** | `All ads` = the page's entire history (often *far* more than the Ad Library shows by default). `Active only` = ads running right now. `Inactive only` = ads that have stopped. |
| **Country** | Limit to ads shown in one country, or `ALL` for everywhere. |
| **Start date from / to** | Only ads that were **running during** this window. Note this is not "created in" — an ad that started last year and is still running today will show up in a window covering today. |
| **Max ads** | Stop after this many ads in total. Great for testing, and for keeping costs predictable. Leave empty to get everything. |
| **Max concurrency** | How many pages/date-ranges to scrape at the same time. `1` is the safe default. `3` is roughly 3× faster for big brands. Higher = faster but slightly more likely to be rate-limited. |
| **Delay between pages (ms)** | Politeness pause between requests. Default `300` works well. Increase to `1000`+ if you ever see captcha warnings in the log. |
| **Raw output** | Off by default. Turn on only if you want Facebook's untouched raw data structure. |
| **Proxy** | Leave on **Residential** — this is required for reliable results. |

#### Example: one competitor, everything they've ever run

```json
{
  "adLibraryUrls": [
    "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&search_type=page&view_all_page_id=116700222203350"
  ]
}
```

#### Example: three competitors' currently-active ads, fast

```json
{
  "adLibraryUrls": [
    "https://www.facebook.com/ads/library/?active_status=active&country=ALL&search_type=page&view_all_page_id=116700222203350",
    "https://www.facebook.com/ads/library/?active_status=active&country=ALL&search_type=page&view_all_page_id=102921707941613",
    "https://www.facebook.com/ads/library/?active_status=active&country=US&search_type=page&view_all_page_id=688155977722463"
  ],
  "maxConcurrency": 3
}
```

#### Example: every advertiser running ads about a topic

```json
{
  "keyword": "running shoes",
  "searchType": "keyword_unordered",
  "country": "US",
  "activeStatus": "active",
  "maxAds": 500
}
```

#### Example: several keywords, exact phrases only

```json
{
  "keywords": ["free trial", "money back guarantee"],
  "searchType": "keyword_exact_phrase",
  "country": "GB",
  "maxAds": 1000,
  "maxConcurrency": 3
}
```

#### Example: quick, cheap test run

```json
{
  "pageIDs": ["116700222203350"],
  "activeStatus": "active",
  "maxAds": 50
}
```

***

### What you get

One row per ad. Here's a real (shortened) example:

```json
{
  "ad_archive_id": "1227225301509075",
  "page_id": "116700222203350",
  "page_name": "BetterMe",
  "is_active": true,
  "start_date": 1723359600,
  "end_date": 1787468400,
  "publisher_platform": ["FACEBOOK", "INSTAGRAM", "AUDIENCE_NETWORK", "MESSENGER"],
  "collation_count": 1,
  "snapshot": {
    "title": "Entrenador personal 24/7 en la aplicación",
    "body": { "text": "Plan de 28 Días para lograr un peso saludable en casa." },
    "caption": "itunes.apple.com",
    "cta_text": "Install now",
    "cta_type": "INSTALL_MOBILE_APP",
    "link_url": "http://itunes.apple.com/app/id1264546236",
    "display_format": "VIDEO",
    "videos": [{ "video_hd_url": "https://...", "video_preview_image_url": "https://..." }],
    "images": [],
    "page_like_count": 6197887,
    "page_categories": ["Health/beauty"]
  }
}
```

**The fields you'll care about most:**

| Field | What it is |
| --- | --- |
| `snapshot.body.text` | The main ad copy |
| `snapshot.title` | The headline |
| `snapshot.cta_text` / `cta_type` | The button ("Shop now", "Install now"…) |
| `snapshot.link_url` | Where the ad sends people |
| `snapshot.images[]` | Direct image URLs (full resolution) |
| `snapshot.videos[]` | Direct video URLs (HD, SD, and thumbnail) |
| `snapshot.display_format` | `VIDEO`, `IMAGE`, `DCO`, carousel… |
| `is_active` | Is it still running right now |
| `start_date` / `end_date` | When it ran (Unix timestamps) |
| `publisher_platform` | Facebook, Instagram, Messenger, Threads… |
| `collation_count` | How many variants of this ad exist |
| `page_name`, `snapshot.page_like_count` | Who's running it, and how big they are |
| `spend`, `currency`, `reach_estimate` | Only filled for political/social-issue ads, where Meta discloses it. Blank for normal commercial ads — that's a Facebook limitation, not a scraper one. |

Every row has the same columns, so nothing breaks when you open it in a
spreadsheet. Fields Facebook doesn't provide come back empty rather than missing.

**Export:** CSV, Excel, JSON, XML, or an API endpoint — from the Export button on
the run's Dataset tab.

***

### Speed and limits

Facebook hands out ads in small batches, so time scales with the number of ads:

| Ads on the page | Roughly how long |
| --- | --- |
| 200 | ~30 seconds |
| 1,000 | ~2–3 minutes |
| 10,000 | ~10 minutes at Max concurrency 3 |

Turning **Max concurrency** up to 3–4 is the single best way to speed up big
brands — the scraper automatically splits their history into date ranges and
works on several at once.

**If a run is interrupted** (you abort it, the platform restarts it, or Facebook
rate-limits part of it), progress is saved automatically. A platform restart
resumes by itself. If you start a *new* run afterwards, it scrapes fresh by
default so your dataset is always complete in one place — or turn on
**Continue from last run** to fetch only what the interrupted run missed (its
ads stay in that run's dataset).

***

### Cost

**$0.15 per 1,000 ads, plus Apify platform usage.** Nothing for runs that find
nothing.

Most Ad Library scrapers roll infrastructure into one flat per-result price, with
a markup baked in. This one keeps the two separate: you pay $0.15 per 1,000 ads
for the scraping itself, and Apify bills the compute and proxy traffic at *your
plan's own rate* — the same credits your subscription already includes.

| What you scrape | Scraper charge | + Platform usage | Roughly |
| --- | --- | --- | --- |
| 200 ads (one brand's active ads) | $0.03 | ~$0.02 | **~$0.05** |
| 1,000 ads | $0.15 | ~$0.10 | **~$0.25** |
| 10,000 ads | $1.50 | ~$1.00 | **~$2.50** |
| 100,000 ads | $15.00 | ~$10.00 | **~$25.00** |

Usage figures are measured from real runs on the **Free** plan rate and rounded up.
Expect roughly $0.06–$0.12 per 1,000 ads depending on run shape — long history
scrapes split into more parallel sessions and use a little more proxy traffic than
a quick active-ads snapshot.

#### Usage gets cheaper as your plan goes up

The $0.15 scraper charge is the same on every plan. The usage half isn't — Apify
discounts it at higher tiers, so the same scrape costs less on a bigger plan:

| Plan | Compute | Residential proxy |
| --- | --- | --- |
| Free | $0.20 / CU | $8.00 / GB |
| Starter | $0.20 / CU | $8.00 / GB |
| Scale | $0.16 / CU | $7.50 / GB |
| Business | $0.13 / CU | $7.00 / GB |

Platform usage is drawn from the prepaid usage credit your plan already includes,
so on a paid plan a good part of it is money you've spent regardless. See
[Apify pricing](https://apify.com/pricing) for the full rate card.

#### Keeping it predictable

- **Set `Max ads`.** The scraper stops exactly there. Perfect for testing, and for
  putting a hard ceiling on any single run.
- **Set a maximum cost per run** (in the run options). The scraper checks this
  before it starts and stops as soon as it has delivered what your budget covers —
  it will never quietly keep scraping past what you're paying for.

A whole-archive scrape of a very large advertiser can be tens of thousands of ads,
so start with `Active status: Active only` or a `Max ads` limit if you just want a
snapshot of what's running now.

***

### Frequently asked questions

**Do I need a Facebook account or login?**
No. The Ad Library is public information, and the scraper needs no account,
password, cookies, or API token.

**Do I need the official Meta Ad Library API?**
No — and it wouldn't help you anyway. Meta's official API only covers political
and social-issue ads in most countries. This scraper returns *all* ad categories,
with the full creatives and media URLs.

**Is this legal?**
The Ad Library is Meta's own public ad-transparency tool, published deliberately
so anyone can inspect advertising. This scraper only collects what's shown there —
no private data, no logged-in data, no personal information. As with any scraping,
you're responsible for how you use the data in your own jurisdiction.

**Why do I get way more ads than the Ad Library page showed me?**
The Ad Library UI usually shows *active* ads by default. The scraper defaults to
**All ads**, which includes everything the page has ever run — often 10–100× more.
Set **Active status** to `Active only` if you want to match what you saw on screen.

**I set a date range but I'm getting ads that started before it. Why?**
Facebook's date filter means "was this ad *running* during this window", not "did
it *start* in this window". A long-running evergreen ad legitimately matches every
window it was live in. This is Facebook's behaviour, and the same thing happens in
the Ad Library UI.

**What about `spend` and `reach`?**
Meta only discloses spend and reach for political and social-issue ads. For normal
commercial ads those fields come back empty — nobody can scrape numbers Facebook
doesn't publish.

**Can I scrape several competitors at once?**
Yes. Paste multiple URLs into **Ad Library URLs** and set **Max concurrency** to 3.
They'll be scraped in parallel into one dataset, and **Max ads** applies across all
of them combined.

**Can I run this automatically every day/week?**
Yes — use Apify **Schedules** on this Actor. A common setup is a daily run per
competitor, so you can diff the results and spot new creatives.

**How do I get the images and videos themselves?**
The dataset gives you direct URLs. Open them in a browser, or feed the column to
any bulk downloader. The scraper doesn't re-host media files.

**The log shows a captcha warning — what do I do?**
That means Facebook rate-limited part of the run. Progress is saved, so re-run to
continue. To avoid it, lower **Max concurrency** or raise **Delay between pages**.

**Can it search by keyword instead of by page?**
Yes. Put a word or phrase in the **Keyword** field (or paste a keyword search URL
with `q=` in it) and it returns matching ads from every advertiser, with the same
output columns as a page scrape. Choose **Any order** or **Exact phrase** with
**Keyword match type**. Keyword searches can match tens of thousands of ads, so
set **Max ads** and narrow the country and dates first.

**Can I mix pages and keywords in one run?**
Yes — fill in both, and every target is scraped in the same run (in parallel when
**Max concurrency** is above 1). **Max ads** counts across all of them together.

***

### Support

Found a bug, need a field that isn't there, or want a custom scraping setup?
Open an issue on the Actor's **Issues** tab and I'll take a look.

# Actor input Schema

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

One or more full Ad Library URLs copied from the browser - each is scraped in the same run (in parallel when Max concurrency > 1). Both kinds work: a page URL (view\_all\_page\_id=...) and a keyword search URL (q=...\&search\_type=keyword\_unordered). Each URL brings its own target, dates, country and active status; the individual fields below fill in whatever a URL does not carry. Max ads applies globally across all of them.

## `adLibraryUrl` (type: `string`):

Paste a single Ad Library URL - either a page URL (view\_all\_page\_id=...) or a keyword search URL (q=...). Its target, dates, country and active status take precedence over the individual fields below. Other URL parameters (ad type, media type, sort order) are ignored - the actor pins those to the browser defaults.

## `pageIDs` (type: `array`):

Multiple numeric page ids to scrape in one run, all using the dates/country/active status set below. Scrape keywords instead with the Keywords field.

## `pageID` (type: `string`):

Numeric Facebook page id, the view\_all\_page\_id value from the Ad Library URL. Not needed when an Ad Library URL is pasted above, or when you are searching by keyword - clear it in that case, or the page is scraped as well.

## `keyword` (type: `string`):

Search every advertiser's ads for this text instead of scraping one page - the same as typing it into the Ad Library search box. Keyword searches can match tens of thousands of ads, so set Max ads and narrow the country and dates. Runs in addition to any page id set above, so clear Page ID if you only want the keyword.

## `keywords` (type: `array`):

Several keywords to search in one run, each using the search type, dates, country and active status set here. Max ads applies globally across all of them.

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

How the keyword(s) are matched. 'Any order' is the Ad Library's own default and matches the words anywhere in the ad; 'exact phrase' matches the words together, in order. Only applies to keyword searches, not page ids (a pasted URL's own search\_type wins).

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

YYYY-MM-DD. Returns ads that were DELIVERING during the window (measured) - a long-running ad started earlier still matches. Leave empty for the whole archive - be aware that can be tens of thousands of ads. Facebook silently ignores the filter when minDate equals maxDate, so the actor widens such a query by one day.

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

YYYY-MM-DD, inclusive. Same delivery-window semantics as the from-date.

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

Which ads to include. 'Active only' matches what the Ad Library UI shows and keeps first runs quick; 'all' covers the page's entire archive, which can be 10-100x larger.

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

ALL covers every country the ad ran in.

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

Use RESIDENTIAL groups. Datacenter IPs get challenged or blocked.

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

Parallel scraping sessions. Multiple pages/keywords run in parallel and a large target's history is split into date ranges scraped simultaneously, each on its own residential proxy session - roughly linear speedup. Set to 1 only if you need results in strict impression-ranking order, or if you ever see captcha warnings in the log.

## `maxAds` (type: `integer`):

Stop after this many unique ads, counted globally across all pages and keywords. The crawl terminates as soon as the cap is hit, so this caps both runtime and cost. Clear it to scrape absolutely everything with no limit.

## `maxPages` (type: `integer`):

Safety cap while testing, applied to each shard separately. Empty = all.

## `delayMs` (type: `integer`):

Milliseconds to sleep between page requests. The network already adds ~700-1500 ms per page, which is the real floor. Measured: 300 ms scraped a 210-ad page in 25 s with no captcha from a bare IP. Raise it if captchas appear. Takes precedence over the legacy 'delay' (seconds) field.

## `pageSize` (type: `integer`):

Facebook caps pagination at 10 results regardless of this value - measured. Kept at the browser default of 30 so the request looks normal.

## `raw` (type: `boolean`):

Push the unprojected GraphQL ad nodes instead of the fixed output schema. Field names are the same; raw skips the null-filling and key ordering.

## `proxyRotations` (type: `integer`):

Residential exit nodes die without warning. If the run fails before pushing anything, the actor mints a new proxy session and retries this many times.

## `resume` (type: `boolean`):

Save progress every ~30s so a run that gets migrated or restarted by the platform picks up where it stopped, without duplicating anything. No effect on fresh runs.

## `continueFromLastRun` (type: `boolean`):

If your previous run with the same input was interrupted (aborted, failed, or captcha-cut), scrape only what it missed. Note the earlier ads stay in THAT run's dataset - this run's dataset will contain only the remainder. Leave off to always get a complete dataset in one place.

## `shardSplitThreshold` (type: `integer`):

When maxConcurrency > 1, a date range reporting more ads than this is bisected into two shards instead of walked serially. ~1500 ads is about 150 pages, roughly 6 minutes of serial work per shard.

## `docId` (type: `string`):

Leave empty. A stale cached id is detected and refreshed automatically.

## `proxySessionId` (type: `string`):

Pin a specific exit IP, for reproducing a run while debugging. Ignored when maxConcurrency > 1 (every shard needs its own session). Nothing is persisted between runs by default.

## `impersonate` (type: `string`):

curl\_cffi target. Empty string disables impersonation.

## Actor input object example

```json
{
  "pageID": "15087023444",
  "searchType": "keyword_unordered",
  "activeStatus": "active",
  "country": "ALL",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxConcurrency": 4,
  "maxAds": 100000,
  "delayMs": 300,
  "pageSize": 30,
  "raw": false,
  "proxyRotations": 3,
  "resume": true,
  "continueFromLastRun": false,
  "shardSplitThreshold": 1500,
  "impersonate": "chrome146"
}
```

# Actor output Schema

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

One record per unique ad: identity and delivery fields at the top level, and the full creative (copy, title, CTA, landing page, image and video URLs) under `snapshot`.

## `summary` (type: `string`):

How many ads were collected versus how many Facebook reported, which pages were covered, how the work was split into shards, and whether anything was cut short by a captcha or the run's maximum cost.

# 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 = {
    "pageID": "15087023444",
    "activeStatus": "active",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    },
    "maxAds": 100000
};

// Run the Actor and wait for it to finish
const run = await client.actor("jmlp/meta-ad-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 = {
    "pageID": "15087023444",
    "activeStatus": "active",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
    "maxAds": 100000,
}

# Run the Actor and wait for it to finish
run = client.actor("jmlp/meta-ad-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 '{
  "pageID": "15087023444",
  "activeStatus": "active",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxAds": 100000
}' |
apify call jmlp/meta-ad-library-scraper --silent --output-dataset

```

## MCP server setup

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