# Google Ads Transparency Scraper | from $0.60/1K Ads (`alexmorain/google-ads-transparency-scraper`) Actor

Scrape the Google Ads Transparency Center by domain, advertiser ID or name: every ad with format, first and last shown dates, preview, and for EU-served ads impressions per country and platform. Plain HTTP, residential proxies, check 1,000 domains in one run.

- **URL**: https://apify.com/alexmorain/google-ads-transparency-scraper.md
- **Developed by:** [Alex Morain](https://apify.com/alexmorain) (community)
- **Categories:** Marketing, Lead generation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## Google Ads Transparency Scraper

This Actor turns the [Google Ads Transparency Center](https://adstransparency.google.com/)
into structured data. Give it website domains, advertiser IDs or advertiser names, and it
returns every ad Google lists for them: verified advertiser, format (text, image, video),
first and last shown dates, number of days shown, and a preview. With details on, each ad
also comes with its variants, the countries where it ran, and for ads served in the European
Union the impressions per country and per platform (Search, YouTube, Shopping, Play, Maps).

It runs over plain HTTP against the same endpoints the Transparency Center website calls. No
browser, no login, no cookies. A run of 20,000 ads across 20 domains takes about a minute.
Pricing is pay per event: $1.20 per 1,000 ads at the entry price, from $0.60 / 1,000 ads on
Gold; $2.00 per 1,000 ads with details ($1.00 on Gold); $2.00 per 1,000 domains in check
mode ($1.00 on Gold); and $0.002 per run.

Google does not publish an API for the Ads Transparency Center. This Actor is the closest
thing to one: a domain or an advertiser in, JSON out.

***

### What this Google ads scraper does

It searches in three ways, and they can be combined in one run.

By website domain. `nike.com` returns every ad pointing to that domain, across every
advertiser account that runs them. Big brands often advertise through several verified
accounts (a retail entity per country, agencies, resellers): a domain search catches them
all, and each ad carries the `advertiserName` so you can tell them apart.

By advertiser ID. `AR16735076323512287233` (or its Transparency Center URL) returns the ads
of that one verified account and nothing else.

By advertiser name. `Supercell` is matched to the verified advertiser with the most ads
whose name contains it. The log lists the other matches with their IDs, so you can pin the
right one in Advertiser IDs on the next run.

You can also paste Transparency Center URLs: an advertiser page, a domain search, or a
single ad page (`/advertiser/AR…/creative/CR…`), which returns that ad with its details.

### Check mode: does this company advertise on Google?

Switch Mode to Check and the Actor returns one line per domain or advertiser instead of its
ads, from a single request each: does it advertise on Google, Google's range for its ad count,
when its most recent ad was last shown, and which advertiser account ran it.

That is the cheap way to qualify a long list of companies. 100 domains took 18 seconds in our
tests. You pay $0.002 per line on the Free plan, whether the company has 10 ads or 100,000.

```json
{
  "input": "puma.com",
  "inputType": "domain",
  "country": null,
  "hasAds": true,
  "adCountMin": 30000,
  "adCountMax": 40000,
  "latestAdLastShown": "2026-10-16T14:52:07Z",
  "latestAdvertiserId": "AR11620109060996595713",
  "latestAdvertiserName": "PUMA SPORTS PHILIPPINES INC.",
  "latestAdUrl": "https://adstransparency.google.com/advertiser/AR11620109060996595713/creative/CR15288039521514422273?region=anywhere",
  "transparencyUrl": "https://adstransparency.google.com/?region=anywhere&domain=puma.com",
  "scrapedAt": "2026-10-16T15:14:22Z"
}
```

### How to use it

From the console: type one or more domains in Website domains, press Start. Everything else
can stay as it is for a first run.

From the API:

```bash
curl -X POST "https://api.apify.com/v2/acts/alexmorain~google-ads-transparency-scraper/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["nike.com"], "countries": ["US"], "maxAdsPerTarget": 200}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("alexmorain/google-ads-transparency-scraper").call(run_input={
    "domains": ["decathlon.fr"],
    "countries": ["FR"],
    "adFormats": ["IMAGE"],
    "platforms": ["GOOGLE_SHOPPING"],
    "maxAdsPerTarget": 500,
})
for ad in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(ad["advertiserName"], ad["format"], ad["lastShown"], ad["adUrl"])
```

### Filters

- `countries`: two-letter codes (`US`, `GB`, `FR`) or names. Each country is searched on its
  own and an ad found in several comes out once, tagged with the first country.
- `startDate` / `endDate`: only ads shown inside the window.
- `adFormats`: `TEXT`, `IMAGE`, `VIDEO`.
- `platforms`: `GOOGLE_SEARCH`, `YOUTUBE`, `GOOGLE_SHOPPING`, `GOOGLE_PLAY`, `GOOGLE_MAPS`.
- `maxAdsPerTarget`: cap per domain, advertiser or name (per country when you list several).
  Default 100, `0` for no cap. Google lists the most recently shown ads first, so a cap keeps
  the freshest ones.

Filters are applied by Google itself, so a filtered run reads only what it returns. The one
exception is asking for exactly two formats, which Google cannot do in one search: the third
format is then skipped after reading, and never charged.

### Only new ads, for scheduled monitoring

Set Mode to New ads only and schedule the Actor daily. It keeps a registry of every ad it
returned before (a named key-value store in your account) and returns only ads it has never
returned. Skipped ads are not charged.

Once a run has read a search to its end, later runs stop that search as soon as they reach
ads last shown before that run (with three days of margin for Google's indexing delay). A
quiet day costs little more than the start fee. In our test, a second run on a 700-ad
search returned nothing new and stopped after 7 requests.

Add a `webhookUrl` and you get a POST with a small JSON summary only when there is something
new: `actor`, `runId`, `mode`, `items`, `searches`. The URL is stored encrypted and never
logged.

### Output fields

Every ad:

| Field | What it holds |
|---|---|
| `adUrl` | The ad's page in the Transparency Center |
| `advertiserId`, `advertiserName` | Verified advertiser account |
| `creativeId` | Google's ID for the ad |
| `format` | `TEXT`, `IMAGE` or `VIDEO` |
| `firstShown`, `lastShown` | First and last time Google showed the ad, UTC |
| `daysShown` | Number of days Google reports the ad as shown |
| `domain` | Domain the ad points to (domain searches) |
| `previewImageUrl` | Image of the ad. For text ads, a picture of the Search ad itself |
| `previewUrl` | Google's HTML preview for image, video and Shopping ads |
| `input`, `inputType`, `country` | Which search produced the item |

With Include details:

| Field | What it holds |
|---|---|
| `variantCount`, `variants` | Every variant Google keeps for the ad, with previews |
| `servedCountries` | Each country where the ad ran: code, name, last shown date; in the EU also first shown date and impressions per platform |
| `impressionsMin`, `impressionsMax` | EU only: Google's impressions range, all EU countries. `impressionsMax` is null when open-ended (10M+) |
| `platforms` | EU only: impressions range on Search, YouTube, Shopping, Play, Maps |
| `productCategory` | EU only: category of a Shopping ad |

Example, a text ad with details:

```json
{
  "adUrl": "https://adstransparency.google.com/advertiser/AR15589870916631265281/creative/CR03027411911672070145?region=DE",
  "advertiserId": "AR15589870916631265281",
  "advertiserName": "Zalando SE",
  "creativeId": "CR03027411911672070145",
  "format": "TEXT",
  "firstShown": "2024-02-08T10:41:13Z",
  "lastShown": "2026-10-16T14:50:15Z",
  "daysShown": 710,
  "domain": "zalando.de",
  "previewUrl": null,
  "previewImageUrl": "https://tpc.googlesyndication.com/archive/simgad/1109224665132657429",
  "input": "zalando.de",
  "inputType": "domain",
  "country": "DE",
  "hasDetails": true,
  "variantCount": 3,
  "impressionsMin": 10000,
  "impressionsMax": 15000,
  "platforms": [
    {"platform": "GOOGLE_SEARCH", "impressionsMin": 10000, "impressionsMax": 15000},
    {"platform": "GOOGLE_SHOPPING", "impressionsMin": 0, "impressionsMax": 1000},
    {"platform": "YOUTUBE", "impressionsMin": 0, "impressionsMax": 1000}
  ],
  "servedCountries": [
    {"countryCode": "DE", "countryName": "Germany", "geoId": 2276, "firstShown": "2024-02-08",
     "lastShown": "2026-10-16", "impressionsMin": 10000, "impressionsMax": 15000, "platforms": ["…"]}
  ],
  "productCategory": null
}
```

### Why this one

It does not get stuck on Google's rate limit. Google lets one IP make about a hundred
requests to the Transparency Center, then answers with a captcha for a quarter of an hour or
more. We measured it from Apify's own servers: a single IP is cut off after 80 to 100
requests, and a shared datacenter pool of 27 IPs was exhausted after 1,700 requests. This
Actor spreads its requests over many residential IPs, changes IP at the first refusal, and
retires each IP before Google flags it. In our tests, 99.5% of requests went through on the
first try, and every refused one went through on a fresh IP.

It reads Google's data, not a rendering of it. No browser, which is why it is fast and why
it fits in 1 GB. The first and last shown dates are Google's own timestamps, not the
day the Actor happened to see the ad.

It tells you what happened. Each search logs the total Google announces for it, the status
message says how many items came back and why a search stopped, and a run that returns
nothing because every search failed ends as FAILED, with the reason, rather than as an
empty success.

### Pricing

| Event | Free | Bronze | Silver | Gold and above |
|---|---|---|---|---|
| Ad | $0.0012 | $0.0010 | $0.0008 | $0.0006 |
| Detailed ad (instead of Ad, not on top) | $0.0020 | $0.0017 | $0.0014 | $0.0010 |
| Advertiser lookup (check mode) | $0.0020 | $0.0017 | $0.0014 | $0.0010 |
| Actor start | $0.002 | | | |

The run log opens with the maximum cost of the run at Free plan prices. Set a maximum charge
on the run and the Actor stops cleanly when it is reached: you are never charged beyond it.

### Limits

- Google gives the ad count of a domain or advertiser as a range (`90000–100000`), not an
  exact number. So does this Actor.
- The text of text ads is in their preview image, not in a field: Google renders Search ads
  as images in the Transparency Center. Headlines and descriptions are not extracted.
- Video ads come with Google's HTML preview, not a YouTube video ID.
- Impressions, first shown date per country and product category exist only for ads served
  in the European Union (Digital Services Act data). Outside the EU, Google publishes the
  countries and the last shown date per country only.
- One domain is read at about 3,000 ads a minute, since Google hands out pages one after
  another. Several domains run in parallel. A brand with 100,000 ads needs a run timeout of
  about 40 minutes, or a country or date filter.
- `GOOGLE_MAPS` is the platform code Google uses for Maps placements as far as we could
  verify: those ads are text ads in small numbers.

### FAQ

#### Is there a Google Ads Transparency Center API?

Not a public one. The Transparency Center website calls internal endpoints that return JSON;
this Actor calls the same ones and hands you a clean dataset.

#### How much does it cost to scrape 1,000 Google ads?

$1.20 on the Free plan, $0.60 on Gold, plus $0.002 per run. With details, $2.00 and $1.00.

#### Why does a domain return ads from several advertisers?

Because Google files ads by verified advertiser account, and one website is often advertised
by several accounts. Use Advertiser IDs to keep a single one.

#### A domain returns zero ads. What now?

Check mode tells you in one request whether Google lists any ad for it. If it says no, the
domain does not advertise on Google, or not under that exact domain: try the domain without
a subdomain, or the advertiser name.

#### How do I export the ads to CSV or Excel?

From the run's Storage tab, or through the dataset API with `format=csv` or `format=xlsx`.

### Support

Something wrong or missing? Open an issue on the Actor page and it gets fixed. If the Actor
saved you time, a short review helps others find it.

# Actor input Schema

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

Websites whose ads you want, one per line: <code>nike.com</code>, <code>decathlon.fr</code>. A full URL works too, only the domain is kept. Google matches the domain the ads point to, across every advertiser account that runs them (a big brand often has several).

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

Google advertiser IDs like <code>AR16735076323512287233</code>, or the advertiser page URL from adstransparency.google.com. Use them to target one exact verified account rather than everything pointing to a domain.

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

Advertiser names as you would type them in the Transparency Center search box: <code>Supercell</code>. Each name is matched to the verified advertiser with the most ads whose name contains it; the log lists the other matches with their IDs so you can pin one in Advertiser IDs. A name that looks like a domain (<code>temu.com</code>) is searched as a domain.

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

Pages copied from adstransparency.google.com: an advertiser page, a domain search (<code>?domain=nike.com\&region=US</code>) or a single ad (<code>/advertiser/AR…/creative/CR…</code>). The <code>region</code> in the URL is kept for that URL. A single ad URL always comes with its details.

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

Only ads shown in these countries: two-letter codes like <code>US</code>, <code>GB</code>, <code>FR</code>, or names in English. Leave empty for all countries. With several countries each one is searched on its own and every ad comes out once, tagged with the first country it was found in.

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

Only ads shown on or after this date. Leave empty for no lower bound.

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

Only ads shown on or before this date. Leave empty for no upper bound.

## `adFormats` (type: `array`):

Only these formats. Leave empty for all. Text ads are Google Search ads; Google shows them as an image of the ad, so their text is in the preview image, not in a field.

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

Only ads shown on these Google platforms. Leave empty for all.

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

Stop each domain, advertiser or name after this many ads, <b>per country</b> when you list several. Set <code>0</code> for no cap: a big brand can hold 100,000 ads. Google lists the most recently shown ads first.

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

One extra lookup per ad, priced as a detailed ad. Adds every variant of the ad, each country where it ran with its last shown date, and <b>for ads served in the EU</b> (Digital Services Act data): first and last shown date per country, impressions per country and per platform (Search, YouTube, Shopping, Play, Maps) as ranges, and the product category of Shopping ads. Outside the EU Google publishes the countries and last shown dates only.

## `mode` (type: `string`):

<b>All ads</b> returns every ad the search finds, on every run.<br><br><b>New ads only</b> keeps a persistent registry of the ads previous runs returned and charged, and returns only ads never returned before: schedule it daily to be told when a competitor launches something new. Once a run has read a search to its end, later runs stop as soon as they reach ads last shown before that run (minus 3 days of margin for Google's indexing delay), so a quiet day costs little more than the start fee.<br><br><b>Check</b> returns one line per domain or advertiser (and per country) instead of ads: does it advertise on Google, Google's range for its ad count, when its latest ad was last shown, which advertiser runs it. One request each, priced per line, made for qualifying long lists of companies.

## `advertisersPerSearchTerm` (type: `integer`):

How many matching advertisers to take for each name in Advertiser names, the ones with the most ads first.

## `registryKey` (type: `string`):

By default the registry is tied to the exact targets, countries and filters, so changing any of them starts a fresh one. Set your own key to keep one registry while you edit the list.

## `webhookUrl` (type: `string`):

If set, the Actor POSTs a small JSON summary to this URL at the end of the run, <b>only when at least one item was returned</b>: <code>actor</code>, <code>runId</code>, <code>mode</code>, <code>items</code>, <code>searches</code>. With New ads only and a daily schedule, that is a push notification when a competitor launches ads.<br><br>Use the final URL: a redirect is not followed, except a 307 or 308, which gets the same POST once. An unreachable webhook is logged as a warning and never fails the run. Stored encrypted, since a webhook URL often carries its own access token.

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

Leave as is. Google lets one IP make about 100 requests, then answers with a captcha for a quarter of an hour or more, so the Actor spreads requests over many residential IPs and rotates at the first refusal. The per-ad price is the same whatever you choose.<br><br>With the plain Apify proxy (<code>{"useApifyProxy": true}</code>, typical in API calls) it starts on datacenter IPs and <b>switches to residential by itself</b> when Google keeps refusing. If you pick your own proxy URLs, the Actor keeps exactly what you chose.

## Actor input object example

```json
{
  "domains": [
    "nike.com"
  ],
  "maxAdsPerTarget": 50,
  "includeDetails": false,
  "mode": "all",
  "advertisersPerSearchTerm": 1,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

All ads (or check lines) in the default dataset.

# 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 = {
    "domains": [
        "nike.com"
    ],
    "maxAdsPerTarget": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,alexmorain/google-ads-transparency-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/l8xeoKWabNUPYYb8q/builds/5obmVKItFdHbah8tn/openapi.json
