# Google Ads Transparency Scraper: Ads & New-Ad Monitor (`lasting_bonnethead/google-ads-transparency`) Actor

Scrape ads from the Google Ads Transparency Center by brand, domain, advertiser ID or URL. Get creatives, images, first/last shown dates, variations and regions, filter by country, platform, format and date, and monitor competitors with only-new-ads mode.

- **URL**: https://apify.com/lasting_bonnethead/google-ads-transparency.md
- **Developed by:** [Fatih S.](https://apify.com/lasting_bonnethead) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 ad creative returneds

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

## Google Ads Transparency Scraper: Ads & New-Ad Monitor

Scrape any advertiser's ads from the public **[Google Ads Transparency Center](https://adstransparency.google.com)**. Search by brand name, domain, advertiser ID or by pasting a Transparency Center URL. Each ad comes back as a clean row: creative ID, advertiser, format, **first and last shown dates**, days live, image or preview URL, size and a link to the ad page. You can also get **every creative variation and the countries the ad ran in**.

Need daily competitor tracking? Turn on **Only new ads**. Each scheduled run then returns, and charges for, only ads you haven't seen before. A daily check of a competitor with no new ads costs almost nothing.

No Google account, no login and no browser needed. **From $0.0012 per ad.**

### What you get

- 🔎 **Every way to search**: brand names (`Nike`), domains (`nike.com`), advertiser IDs (`AR16735076323512287233`) and pasted URLs from adstransparency.google.com (advertiser pages, single ads and domain searches, with their filters kept).
- 🧠 **Smart brand matching**: Google's autocomplete often lists a one-ad "Nike" account in Kenya above **Nike, Inc.** (9,000+ ads). We rank exact name matches first, ignoring Inc./LLC/Ltd/S.r.l., then sort by ad volume, so `Nike` gets you Nike's real ads.
- 🎛️ **All the website's filters**: region (244 countries and territories, or anywhere), platform (Search, YouTube, Shopping, Maps, Play), format (text, image, video) and date range.
- 🖼️ **Ready-to-use creatives**: direct image URLs with width and height for image and text ads, plus Google's preview URL for video and responsive ads.
- 🧬 **Ad details (optional)**: all variations of each ad, plus the countries it ran in, with last-shown date and impression ranges per platform where Google publishes them.
- 🆕 **Only-new-ads monitor**: remembers creative IDs in a named key-value store. Schedule it daily and get only fresh ads.
- 🏢 **Advertisers mode**: one row per advertiser with name, country, verified status and approximate ad count. Great for building competitor lists.
- 🛡️ **Reliable**: uses the same public endpoints as the website. Retries with backoff on rate limits, rotates proxy sessions, keeps each input isolated so one bad input never kills the run, and writes a per-input `SUMMARY`.
- 💸 **Cheap and fair**: no start fee, nothing charged for failed or empty lookups, and the max-charge limit you set is always respected.

### Use cases

- **Competitor ad research**: see every ad a competitor runs on Google Search, YouTube, Shopping, Maps and Play, and how long each has been live. Long-running ads are usually the winners.
- **Creative inspiration and swipe files**: collect image and text ads with direct image URLs for your boards and AI tools.
- **Daily new-ad alerts**: schedule Only new ads mode and send new rows to Slack, email or a sheet with Apify integrations.
- **Agency prospecting**: find brands that are actively advertising in a country, with ad volume per advertiser.
- **Brand protection**: watch which advertisers run ads for your domain or brand name.
- **Market and academic research**: track ad formats, countries and campaign duration across an industry.

### Input examples

**All recent ads from a brand in the US**

```json
{
  "queries": ["Nike"],
  "region": "US",
  "maxAdsPerSearch": 200
}
```

**YouTube video ads for a domain in Germany, shown in September, with variations and countries**

```json
{
  "domains": ["booking.com"],
  "region": "DE",
  "platforms": ["YOUTUBE"],
  "adFormat": "VIDEO",
  "startDate": "2026-09-01",
  "endDate": "2026-09-30",
  "includeDetails": true
}
```

**Daily competitor monitor (schedule this as a task)**

```json
{
  "queries": ["adidas", "puma.com"],
  "advertiserIds": ["AR16735076323512287233"],
  "region": "US",
  "maxAdsPerSearch": 300,
  "onlyNewAds": true,
  "monitorStoreName": "sportswear-us-monitor"
}
```

**Paste URLs straight from the website** (their region, platform, format and date filters are kept)

```json
{
  "startUrls": [
    { "url": "https://adstransparency.google.com/advertiser/AR16735076323512287233?region=US&format=VIDEO" },
    { "url": "https://adstransparency.google.com/?region=GB&domain=booking.com" },
    { "url": "https://adstransparency.google.com/advertiser/AR02934798844673654785/creative/CR03098079499674189825?region=anywhere" }
  ]
}
```

**Find advertisers (not ads)**

```json
{
  "resultType": "advertisers",
  "queries": ["Nike", "booking.com"],
  "region": "US",
  "maxAdvertisersPerQuery": 5
}
```

#### Input reference

| Field | Default | Notes |
|---|---|---|
| `queries` | – | Brand names or domains. Names are resolved to advertisers via autocomplete; domains search all ads pointing to that domain. Advertiser IDs and Transparency Center URLs also work here. |
| `domains` | – | Exact domains (`nike.com`). Protocol, `www.` and paths are stripped. |
| `advertiserIds` | – | IDs starting with `AR`, as in advertiser page URLs. |
| `startUrls` | – | `/advertiser/AR…`, `/advertiser/AR…/creative/CR…` or `/?domain=…` URLs. URL filters override the global filters for that URL. |
| `resultType` | `ads` | `ads` = one row per creative; `advertisers` = one row per advertiser. |
| `region` | `US` | 2-letter country code or `ANYWHERE`. |
| `platforms` | all | `SEARCH`, `YOUTUBE`, `SHOPPING`, `MAPS`, `PLAY`. Each selected platform is searched separately; an ad found on several is output once. |
| `adFormat` | `ALL` | `ALL`, `TEXT`, `IMAGE`, `VIDEO`. |
| `startDate` / `endDate` | – | `YYYY-MM-DD` (both inclusive) or relative (`30 days`). |
| `maxAdsPerSearch` | 100 | Max ads scanned per input (and per platform). Results are ordered newest activity first. Values above 5,000 are clamped. |
| `maxAdvertisersPerQuery` | 5 | How many matching advertisers a brand-name query includes (max 50). |
| `includeDetails` | false | Adds `variationCount`, `variations[]`, `regionsShown[]` and `platformsShown[]` (one extra request per ad). |
| `detailConcurrency` | 6 | Parallel detail requests (1–20). |
| `onlyNewAds` | false | Monitor mode; see below. |
| `monitorStoreName` | `ads-transparency-monitor` | Named key-value store holding seen creative IDs. |
| `includeRawData` | false | Attaches the raw payloads as `raw`. |
| `proxyConfiguration` | Apify residential | Recommended. If the proxy is unusable, the run falls back to a direct connection with a warning. |

### Output

#### Ads (`resultType: "ads"`)

```json
{
  "creativeId": "CR02775595230328520705",
  "advertiserId": "AR16735076323512287233",
  "advertiserName": "Nike, Inc.",
  "domain": null,
  "format": "TEXT",
  "firstShown": "2023-11-16T21:46:49.822Z",
  "lastShown": "2026-10-08T19:13:38.071Z",
  "shownForDays": 1058,
  "servedDays": 902,
  "previewType": "image",
  "imageUrl": "https://tpc.googlesyndication.com/archive/simgad/2146926076785337219",
  "previewUrl": "https://tpc.googlesyndication.com/archive/simgad/2146926076785337219",
  "width": 348,
  "height": 209,
  "impressionsLow": null,
  "impressionsHigh": null,
  "isAdvertiserUnverified": false,
  "adUrl": "https://adstransparency.google.com/advertiser/AR16735076323512287233/creative/CR02775595230328520705?region=US",
  "advertiserUrl": "https://adstransparency.google.com/advertiser/AR16735076323512287233?region=US",
  "region": "US",
  "platformFilter": null,
  "dateRangeFilter": null,
  "matchedQuery": "Nike",
  "searchType": "advertiserName",
  "variationCount": 5,
  "variations": [
    { "previewType": "image", "imageUrl": "https://tpc.googlesyndication.com/archive/simgad/2146926076785337219", "previewUrl": "…", "width": 348, "height": 209 },
    { "previewType": "script", "imageUrl": null, "previewUrl": "https://displayads-formats.googleusercontent.com/ads/preview/content.js?…", "width": null, "height": null }
  ],
  "regionsShown": [
    { "regionCode": "US", "regionName": "United States", "geoCriterionId": 2840, "firstShown": null, "lastShown": "2026-09-01", "impressionsLow": null, "impressionsHigh": null, "platforms": [] }
  ],
  "platformsShown": [],
  "scrapedAt": "2026-10-08T19:45:02.975Z"
}
```

| Field | Notes |
|---|---|
| `creativeId` | Stable unique ad ID (`CR…`). Use it for de-duplication. |
| `format` | `TEXT`, `IMAGE` or `VIDEO`. Google renders text ads as images, so text ads often have an `imageUrl` too. |
| `firstShown` / `lastShown` | When the ad was first and most recently shown (ISO 8601, UTC), as reported by Google. |
| `shownForDays` | Calendar days from first to last shown, inclusive (same day = 1). |
| `servedDays` | Number of days Google reports the ad actually ran (can be lower than `shownForDays` if it paused). |
| `previewType` | `image` (direct image in `imageUrl`), `script` (Google's preview renderer URL, used for video and responsive ads), `iframe` or `html`. |
| `imageUrl`, `width`, `height` | Set for image previews. |
| `impressionsLow/High` | Only published by Google for some ads and regions (for example political ads and EU/EEA). Otherwise `null`. |
| `domain` | Matched domain for domain searches. |
| `region`, `platformFilter`, `dateRangeFilter` | Filters that produced the row. |
| `matchedQuery`, `searchType` | The input that produced the row, and whether it was `advertiserName`, `domain`, `advertiserIds` or `creative`. |
| `variationCount`, `variations[]` | *includeDetails only.* Every version of the creative. |
| `regionsShown[]` | *includeDetails only.* Countries the ad ran in, each with `lastShown`, `firstShown` where available, impression ranges and per-platform impressions where Google publishes them. |
| `isNew` | *onlyNewAds only.* Always `true`, because only new ads are output. |
| `raw` | *includeRawData only.* |

#### Advertisers (`resultType: "advertisers"`)

```json
{
  "advertiserId": "AR02934798844673654785",
  "name": "Booking.com",
  "legalName": "Booking.com B.V.",
  "countryCode": "NL",
  "approxAdCountLow": 400000,
  "approxAdCountHigh": 500000,
  "isUnverified": false,
  "domain": "booking.com",
  "adsInScan": 200,
  "advertiserUrl": "https://adstransparency.google.com/advertiser/AR02934798844673654785?region=US",
  "region": "US",
  "matchedQuery": "booking.com",
  "scrapedAt": "2026-10-08T19:45:10.909Z"
}
```

`approxAdCountLow/High` is Google's ad-count range for the selected region and filters. For domain inputs, advertisers are found by scanning up to `maxAdsPerSearch` of the domain's ads (`adsInScan` = how many of them belong to that advertiser).

#### Run summary

Every run also saves a `SUMMARY` record in the default key-value store. It lists each input's status (`ok`, `no_results`, `not_found`, `error`, `skipped`), the advertisers a brand query resolved to, Google's total ad-count estimate, ads scanned and output, already-seen ads skipped, monitor state, charged events and HTTP stats.

### Only-new-ads monitor mode

1. Set `onlyNewAds: true` and pick a `monitorStoreName` (for example `acme-competitors-us`).
2. **The first run returns every ad it finds** (up to your limits) and remembers their creative IDs. This is your baseline.
3. Each later run with the same store returns **only creatives not delivered before**. You're charged only for those. Already-seen ads are skipped for free.
4. Schedule it (daily is typical) and connect an integration such as Slack, email, Google Sheets or a webhook.

Details:

- Seen IDs are stored in the named key-value store under `SEEN_CREATIVE_IDS`, split into chunks automatically for very large sets (up to 2 million IDs; the oldest are dropped after that). Named stores persist on your account until you delete them.
- An ad is marked as seen only after it has been pushed to the dataset. If a run stops at your max-charge limit, the remaining new ads are delivered next time.
- Results are ordered newest activity first. For each input, set `maxAdsPerSearch` comfortably above the number of new ads you expect per run.
- One store can be shared by many inputs because creative IDs are globally unique. Use separate store names for independent monitors, and don't run two monitors on the same store at the same time.
- To start over, delete the store or use a new `monitorStoreName`.

### Pricing

Pay per event, with no start fee:

| Event | FREE plan | BRONZE | SILVER | GOLD | Charged when |
|---|---|---|---|---|---|
| `ad-result` | **$0.0012** | $0.0011 | $0.0010 | $0.0009 | Each ad row (in monitor mode, only new ads) |
| `advertiser-record` | $0.0008 | $0.0007 | $0.0006 | $0.0005 | Each advertiser row |
| `ad-detail` | $0.002 | $0.0018 | $0.0015 | $0.0012 | Each ad whose details (variations + regions) were fetched successfully. Only with `includeDetails`. |

Examples on the FREE plan: 1,000 ads cost **$1.20**. 1,000 ads with full details cost $3.20. A daily monitor of 10 competitors that finds 20 new ads a day costs about $0.72 a month.

Nothing is charged for inputs that fail, find nothing or match no advertiser, or for ads skipped as already seen. The run stops cleanly at your **maximum cost per run**.

### Tips

- **Can't find a brand?** Advertisers are listed under their legal or account name. Try the domain (`brand.com`), set `region` to `ANYWHERE`, or open the brand on adstransparency.google.com and paste the URL.
- **Want only one advertiser account?** Use `advertiserIds` or an advertiser URL instead of a brand name.
- **Getting 429 warnings?** They're retried automatically. Lowering `detailConcurrency` makes runs gentler.
- Video and responsive ads have no static image. Use `previewUrl` (Google's renderer) or open `adUrl`.

### FAQ

**Is it legal?** The Actor reads only the public, no-login data that Google publishes in the Ads Transparency Center for transparency purposes. It doesn't bypass any login or paywall. You're responsible for using the data in line with applicable laws and Google's terms, especially if you republish creatives, which may be protected by the advertisers' copyright.

**How fresh is the data?** Live: every run queries the Transparency Center directly.

**Why are impressions often empty?** Google only publishes impression ranges for some ads (for example political ads and ads shown in the EU/EEA).

### Disclaimer

This is an independent tool. It is **not affiliated with, endorsed by or sponsored by Google LLC**. "Google", "Google Ads", "YouTube" and related marks are trademarks of Google LLC. The Actor uses publicly available data from adstransparency.google.com. The site's internal endpoints can change without notice; if they do, we update the Actor as quickly as possible.

# Actor input Schema

## `queries` (type: `array`):

Brand / advertiser names (e.g. "Nike") or bare domains (e.g. "nike.com"). Names are resolved through the Transparency Center autocomplete to the best-matching verified advertisers (see "Max advertisers per query"); domains search every ad that points to that domain. Advertiser IDs (AR…) and Transparency Center URLs are also accepted here.

## `domains` (type: `array`):

Exact advertised domains, e.g. booking.com. Protocol, www. and paths are stripped.

## `advertiserIds` (type: `array`):

Transparency Center advertiser IDs, e.g. AR16735076323512287233 (the ID in an advertiser page URL).

## `startUrls` (type: `array`):

Paste URLs from adstransparency.google.com: advertiser pages (/advertiser/AR…), single ads (/advertiser/AR…/creative/CR…) or domain searches (/?domain=nike.com). Filters in the URL (region, platform, format, start-date, end-date, preset-date) override the filters below for that URL.

## `resultType` (type: `string`):

Ads: one row per ad creative. Advertisers: one row per advertiser with name, country and approximate ad count.

## `region` (type: `string`):

Only ads shown in this country (same as the region picker on the website). ANYWHERE = all regions.

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

Only ads shown on these Google platforms. Leave empty for all. Each selected platform is searched separately (maxAdsPerSearch applies per platform); ads found on several are output once.

## `adFormat` (type: `string`):

Only ads of this format.

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

Only ads shown on or after this date (YYYY-MM-DD, or relative like "30 days"). Uses the Transparency Center's own date filter.

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

Only ads shown on or before this date (inclusive, YYYY-MM-DD).

## `maxAdsPerSearch` (type: `integer`):

Maximum ads scanned per input (query, domain, advertiser ID or URL). Ads come newest-activity first. Hard cap 5,000 (higher values are clamped).

## `maxAdvertisersPerQuery` (type: `integer`):

For brand-name queries: how many matching advertisers to include. Exact name matches (ignoring Inc., LLC, Ltd…) come first, then by ad volume. Brands often run ads under several advertiser accounts.

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

One extra request per ad to get every creative variation (all image/text versions) and the per-country breakdown (regions shown, last shown date, impression ranges per platform where published). Charged as an extra "ad-detail" event.

## `detailConcurrency` (type: `integer`):

Parallel detail requests. Lower it if you see many 429 warnings.

## `onlyNewAds` (type: `boolean`):

Return (and charge for) only ads not delivered by previous runs that used the same monitor store. The first run returns everything it finds and remembers it. Ideal for daily schedules.

## `monitorStoreName` (type: `string`):

Named key-value store that remembers seen creative IDs. Use a different name per monitor (e.g. "nike-us-monitor") if you want them independent.

## `includeRawData` (type: `boolean`):

Attach the raw Transparency Center payloads to each row (field "raw"). For debugging / power users.

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

Apify Proxy is recommended (residential by default) so Google does not rate-limit the run.

## Actor input object example

```json
{
  "queries": [
    "Nike"
  ],
  "resultType": "ads",
  "region": "US",
  "adFormat": "ALL",
  "maxAdsPerSearch": 100,
  "maxAdvertisersPerQuery": 5,
  "includeDetails": false,
  "detailConcurrency": 6,
  "onlyNewAds": false,
  "monitorStoreName": "ads-transparency-monitor",
  "includeRawData": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "queries": [
        "Nike"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("lasting_bonnethead/google-ads-transparency").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 = {
    "queries": ["Nike"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("lasting_bonnethead/google-ads-transparency").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 '{
  "queries": [
    "Nike"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call lasting_bonnethead/google-ads-transparency --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lasting_bonnethead/google-ads-transparency"
        }
    }
}
```

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/oIuLClBOtBaqFeIRf/builds/xSFdGHJ4jpHytlSpc/openapi.json
