# Google Ads Transparency Center Scraper & API (`brenton8907/google-ads-transparency-center`) Actor

Every Google ad an advertiser is running: creatives, formats, first/last shown, impression ranges, per-country reach. Search by domain, brand or advertiser ID. Competitor ads monitor: schedule it and get only new ads. Batch 'is this domain advertising?' checks for lead enrichment. No browser.

- **URL**: https://apify.com/brenton8907/google-ads-transparency-center.md
- **Developed by:** [Brenton Keller](https://apify.com/brenton8907) (community)
- **Categories:** Marketing, Lead generation, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 ad creatives

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?

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

## Google Ads Transparency Center Scraper

Pull every ad an advertiser is running on Google (Search, YouTube, Shopping, Maps, Play) from the
[Ads Transparency Center](https://adstransparency.google.com), as clean JSON/CSV. Search by **domain**,
**brand name**, **advertiser ID** or a pasted **Transparency Center URL**. Filter by country, format,
platform and date. Run it on a schedule with **"only new ads"** to get a feed of newly launched competitor
creatives. Use **advertiser-check mode** to answer *"is this company running Google Ads, and how many?"*
for thousands of domains at a fraction of a cent each.

No browser, no login. Talks to the Transparency Center's own data API directly with a browser-grade TLS
fingerprint, paces itself under Google's rate limits and rotates proxies automatically, so runs finish
fast and don't fail halfway.

**Try it free:** every Apify account includes free monthly usage, and 1,000 ads cost $1, so checking a competitor is free.

### Competitor ads monitor: schedule it, get an alert when new ads appear

This is the highest-value way to use the actor: a daily run that returns **only the ads you have not seen before**, delivered to Slack or email. Three steps, no code:

1. **Create a task** from this actor with the input below (one line per competitor domain or advertiser ID). Run it once; the first run seeds the list of known ads.
2. **Schedule it**: in the task, open *Schedule* (or Console → Schedules) and pick daily or hourly. Each run costs only the new ads it finds; a quiet day costs a few requests and no results.
3. **Alerts**: in the task's *Integrations* tab add **Slack** or **Gmail** on "run succeeded". Use the run's status message as the notification text; in monitor mode it reads `3 NEW ad(s) since last run across 5 target(s)` or `No new ads since last run`. Slack message template (Handlebars):

   ```
   *Google Ads monitor:* {{resource.statusMessage}}
   <https://console.apify.com/storage/datasets/{{resource.defaultDatasetId}}|Open the new ads>
   ```

   For "only notify when there is something new", route the run through **n8n / Make / Zapier** (Apify triggers exist for all three) and filter on the dataset item count.

```json
{
  "domains": ["nike.com", "adidas.com", "onrunning.com"],
  "onlyNewAds": true,
  "monitorStoreName": "running-shoes-competitors",
  "includeDetails": true,
  "maxAdsPerQuery": 200
}
```

Each new ad row carries `is_new: true`, the creative preview URL, format, first/last shown and (with `includeDetails`) served countries and surfaces, so the alert is actionable: what launched, where, in which format. Use one `monitorStoreName` per watchlist. Works with `advertiserIds`, `queries` (brand names) and `startUrls` too; add `region`, `format` or `surface` to narrow it (e.g. only new **YouTube** video ads in **DE**).

What it costs at scale: 5 competitors × ~10 new ads/day with details = 50 × $0.004 = **$0.20/day, ~$6/month**; without details, $1.50/month. Compare with ad-spy subscriptions at $50–100/month.

### What it does

| Mode | One row per | Use it for |
|---|---|---|
| `ads` (default) | ad creative | competitor creative research, ad libraries, longevity analysis (which ads run for months), creative monitoring |
| `advertisers` | matched advertiser | lead enrichment / qualification: is this domain advertising on Google right now, under which account(s), roughly how many ads |

### Who it's for

- **Performance marketers and agencies** tracking competitors' Google ads: new-creative alerts, formats, and how long each creative survives (an ad-spy tool for Google, YouTube and Shopping ads at pay-per-result prices).
- **Sales and lead-gen teams** enriching prospect lists (Clay, n8n, Make, Zapier, Google Sheets) with "runs Google Ads: yes/no + volume" as a buying signal.
- **Brand and compliance teams** watching for affiliates, resellers or impersonators advertising on their domain.
- **Researchers and journalists** studying advertising activity by region and platform.
- **AI agents**: every field is stable and typed, and the actor is exposed as an MCP tool via Apify.

### Input examples

**All ads for a domain, US, last 90 days, YouTube only**

```json
{
  "domains": ["allbirds.com"],
  "region": "US",
  "surface": "YOUTUBE",
  "dateFrom": "2026-06-01",
  "maxAdsPerQuery": 500
}
```

**A brand by name, with full per-country details**

```json
{
  "queries": ["Nike"],
  "maxAdvertisersPerQuery": 1,
  "includeDetails": true
}
```

**Paste Transparency Center URLs**

```json
{
  "startUrls": [{ "url": "https://adstransparency.google.com/advertiser/AR18378488041124659201?region=GB" }]
}
```

**Monitor by advertiser ID instead of domain** (see the monitor section above for scheduling and alerts)

```json
{
  "advertiserIds": ["AR18378488041124659201", "AR16735076323512287233"],
  "onlyNewAds": true,
  "monitorStoreName": "footwear-competitors"
}
```

**Lead enrichment: is each domain advertising?**

```json
{
  "domains": ["allbirds.com", "warbyparker.com", "some-local-plumber.com"],
  "mode": "advertisers",
  "region": "US"
}
```

### Output

#### `ads` mode

```json
{
  "creative_id": "CR04143963882324492289",
  "advertiser_id": "AR18378488041124659201",
  "advertiser_name": "Nike Retail BV",
  "format": "TEXT",
  "matched_domain": "nike.com",
  "first_shown": "2026-07-22T04:31:55+00:00",
  "last_shown": "2026-09-01T21:04:44+00:00",
  "days_shown": 43,
  "impressions": null,
  "impressions_low": null,
  "impressions_high": null,
  "preview_url": "https://tpc.googlesyndication.com/archive/simgad/12774179880874022668",
  "preview": { "script_url": "…", "iframe_url": null, "html_snippet": null, "image_url": "…" },
  "ad_url": "https://adstransparency.google.com/advertiser/AR18378488041124659201/creative/CR04143963882324492289?region=anywhere",
  "advertiser_url": "https://adstransparency.google.com/advertiser/AR18378488041124659201?region=anywhere",
  "query": "nike.com",
  "query_kind": "domain",
  "region": "US",
  "matched_total": { "low": 100000, "high": 200000 },
  "is_new": true
}
```

With `includeDetails: true` each row also gets:

```json
"details": {
  "served_countries": ["US", "GB", "DE", "FR", "CA"],
  "country_reach": [{ "country": "US", "first_served": "2026-07-22", "last_served": "2026-09-01", "impressions": null, "surfaces": [] }],
  "eu_reach": { "impressions": { "low": 10000, "high": 50000 }, "first_served": "2026-07-21", "last_served": "2026-09-01" },
  "variations": [ { "script_url": "…", "image_url": "…" } ],
  "targeting": { "categories": { "demographics": {"included": true}, "geography": {"included": true} } },
  "category": { "gcb_category_id": 10021, "vertical": 1 },
  "customer_name": "Nike Retail BV",
  "contains_synthetic_media": false
},
"countries": ["US", "GB", "DE", "FR", "CA"],
"surfaces": ["SEARCH", "YOUTUBE"]
```

Impression and spend ranges are only published by Google for EU-served ads and political ads; elsewhere they are `null`. Where they exist they are lifted to the top level as `impressions_low` / `impressions_high` (global) and `eu_impressions_low` / `eu_impressions_high` (EU reach from the details block), so CSV exports and the Overview table show them without digging into `details`.
`matched_total` is Google's estimate of how many ads match the whole query (useful even when you cap `maxAdsPerQuery`).

#### `advertisers` mode

```json
{
  "query": "allbirds.com",
  "query_kind": "domain",
  "region": "US",
  "is_advertising": true,
  "advertiser_id": "AR12345678901234567890",
  "advertiser_name": "Allbirds Inc",
  "creatives_count_low": 1000,
  "creatives_count_high": 2000,
  "advertisers_on_domain": 1,
  "formats_seen": ["IMAGE", "TEXT", "VIDEO"],
  "last_shown": "2026-09-02T01:46:39+00:00",
  "advertiser_url": "https://adstransparency.google.com/advertiser/AR12345678901234567890?region=US"
}
```

Domains with no ads return one row with `is_advertising: false`, so every input gets exactly one answer.

#### Advertiser-check rows for a domain

Each advertiser row for a domain carries **its own** ad count for that domain (`creatives_count_low` / `creatives_count_high`), the advertiser's billing `country`, `legal_name` and `unverified` flag from its Transparency Center profile, and the domain-wide total in `domain_creatives_count_low` / `domain_creatives_count_high`. This distinguishes the domain owner (Nike, Inc.: 7,000–8,000 ads on nike.com) from resellers and affiliates that also link to the domain (an agency with 500–600). Costs 2 extra requests per advertiser row; `maxAdvertisersPerQuery` caps it.

### Pricing

Pay per result, no subscription:

| Event | Price |
|---|---|
| Ad creative (`ads` mode) | $1.00 per 1,000 |
| Ad creative with full details (`includeDetails`) | $4.00 per 1,000 |
| Advertiser check row (`advertisers` mode) | $2.00 per 1,000 |

Plus normal Apify platform usage (a few cents per thousand ads; the actor runs in 256 MB and makes one request
per 40 ads). Failed targets are not charged.

### Limits and notes

- Google paginates to roughly **1,000 ads per query**. Split large advertisers by format, surface or date range to get more.
- Google keeps about **12 months** of ad history.
- A domain search returns every advertiser whose ads link to that domain, not only the domain owner. That can include resellers, affiliates and demo/test ads (`example.com` yields real ads from unrelated advertisers). Use `advertisers` mode to see who they are, or `advertiserIds` to pin one account.
- Brand-name queries are resolved through Google's own suggestion index; the largest exact/prefix match is used first. Use `advertiserIds` for precision.
- Ad-text (keyword) search is only available for political ads in Google's data and is not exposed here.
- Google rate-limits by IP. Keep the default Apify proxy on for anything beyond a few hundred ads; the actor rotates sessions on every rate-limit response and backs off, so runs complete instead of failing.
- If Google blocks every request (bot-check page or HTTP 429 after retries), the run fails with a clear error and nothing is charged. Enable Apify Proxy (residential) or raise `requestDelaySeconds`. If only some targets are blocked, the run succeeds and those targets get a row with an `error` field.
- Rows whose detail lookup failed (`details_error`) are charged at the base ad rate, not the enriched rate.
- Monitor state is per `monitorStoreName` (a named key-value store in your account). Do not run two monitor runs against the same store at the same time; use one store per watchlist or schedule.

### Use as an MCP tool / from code

The actor is callable from any MCP client through Apify's MCP server, and from code with the Apify API. Agent pattern: keep a watchlist, call with `onlyNewAds: true` on a schedule, and act only on the rows that come back.

```bash
curl -X POST "https://api.apify.com/v2/acts/<username>~google-ads-transparency-center/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"domains": ["allbirds.com"], "mode": "advertisers", "region": "US"}'
```

### Legal

This actor only reads data that Google publishes for public inspection in the Ads Transparency Center. Use it in
line with Google's terms and applicable law; you are responsible for how you use the output.

### Support

Open an issue on the actor page with the run ID and input. If Google changes the Transparency Center's data format, affected fields go null rather than failing the run; report which field and we will patch it. If the actor saved you time, a review helps other people find it.

# Actor input Schema

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

Landing-page domains, e.g. nike.com. Returns every ad whose landing page is on that domain, across all advertisers that use it.

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

Google advertiser IDs (start with AR…, from a Transparency Center URL). Fastest and most precise way to pull one advertiser's ads.

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

Free-text brand or company names, e.g. 'Nike', 'Allbirds'. Resolved to advertiser IDs via the Transparency Center suggestion API (best match first; set 'Max advertisers per query' to include more).

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

Paste adstransparency.google.com URLs (advertiser pages, search pages with ?domain= or ?text=). Parsed into the equivalent query.

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

'Ads' lists creatives. 'Advertiser check' is the cheap enrichment mode: for each domain/name, return the advertiser(s) found and how many ads they run, without listing the ads. Ideal for 'is this company running Google Ads?' at scale.

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

ISO 3166-1 alpha-2 country code (US, GB, DE, …) or ANYWHERE. Restricts results to ads shown in that country.

## `format` (type: `string`):

Only ads of this format.

## `surface` (type: `string`):

Where the ad was shown.

## `dateFrom` (type: `string`):

Only ads active on or after this date. Google keeps roughly 12 months of history.

## `dateTo` (type: `string`):

Only ads active on or before this date.

## `maxAdsPerQuery` (type: `integer`):

Cap per domain / advertiser / keyword. 40 ads per request. Google stops paginating at roughly 1,000 per query.

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

When a brand name or domain resolves to several advertiser accounts, how many to include (best match first).

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

One extra request per ad: per-country impression ranges, surfaces (Search/YouTube/…), targeting, product category, all creative variations. Billed as enriched record.

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

Remember creative IDs between runs (named key-value store) and output only ads not seen before. Schedule the actor to get a feed of newly launched competitor ads.

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

Named key-value store that holds seen creative IDs. Use different names for different watchlists.

## `includeRaw` (type: `boolean`):

Attach the untouched upstream record under 'raw'.

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

Google throttles by IP. Apify proxy (default) rotates exit IPs on rate limits. Residential proxies are not needed.

## `requestDelaySeconds` (type: `number`):

Pacing to stay under Google's per-IP rate limit. Lower is faster but risks 429s (which are retried with a fresh session).

## Actor input object example

```json
{
  "domains": [
    "nike.com"
  ],
  "mode": "ads",
  "region": "ANYWHERE",
  "format": "ALL",
  "surface": "ALL",
  "maxAdsPerQuery": 200,
  "maxAdvertisersPerQuery": 3,
  "includeDetails": false,
  "onlyNewAds": false,
  "monitorStoreName": "ads-transparency-monitor",
  "includeRaw": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "requestDelaySeconds": 1.5
}
```

# Actor output Schema

## `items` (type: `string`):

One row per ad creative (or per advertiser in advertiser-check mode), as JSON from the default dataset.

## `csv` (type: `string`):

The same rows as CSV, for spreadsheets.

## `run` (type: `string`):

The run page with the Overview table, status message and log.

# 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"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

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

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

```

## MCP server setup

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

```

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/YeWCNe1B0CbVnnAkd/builds/FfgEgabg6bu1hGlgx/openapi.json
