# Ad Library Monitor: Competitor Ads on Google, Meta & TikTok (`cleanpull/ad-library-monitor`) Actor

Watch competitors' ads in the public ad libraries of Google, Meta (Facebook, Instagram) and TikTok: new and stopped ads with first and last shown dates, text, links to creatives and landing pages. Only your Apify account needed.

- **URL**: https://apify.com/cleanpull/ad-library-monitor.md
- **Developed by:** [CleanPull](https://apify.com/cleanpull) (community)
- **Stats:** 2 total users, 1 monthly users, 88.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 watch item checkeds

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

## Ad Library Monitor: Competitor Ads on Google, Meta & TikTok

Give it **competitors' brand names or domains** and get the ads they run, from the platforms' public ad libraries: the **Google Ads Transparency Center** (search, YouTube and display ads, any country), the **Meta Ad Library** (Facebook and Instagram, any country) and **TikTok's ads library** (ads shown in the EU, the UK and Switzerland). Nothing to set up beyond your Apify account: no tokens, no developer apps. Every ad comes with its **first and last shown dates**, whether it is **active**, its library page and, where the library gives them, its text, title, call to action, landing page and a **link to the creative**. Schedule it and every run says **which ads are new and which stopped since the last one**. An ad counts as stopped only after **two runs in a row** find it inactive or gone. From **$1.00 per 1,000 watch items checked** and **$1.50 per 1,000 ads**, plus $3.50 per 1,000 Meta and TikTok library pages.

### What you can do with it

- **"Tell me when a competitor launches new ads."** Put their brands in a watchlist, turn on Changes only and schedule it daily: you get each new ad with its first shown date, and a change row for it.
- **"Which of their ads stopped?"** A stop is confirmed on the second run that reads the advertiser in full without the ad active, so one missed sighting never reads as a stop.
- **"What creatives are they running right now?"** All active ads per advertiser, newest shown first, with the format (text, image, video), a link to each creative and the library page to open it.
- **"Who advertises on a topic in Europe?"** Keywords search Meta's and TikTok's libraries for ads that use them, from any advertiser.
- **"Feed a digest or an AI agent."** Compact JSON with the same fields on every platform, a field picker, and a dataset view per job.

### Where the ads come from

| Platform                           | How it is read                                                     | Which ads                                                                             | Watch by                                                                           |
| ---------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| **Google Ads Transparency Center** | Directly                                                           | Every verified advertiser's search, YouTube, display and Shopping ads, in any country | Brand name (its advertiser accounts), domain (ads that point to it), advertiser id |
| **Meta Ad Library**                | Through our ad library service (a third-party API, ScrapeCreators) | Facebook, Instagram, Messenger and Audience Network ads, in any country               | Brand name (the brand's pages), domain or keyword (ads that mention it), page id   |
| **TikTok ads library**             | Through the same service                                           | Ads shown in the EU, the UK and Switzerland (TikTok's public library)                 | Brand name (its business accounts), domain or keyword                              |

The Meta and TikTok data is provided by a third-party API that reads the platforms' public ad libraries; CleanPull holds the account and its key (never in your input, logs or results). Each page it returns (30 Meta ads, 12 TikTok ads) is a **library request** on your bill.

How each is read:

1. **Google:** a brand name is matched to its advertiser accounts (Gymshark → Gymshark Ltd, Gymshark USA Inc and two more), then their ads are read newest shown first, 40 a page. Google gives the last day each ad was shown, so an ad shown in the last 3 days is active. Requests are spaced 1.5 s apart, from the run's own address. When Google declines a run (a captcha), that run's Google items are read through the ad library service instead, so the run still completes.
2. **Meta:** the brand's ads from the Meta Ad Library, in each country chosen (or all), active ones by default, 30 a page, with Meta's own active flag, start date and last date shown. A brand name keeps the pages named for it (others that carry the name are left out and counted); a domain or keyword finds every page's ads that mention it; a page id reads that page exactly.
3. **TikTok:** the brand's business accounts in TikTok's ads library (Gymshark → GYMSHARK LTD and others), 12 ads a page, with first and last shown dates and reach. An ad shown in the last 7 days counts as active, so a TikTok stop is reported only after an ad has gone a week unshown and two runs agree (TikTok's library dates are not yet proven to update daily). The library covers the EU, the UK and Switzerland as a whole, so countries do not narrow it.

In tests from Apify's servers, Google returned ads for every brand and domain tried (Allbirds, Gymshark in the US, UK, Germany and France, gymshark.com with 532 active ads, bombas.com), Meta and TikTok returned Gymshark's ads with text, media links and dates, and watchlists read twice showed **no false changes**.

### What makes the data right

- **Stopped means stopped.** An ad remembered as active that a run finds inactive, or misses on a read that covered every active ad, is counted once in the run summary; it is reported as stopped when the next run agrees. A read cut short by Ads per watch item never counts an unread ad as stopped, and a read that finds no ads at all, or loses more than half of the active ads at once, counts no stops (it looks like a failed read; the run notes it).
- **New means new.** After the first run, an ad is new when the watchlist has not seen it and its platform start date is after the last run (a day of grace: TikTok dates by day, and libraries list some ads a little late). An old ad that only now came into view is added to memory quietly, not reported as new.
- **A block is never an empty result.** A refusal, a rate limit or a service outage is named in the run summary with the reason, and the platform's other watch items are not tried in that run. Captchas are never solved. Nothing that failed is charged.
- **Only links, never files.** Creatives are linked where the library gives a link (Google's image previews, Meta's images and videos, TikTok's videos; these addresses expire after a while); nothing is downloaded or stored. The `adUrl` of every ad opens it in the platform's own library.
- **Businesses only.** Advertisers are pages and business accounts; no personal data of individuals is read.
- **Only delivered rows are billed,** and rows already delivered are never charged twice, even after a restart.

### Input

| Field                                | What it does                                                                                                   |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| `advertisers`                        | Brand names or domains, one per line; each is checked on every platform chosen                                 |
| `keywords`                           | Optional. Words in ad text (Meta and TikTok)                                                                   |
| `googleAdvertiserIds`, `metaPageIds` | Optional. Exact advertisers: AR… ids or Transparency Center addresses; page ids or Ad Library addresses        |
| `platforms`                          | `google`, `meta`, `tiktok` (default all)                                                                       |
| `countries`                          | Optional. Up to 5 two-letter codes, each read separately on Google and Meta; empty is anywhere                 |
| `activeOnly`                         | Only ads running now (default on). Off: stopped ads too (Meta lists stopped non-political ads only for the EU) |
| `maxAdsPerTarget`                    | Ads read per watch item and platform each run (default 100, up to 1,000)                                       |
| `minDaysRunning`                     | Optional. Only ads running at least this many days (35: proven ads). Meta and TikTok; younger ads are free     |
| `oneRowPerAdvertiser`                | One row per advertiser and watch item instead of per ad (default off)                                          |
| `changesOnly`, `monitorName`         | Only new ads and confirmed stops since the last run with the same watchlist name                               |
| `maxRunCostUsd`                      | Optional. Max cost per run, for scheduled runs without a spending limit of their own                           |
| `fields`, `maxConcurrency`           | Field selection; watch items at a time (1 or 2)                                                                |

A **watch item** is one line on one platform: "Allbirds" with Google and Meta chosen is two watch items.

#### Example inputs

A competitor watchlist on Google, checked daily for changes:

```json
{
  "advertisers": ["Allbirds", "Gymshark", "bombas.com"],
  "platforms": ["google"],
  "countries": ["US"],
  "changesOnly": true,
  "monitorName": "competitors"
}
```

The same brands on Meta (US) and TikTok, newest 30 ads each:

```json
{
  "advertisers": ["Allbirds", "Gymshark"],
  "platforms": ["meta", "tiktok"],
  "countries": ["US"],
  "maxAdsPerTarget": 30,
  "changesOnly": true,
  "monitorName": "competitors-social"
}
```

Everything an advertiser runs on Google, active or not:

```json
{
  "googleAdvertiserIds": ["AR01371945870127267841"],
  "activeOnly": false,
  "maxAdsPerTarget": 1000
}
```

### Output

Two kinds of rows, told apart by `recordType`. An ad row (Google, from a run on 2026-10-15):

```json
{
  "recordType": "ad",
  "platform": "google",
  "advertiser": "Allbirds Inc",
  "advertiserId": "AR01371945870127267841",
  "adId": "CR06031181514438344705",
  "adKey": "0476abec7da5a0db288873742d7aa5dfe6a217cf23070cd5ebfadb45a0e30157",
  "status": "active",
  "active": true,
  "firstShown": "2026-02-12T18:33:36.000Z",
  "lastShown": "2026-10-15T00:18:38.000Z",
  "firstSeen": "2026-10-15T02:07:47.759Z",
  "text": null,
  "title": null,
  "mediaType": "text",
  "mediaUrl": "https://tpc.googlesyndication.com/archive/simgad/7226910236503050294",
  "cta": null,
  "landingUrl": null,
  "countries": [],
  "publisherPlatforms": null,
  "reach": null,
  "adUrl": "https://adstransparency.google.com/advertiser/AR01371945870127267841/creative/CR06031181514438344705?region=anywhere",
  "watch": "Allbirds",
  "watchType": "advertiser",
  "checkedAt": "2026-10-15T02:10:49.852Z"
}
```

`status` is `new` (first seen since the last run and started since then), `active`, or `inactive` (with Active ads only off). `firstShown` and `lastShown` are the library's dates; `firstSeen` is when this watchlist first saw the ad. A Meta row, from a run on 2026-10-15:

```json
{
  "recordType": "ad",
  "platform": "meta",
  "advertiser": "Gymshark",
  "advertiserId": "129669023798560",
  "adId": "1082781854356123",
  "adKey": "c8ee6a86fae0ad6b67051abc4f2bbc0632d65a936bb525b74e62caad4283f4db",
  "status": "active",
  "active": true,
  "firstShown": "2026-10-01T07:00:00.000Z",
  "lastShown": "2026-10-14T07:00:00.000Z",
  "firstSeen": "2026-10-15T18:32:33.313Z",
  "text": "The pieces everyone’s talking about. Shop our best sellers now 👀",
  "title": "Shop Best Sellers",
  "mediaType": "image",
  "mediaUrl": "https://scontent-phl2-1.xx.fbcdn.net/v/t39.35426-6/829004198_3661677960646258_6377294838161671042_n.jpg?…",
  "cta": "Shop now",
  "landingUrl": "https://www.gymshark.com/collections/must-have",
  "countries": ["US"],
  "publisherPlatforms": ["facebook", "instagram", "audience_network", "messenger", "threads"],
  "reach": null,
  "adUrl": "https://www.facebook.com/ads/library?id=1082781854356123",
  "watch": "Gymshark",
  "watchType": "advertiser",
  "firstRun": true,
  "checkedAt": "2026-10-15T18:32:33.313Z"
}
```

What each platform fills:

| Field                   | Google                                   | Meta                                                      | TikTok                                      |
| ----------------------- | ---------------------------------------- | --------------------------------------------------------- | ------------------------------------------- |
| `text`, `title`         | not given (the text is inside the image) | the ad's body and title                                   | `title` (the ad's caption, when it has one) |
| `mediaType`, `mediaUrl` | text, image or video; the preview image  | image or video, and its address                           | video or image, and its address             |
| `cta`, `landingUrl`     | not given                                | the call to action and the landing page                   | not given                                   |
| `countries`             | the countries searched                   | the countries searched                                    | not given (the EU, the UK and Switzerland)  |
| `publisherPlatforms`    | not given                                | facebook, instagram, messenger, audience_network, threads | tiktok                                      |
| `reach`                 | not given                                | Meta's reach estimate, when it gives one                  | estimated audience ("10K-100K")             |

A change row (free), from the second run on (this one from the same run, 3 minutes after the first):

```json
{
  "recordType": "change",
  "change": "new",
  "platform": "google",
  "advertiser": "Allbirds Inc",
  "adId": "CR10446175794693668865",
  "adKey": "0e1be3de6440249add466215fa05a991ba99bf009ed9ae2115b43f2e23596561",
  "firstShown": "2026-10-12T22:14:37.000Z",
  "lastShown": "2026-10-15T01:40:25.000Z",
  "text": null,
  "adUrl": "https://adstransparency.google.com/advertiser/AR01371945870127267841/creative/CR10446175794693668865?region=anywhere",
  "watch": "Allbirds",
  "watchType": "advertiser",
  "since": "2026-10-15T02:07:47.759Z",
  "checkedAt": "2026-10-15T02:10:49.852Z"
}
```

**Proven advertisers.** Days running (`minDaysRunning`) keeps only ads that started at least that many days ago: every ad's start date is checked before any row, on Meta and TikTok. Younger ads, and ads without a start date, are left out and never charged, and they do not count toward Ads per watch item; to bound cost, a watch item then reads at most 3 times the pages it would otherwise take (the run notes say how many pages were read and how many ads were younger). The library requests are charged as usual. Google is read as before (its Transparency Center has no keyword search). With **One row per advertiser** (`oneRowPerAdvertiser`), each watch item gives one row per advertiser instead of per ad, charged as one ad, ready for a store lookup (Meta keyword `gymshark`, Days running 35, from the recorded answer used in the tests):

```json
{
  "recordType": "advertiser",
  "platform": "meta",
  "advertiser": "Gymshark",
  "advertiserId": "129669023798560",
  "landingDomain": "gymshark.com",
  "landingUrl": "https://www.gymshark.com/collections/must-have",
  "adsKept": 2,
  "earliestFirstShown": "2026-07-02T07:00:00.000Z",
  "daysRunning": 104,
  "sampleAdUrl": "https://www.facebook.com/ads/library?id=1082781854356123",
  "watch": "gymshark",
  "watchType": "keyword",
  "checkedAt": "2026-10-15T02:00:00.000Z"
}
```

`landingDomain` is the site most of the advertiser's ads link to. Change rows are left out in this mode; an advertiser found by two watch items has a row for each.

`change` is `new` (its ad row comes with it) or `stopped` (with the last sighting). Dataset views: **Ads**, **New and stopped ads**, **Advertisers** and **Overview**. `RUN_SUMMARY` in the key-value store gives the outcome (`COMPLETE`, `PARTIAL`, `NO_CHANGES`, `EMPTY`, `FAILED`), per watch item the advertisers matched, ads read, active, new, stopped, missing once, requests and whether every active ad was read, the charges, and every watch item that could not be read, with the reason.

### Pricing

| Event                                                                                               | Free and Bronze | Silver | Gold and above |
| --------------------------------------------------------------------------------------------------- | --------------- | ------ | -------------- |
| Watch item checked (one platform, per 100 ads read: 1 up to 100, 2 up to 200)                       | $1.00           | $0.90  | $0.80          |
| Ad (a delivered ad row)                                                                             | $1.50           | $1.35  | $1.20          |
| Library request (a page of 30 Meta or 12 TikTok ads, or of Google ads when Google declines the run) | $3.50           | $3.25  | $3.00          |

Per 1,000. Each run also has a $0.00005 start fee. Change rows are free. A watchlist of 10 competitors on Google checked daily with Changes only is about 300 checks a month ($0.30), plus the new ads ($1.50 per 1,000). The same 10 on Meta and TikTok at 30 ads each add 1 Meta and 3 TikTok library requests per competitor a day: about 1,200 requests a month ($4.20). The first run delivers every active ad as the baseline (up to Ads per watch item each).

### Limits

- **Meta and TikTok come through a third-party API** (ScrapeCreators) that reads the public libraries; if it is down or slow, those items are named in the run summary and read on the next run.
- **TikTok: the EU, the UK and Switzerland only** (TikTok publishes its ads library for those markets). There is no ad text beyond a caption, no call to action or landing page, and countries do not narrow the search. Stops are reported only after a week unshown.
- **Meta's stopped ads** (Active ads only off) are listed for ads that reached the EU; outside the EU, Meta's library keeps only political and social-issue ads once they stop. Running ads are listed in every country.
- **Google gives no ad text** for image and video ads (it is in the creative) and no landing page; its library lists verified advertisers only. Google may occasionally decline a run (it answers with a captcha, mostly when runs follow each other within minutes); that run's Google items are then read through the ad library service (charged as library requests) and the run completes.
- **Large advertisers:** with more active ads than Ads per watch item, the first ads the library returns are read, and stops are not reported for that item (a stop needs a full read). Raise Ads per watch item to cover them; each Meta or TikTok page is a library request.
- **A brand name on Meta** keeps the pages whose name is the brand's; give a page id to watch a page with another name. Nothing needs or uses a sign-in to a personal account.
- **Nothing found**: when no watch item could be read because none exists (an advertiser name with no match), the run still succeeds, with one free `recordType: "unreadable"` row per item giving the `reason`; nothing is charged. A run near its time limit stops starting watch items, delivers what it read and lists the rest with the reason.

### Changelog

- **0.2.x, proven advertisers** (2026-10-24): Days running (`minDaysRunning`) keeps only ads running at least that many days on Meta and TikTok (start dates checked on each ad); younger ads are not delivered or charged. One row per advertiser (`oneRowPerAdvertiser`): page, landing domain, ads kept, earliest start, days running and a sample ad, each charged as one ad. Prices and events unchanged.

- **0.2.x, reliability** (2026-10-24): runs that find nothing (advertisers with no match, input naming nothing readable) end SUCCEEDED with a free `unreadable` row per item saying why, instead of FAILED; a run stops starting watch items before its time limit and ends with what it read, never TIMED-OUT. Google advertiser ids, Meta page ids and platforms are checked in the input form. Prices and events unchanged.

- **0.2** (2026-10-15): Meta and TikTok through CleanPull's ad library service: no tokens or developer apps needed, Meta ads in every country with text, media links and calls to action; TikTok's library for the EU, the UK and Switzerland with captions and video links. Google falls back to the service when it declines a run. New event: library request.

- **0.1.x** (2026-10-15): RUN_SUMMARY `perWatchItem` says whether each watch item was delivered in full (`complete`); a watch item whose check could not be paid for no longer counts in `watchItemsRead`.

- **0.1** (October 2026): Google Ads Transparency Center by brand, domain or advertiser id; Meta Ad Library API and TikTok Commercial Content API with your own credentials as secret inputs; new and stopped ads with first and last shown dates, stops confirmed on a second run; watchlists with Changes only; Max cost per run.

# Actor input Schema

## `advertisers` (type: `array`):

Brand names or domains, one per line. A name is matched to the platform's advertisers (Google: Allbirds → Allbirds Inc; Meta: the brand's pages; TikTok: its business accounts); a domain (brand.com) finds Google's ads that point to it and Meta and TikTok ads that mention it. Each line is checked on every platform chosen.

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

Optional. Words to find in ad text, one per line (Meta and TikTok; the Google library is searched by advertiser only). Every advertiser's ads that use them come back.

## `googleAdvertiserIds` (type: `array`):

Optional. Advertiser ids (AR…) or Ads Transparency Center addresses, for an exact advertiser.

## `metaPageIds` (type: `array`):

Optional. Facebook page ids, or Ad Library addresses with view_all_page_id=…, for an exact page.

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

Where to look. Meta and TikTok are read from their public ad libraries through CleanPull's ad library service, at one library request per page of ads (30 Meta ads, 12 TikTok ads); Google is read directly.

## `countries` (type: `array`):

Optional. Two-letter country codes (US, DE, GB), up to 5; each is read separately on Google and Meta. Empty: anywhere. TikTok's library covers the EU, the UK and Switzerland as a whole and is not narrowed by country.

## `activeOnly` (type: `boolean`):

Only ads running now (Google: shown in the last 3 days; TikTok: in the last 7 days; Meta: its active flag). Off: stopped ads too (Meta lists stopped non-political ads only for the EU).

## `maxAdsPerTarget` (type: `integer`):

The most ads read per watch item and platform each run. A check is charged per 100 ads read (1 up to 100, 2 up to 200), plus one library request per page of Meta or TikTok ads. Stops are confirmed only for watch items read in full.

## `minDaysRunning` (type: `integer`):

Only ads that started at least this many days ago (35: proven ads, still running after five weeks). Meta and TikTok only: Meta is asked for these ads directly; every ad's start date is checked again before any row. Ads running fewer days, or without a start date, are left out and never charged (library requests are charged as usual). Google is read as before: its Ads Transparency Center has no keyword search. With Changes only, no ad is new (new ads started since the last run).

## `oneRowPerAdvertiser` (type: `boolean`):

Instead of a row per ad, one row per advertiser and watch item: name, page id, landing domain (the site most of its ads link to), ads kept, earliest start date, days running and a sample ad link. Ready to feed a store lookup. Each advertiser row is charged as one ad; change rows are left out.

## `changesOnly` (type: `boolean`):

After the first run, return only new ads and confirmed stops (plus the ad rows of the new ads). The first run returns every ad as the baseline.

## `monitorName` (type: `string`):

Runs with the same name share their memory of seen ads. Use a different name per watchlist.

## `maxRunCostUsd` (type: `number`):

Optional. The run stops before charging past this amount, for scheduled runs without a spending limit of their own. What was not read is read in full on the next run.

## `fields` (type: `array`):

Optional. Only these fields in each row (recordType always stays), e.g. advertiser, adUrl, firstShown.

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

Watch items read at once. Requests to each platform are always one at a time and spaced.

## Actor input object example

```json
{
  "advertisers": [
    "Allbirds",
    "gymshark.com"
  ],
  "platforms": [
    "google",
    "meta",
    "tiktok"
  ],
  "activeOnly": true,
  "maxAdsPerTarget": 100,
  "oneRowPerAdvertiser": false,
  "changesOnly": false,
  "monitorName": "default",
  "maxConcurrency": 2
}
```

# Actor output Schema

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

Ad and change rows.

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

No description

## `changes` (type: `string`):

No description

## `advertisers` (type: `string`):

One row per advertiser (when One row per advertiser is on).

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

Outcome (COMPLETE, PARTIAL, NO_CHANGES, EMPTY, FAILED), counts per watch item and platform, advertisers matched, charges and every watch item that could not be read, with the reason.

# 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 = {
    "advertisers": [
        "Allbirds",
        "gymshark.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanpull/ad-library-monitor").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 = { "advertisers": [
        "Allbirds",
        "gymshark.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("cleanpull/ad-library-monitor").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 '{
  "advertisers": [
    "Allbirds",
    "gymshark.com"
  ]
}' |
apify call cleanpull/ad-library-monitor --silent --output-dataset

```

## MCP server setup

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

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/1aRgYjfzwIQoaCTji/builds/U0YoM5J7AzDt4bF0N/openapi.json
