# Google Ads Transparency Scraper - no silent empty results (`marekhartmann/google-ads-transparency-scraper`) Actor

Scrape every ad an advertiser is running from Google's Ads Transparency Center: creative, format, first and last shown, region. When the result is empty it tells you why - your typo or our bug - instead of billing you for nothing.

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

## Pricing

from $2.00 / 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.

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 Scraper

Get every ad a company is currently running on Google — search, display, video, shopping —
straight from Google's own **Ads Transparency Center**. Public data, no login, no personal
information: just who paid for which ad, where, and when.

**What makes this one different:** when the result is empty, it tells you *why*. Most scrapers
in this niche return an empty dataset and bill you for it.

### Output

One row per ad:

| Field | What it is |
|---|---|
| `advertiserName`, `advertiserId` | the verified advertiser Google shows |
| `creativeId`, `creativeType` | ad identity; `IMAGE` or `HOSTED_PREVIEW` |
| `formatCode` | Google's raw format code, kept as-is (they don't document it) |
| `firstShown`, `lastShown` | when the ad started and last ran, as ISO timestamps |
| `region`, `regionsSeenIn` | the region you asked for, and how many regions ran this ad |
| `imageUrl`, `previewUrl` | the creative itself |
| `detailUrl` | the ad's page in the Transparency Center |

### Input

```json
{
  "advertisers": ["Nike", "AR16735076323512287233"],
  "region": "US",
  "maxCreativesPerAdvertiser": 200
}
```

You can pass company names or advertiser IDs. Names are resolved inside the region you chose —
and if the closest match turns out to have no ads, the next candidate is tried before the Actor
gives up. That matters more than it sounds: searching for "Nike" returns a person called
*Nike Lee* first, who has never run an ad. An advertiser ID is exact, so use one when you know it.

### Why "no silent empty results" is the whole point

Google's endpoint has a trap in it. The request carries an opaque block of client context that
looks like decoration. Leave it out and the server answers **HTTP 200 with a body of `{}`** —
a perfectly successful request with zero ads in it.

A scraper built on that assumption looks like it works. It returns an empty dataset, charges
you per run, and you have no way to tell whether the advertiser really has no ads.

Worse: **the same empty `{}` also comes back when the advertiser genuinely doesn't exist.**
The response cannot tell you which of the two happened.

So this Actor asks a second question. When a result is empty, it re-runs the same request
against an advertiser it knows exists:

- that control question returns ads → the protocol is fine → **your** advertiser or region is
  the issue. The run **succeeds** with an empty dataset and an explanation. With pay-per-result
  billing you pay nothing.
- the control question is also empty → **Google changed something** and the run **fails**
  loudly, so you find out from an error and not from a silently empty spreadsheet.

The same rule covers the rest: every returned row is checked to actually belong to the
advertiser you asked for. If the filter silently stopped applying, the run fails instead of
handing you somebody else's ads.

The run report (`RUN_REPORT` in the key-value store) says per advertiser which of these happened.

### Uses

- see a competitor's entire live ad set, with creatives and how long each has run
- track when a campaign started or stopped
- compare the same advertiser across countries — the ad sets differ
- collect creative examples for a pitch or a swipe file

### Notes

- Regions matter. Ads are published per country: an advertiser can have hundreds of ads in the
  US and none in Slovakia. Pass a two-letter country code; an unknown one stops the run rather
  than quietly scraping something else.
- Only *verified* advertisers appear in the Transparency Center. That is Google's rule, not a
  limitation of this Actor.
- No login, no cookies. Sending credentials actually makes the endpoint reject the request.
- The connection speaks HTTP/1.1 on purpose. Over HTTP/2 the same request is answered with
  HTTP 429 every single time — measured, not guessed.

### Need a scraper for something else?

I build custom Apify Actors and browser automation the same way this one is built:
a verified result or an explicit failure, never an empty dataset sold as a success.

- My other Actors: https://apify.com/marekhartmann
- Code and test suites: https://github.com/marekhartmann-creator

Tell me the site and what you need out of it.

# Actor input Schema

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

Company names (for example "Nike") or advertiser IDs in the AR… form. A name is resolved to an ID in the chosen region; if it cannot be resolved you get a clear message instead of an empty result.

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

Two-letter country code (US, GB, DE, SK…) or Google's own four-digit region number. Ads are published per region, so the same advertiser has different ads in different countries. An unknown region stops the run instead of silently scraping the whole world.

## `maxCreativesPerAdvertiser` (type: `integer`):

Upper limit of ads collected for each advertiser. Large advertisers run thousands, so this keeps a run bounded and predictable.

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

Apify proxy settings. The Ads Transparency Center is public and does not need a proxy for small runs; use one for large or repeated runs.

## Actor input object example

```json
{
  "advertisers": [
    "Nike",
    "AR16735076323512287233"
  ],
  "region": "US",
  "maxCreativesPerAdvertiser": 200,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `creatives` (type: `string`):

One row per ad: advertiser, creative id and type, format code, first and last shown, image or preview URL, and a link to the ad in the Transparency Center.

## `runReport` (type: `string`):

Per-advertiser outcome and, when a result is empty, whose side the problem is on: your input or ours.

# 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": [
        "Nike"
    ],
    "region": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("marekhartmann/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 = {
    "advertisers": ["Nike"],
    "region": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("marekhartmann/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 '{
  "advertisers": [
    "Nike"
  ],
  "region": "US"
}' |
apify call marekhartmann/google-ads-transparency-scraper --silent --output-dataset

```

## MCP server setup

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