# Google Ads Transparency Scraper (Reliable, No Browser) (`steadyapi/google-ads-transparency-scraper`) Actor

Scrape Google Ads Transparency Center by advertiser, website domain or advertiser ID. Creatives, formats, landing domains, first/last shown dates. No browser = lower cost and fewer failures.

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

## Pricing

from $1.05 / 1,000 results

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 — Advertisers, Creatives & Landing Domains

Pull every ad a company is running through Google Ads, Search, Display, YouTube and Shopping — by website domain, advertiser name or advertiser ID. No headless browser, no login, no cookies to paste.

***

### The failure mode that breaks most Ads Transparency scrapers

The Ads Transparency Center has no public API. Everything goes through an internal RPC endpoint that answers happily for a while and then stops.

Measured on 2026-09-20: after roughly **30 requests from a single IP**, the endpoint stops returning JSON and starts returning a 3.3 KB **HTML rate-limit page** — sometimes with a `429`, sometimes not. A scraper that only checks the status code, or that assumes the body is JSON, will either crash or hand you an empty result and call it a win.

This Actor treats every one of those cases as a block:

- Non-JSON body on a `200` response → blocked.
- `429`, `403`, `503` → blocked.
- Socket-level hang → blocked. A hard `AbortSignal` cuts the request instead of letting a dead proxy tunnel burn two minutes of compute.

On a block it **throws the whole session away** — IP and cookies together — takes a new proxy session, backs off exponentially and retries. Refreshing cookies alone does nothing once the IP is on Google's list.

### What else is different

- **No headless browser.** Data comes straight from the JSON RPC. A 32-ad run finishes in about two minutes on 1 GB of RAM.
- **Every entry type in one field.** Domain, advertiser name, advertiser ID or a full Ads Transparency Center URL — the type is detected per entry, so you can mix them freely.
- **Multi-account brands are merged.** Big advertisers run ads from several accounts (Nike alone shows up under `Nike, Inc.`, `NIKE GLOBAL TRADING B.V.` and its media agency). Searching by name pulls the top matching accounts together instead of silently returning one of them.
- **Per-country last-shown dates.** With creative details on, each ad reports every country it ran in and when it was last seen there — resolved to real ISO country codes, all 249 of them.
- **Partial success.** A query that resolves to nothing returns a row with `error` and `errorType`, not a dead run.

### Input

| Field | Type | Notes |
| --- | --- | --- |
| `queries` | array | Domain (`nike.com`), advertiser name (`Coca-Cola`), advertiser ID (`AR1673…`) or an Ads Transparency Center URL. Mix freely |
| `region` | string | ISO country code, or `anywhere` for worldwide |
| `maxAdsPerQuery` | integer | Stop after this many ads per entry. Default `50` |
| `maxAdvertisersPerQuery` | integer | How many advertiser accounts to merge when the entry is a name. Default `2` |
| `includeCreativeDetails` | boolean | Adds every creative variant and the per-country last-shown dates. One extra request per ad |
| `requestDelayMs` | integer | Default `700`. Lower is faster but hits the rate limit sooner |
| `proxyConfiguration` | object | Strongly recommended. Residential proxies give the highest success rate |

#### Example

```json
{
  "queries": [
    "nike.com",
    "Coca-Cola",
    "AR16735076323512287233",
    "https://adstransparency.google.com/advertiser/AR16735076323512287233?region=US"
  ],
  "region": "US",
  "maxAdsPerQuery": 50,
  "includeCreativeDetails": true
}
```

### Output

One row per ad creative.

```json
{
  "advertiserName": "Nike, Inc.",
  "advertiserId": "AR16735076323512287233",
  "advertiserLocation": "US",
  "creativeId": "CR03886179215074131969",
  "adUrl": "https://adstransparency.google.com/advertiser/AR16735076323512287233/creative/CR03886179215074131969?region=US",
  "creativeKind": "image",
  "formatCode": 1,
  "imageUrl": "https://tpc.googlesyndication.com/archive/simgad/2393121755386194318",
  "imageWidth": 348,
  "imageHeight": 219,
  "previewUrl": null,
  "videoId": null,
  "landingDomain": "nike.com",
  "firstShownAt": "2023-11-16T22:13:08.000Z",
  "lastShownAt": "2026-09-20T04:12:13.000Z",
  "variantCount": 5,
  "shownInRegions": [{ "region": "US", "lastShownDate": "2026-09-01" }],
  "query": "nike.com",
  "queryType": "domain",
  "region": "US",
  "error": null,
  "errorType": null,
  "scrapedAt": "2026-09-20T04:29:40.713Z"
}
```

`creativeKind` is derived from the creative payload itself: `image` when the ad ships a static image, `rendered` when Google returns a rendered preview frame, `video` when a YouTube video is attached. `formatCode` is the raw internal format value, exposed as-is rather than guessed at.

#### Failed queries

```json
{
  "query": "not-a-real-domain-xyz123.com",
  "queryType": "domain",
  "error": "No ads found for \"not-a-real-domain-xyz123.com\" in region US.",
  "errorType": "NO_RESULTS"
}
```

`errorType` is one of `NO_RESULTS`, `NOT_FOUND`, `BLOCKED`, `REQUEST_FAILED`, `DETAIL_UNAVAILABLE`, `INVALID_INPUT`, `UNEXPECTED_ERROR`.

#### Run summary

Every run writes `RUN_SUMMARY` to the key-value store, so an automation can assert on it instead of counting rows:

```json
{
  "requested": 5,
  "succeeded": 4,
  "failed": 1,
  "successRate": 80,
  "adsCollected": 32,
  "retries": 0,
  "detailFailures": 0,
  "proxySessionsUsed": 2,
  "region": "US"
}
```

### What people use it for

- **Competitor ad monitoring.** Which creatives a rival is running right now, and which ones they quietly killed.
- **Creative research.** Pull hundreds of live ads in a vertical before briefing a new campaign.
- **Agency and affiliate discovery.** `landingDomain` reveals which media agencies and affiliates buy traffic for a brand.
- **Ad longevity signals.** `firstShownAt` and `lastShownAt` show which creatives survived months of optimisation — the ones worth copying.
- **Political and regulated-ad tracking.** Region filtering works on every country Google reports.

### Notes and limits

- Only ads Google publishes in the Ads Transparency Center are available. Google caps very large advertisers, so a brand listed as "~9,000 ads" will not return all of them.
- Ad counts shown in the Transparency Center are approximate ranges by design.
- Searching by advertiser name needs an exact-ish match. If a name returns nothing, use the website domain instead — it is the more reliable key.
- `includeCreativeDetails` doubles the request count. Leave it off for broad sweeps, turn it on when you need variants and per-country dates.
- Every returned row, including rows that carry an `error`, counts as one result.

Found a bug or need a field that is missing? Open an issue on the Actor page.

### Code examples

Copy-paste Python and Node examples (zero dependencies) live in [github.com/steadyapi/scrapers](https://github.com/steadyapi/scrapers). The Python one for this Actor is `python/google_ads_transparency.py`.

Minimal call with plain HTTP:

```bash
curl -X POST "https://api.apify.com/v2/acts/steadyapi~google-ads-transparency-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' -d @input.json
```

### Use it from AI agents (MCP)

Add this Actor as a tool in Claude, Cursor or any MCP client through the Apify MCP server:

```text
https://mcp.apify.com/?tools=steadyapi/google-ads-transparency-scraper
```

Authenticate with your Apify token (OAuth is also supported). The agent can then call the Actor directly and read the results.

### FAQ

**What can I search by?**
A website domain (nike.com), an advertiser name (Coca-Cola), an advertiser ID (AR...) or a full Ads Transparency Center URL. Mix them in one run.

**Does it include YouTube and display ads?**
Yes. Search, display and video creatives are returned, each with its format, first and last shown dates and preview links.

**Can I limit results to one country?**
Set `region` to a country code, or `anywhere` for worldwide results with the list of regions each ad ran in.

**Do I need a Google Ads account?**
No. The Ads Transparency Center is public and no login is used.

# Actor input Schema

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

One entry per line. Each entry can be a website domain (nike.com), an advertiser name (Nike, Inc.), an advertiser ID (AR16735076323512287233), or a full Ads Transparency Center URL. The type is detected automatically.

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

Only return ads that were served in this country. Choose 'anywhere' to search worldwide.

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

Stop after this many ads for each entry in the list above. Large advertisers can have tens of thousands of ads, so keep this sane.

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

Only used when the entry is an advertiser name. Big brands run ads from several advertiser accounts, so the top matches are merged. Ignored for domains and advertiser IDs.

## `includeCreativeDetails` (type: `boolean`):

Adds one extra request per ad and returns every creative variant plus the per-country last-shown dates. Slower and more expensive, so it is off by default.

## `requestDelayMs` (type: `integer`):

Pause between requests. Lower is faster but hits Google's rate limit sooner. 700 ms is a good balance.

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

Google rate-limits a single IP after roughly 30 requests, so a proxy is strongly recommended. Residential proxies give the highest success rate.

## Actor input object example

```json
{
  "queries": [
    "nike.com"
  ],
  "region": "US",
  "maxAdsPerQuery": 50,
  "maxAdvertisersPerQuery": 2,
  "includeCreativeDetails": false,
  "requestDelayMs": 700,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Advertiser, creative, format, image or preview URL, landing domain and first/last shown dates for every ad.

## `runSummary` (type: `string`):

requested / succeeded / failed / successRate, ads collected, retries and how many proxy sessions were used.

# 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.com"
    ],
    "region": "US",
    "maxAdsPerQuery": 50,
    "maxAdvertisersPerQuery": 2,
    "requestDelayMs": 700,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyapi/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 = {
    "queries": ["nike.com"],
    "region": "US",
    "maxAdsPerQuery": 50,
    "maxAdvertisersPerQuery": 2,
    "requestDelayMs": 700,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("steadyapi/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 '{
  "queries": [
    "nike.com"
  ],
  "region": "US",
  "maxAdsPerQuery": 50,
  "maxAdvertisersPerQuery": 2,
  "requestDelayMs": 700,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call steadyapi/google-ads-transparency-scraper --silent --output-dataset

```

## MCP server setup

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