# Google Ads Transparency Center Scraper (`ceo.sss/google-ads-transparency-scraper`) Actor

See every ad an advertiser is running on Google (Search, YouTube, Shopping, Maps, Play) with first/last shown dates, format, preview creative and ad text, by advertiser ID, advertiser name or domain. Pay per ad.

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

## Pricing

from $2.00 / 1,000 ad scrapeds

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/platform/actors/running/actors-in-store#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

See every ad a company is running on Google (Search, YouTube, Shopping, Maps, Play) with first/last shown dates, format, preview creative, regions and ad text, as JSON/CSV/Excel. Look up by advertiser ID, advertiser name or website domain. Pay per ad returned.

Data source: [adstransparency.google.com](https://adstransparency.google.com), Google's public ad library. No login, no browser, plain HTTP against the same endpoints the site itself uses.

### What you get per ad

```json
{
  "advertiserId": "AR01625195283841286145",
  "advertiserName": "Shopify Inc.",
  "creativeId": "CR13279474632198455297",
  "format": "TEXT",
  "firstShown": "2026-07-06",
  "lastShown": "2026-08-17",
  "daysShown": 44,
  "previewImageUrl": "https://tpc.googlesyndication.com/archive/simgad/10560228437528131731",
  "adUrl": "https://adstransparency.google.com/advertiser/AR01625195283841286145/creative/CR13279474632198455297?region=anywhere",
  "query": "Shopify -> Shopify Inc.",
  "regions": ["US"],
  "regionDetails": [{ "region": "US", "lastShown": "2026-08-17" }],
  "text": [
    "Shopify",
    "www.shopify.com/",
    "Cheap Wholesale Products for Resale: 2026 Buying Guide",
    "Cheap wholesale products for resale can deliver high margins. Learn where to source, how to vet suppliers, and which items sell best online. 100+ Professional Themes."
  ]
}
```

| Field | Always | Meaning |
|---|---|---|
| `advertiserId`, `advertiserName` | yes | Google advertiser (AR...) and its verified name |
| `creativeId` | yes | Google creative ID (CR...), unique per ad; rows are deduplicated on it |
| `format` | yes | `TEXT`, `IMAGE` or `VIDEO` (Google's classification) |
| `firstShown`, `lastShown` | almost always | Dates the ad was first/last served (UTC). Missing only when Google omits them |
| `daysShown` | yes | Google's count of days the ad was served |
| `previewImageUrl` | when Google stores a screenshot | Static preview image of the ad |
| `adUrl` | yes | The ad's page on the Transparency Center |
| `matchedDomain` | domain queries | The domain that matched your query |
| `advertiserUnverified` | when true | Advertiser has not completed Google's verification |
| `query` | yes | Which input entry produced this row |
| `regions`, `regionDetails` | with `includeCreativeDetails` | Countries the ad was shown in, with per-country first/last dates where Google provides them |
| `text`, `creativeImageUrls` | with `includeCreativeDetails`, when Google renders the ad as HTML | Visible ad copy (headline, description, display URL...) and creative image URLs |
| `previewScriptUrl` | when Google renders the ad as HTML | Google's preview renderer URL for the ad |
| `payerName` | with details, when different from advertiser | The entity that paid for the ad |
| `raw` | with `includeRaw` | Parsed upstream response, for debugging |

Fields the source does not provide are omitted rather than filled with nulls or guesses. In particular there is no click-through/landing URL and no impression or spend numbers for non-political ads. Google does not expose them.

### Input

```json
{
  "advertiserIds": ["AR16735076323512287233"],
  "advertiserNames": ["Shopify"],
  "domains": ["notion.so"],
  "region": "anywhere",
  "format": "all",
  "platform": "all",
  "dateFrom": "2026-01-01",
  "dateTo": "2026-08-17",
  "maxAdsPerAdvertiser": 100,
  "includeCreativeDetails": false
}
```

| Field | Default | Notes |
|---|---|---|
| `advertiserIds` | `[]` | `AR...` IDs from any adstransparency.google.com/advertiser/AR... URL. Most precise |
| `advertiserNames` | `[]` | Free text. Google's name search is a prefix match that is not ranked by size, so the actor fetches 50 candidates and picks the verified advertiser with the most ads. The choice is logged (`"Shopify" -> Shopify Inc. (AR0162..., CA, ~80000 ads)`). Raise `maxAdvertisersPerName` to take more than one |
| `domains` | `[]` | Returns ads from every advertiser whose ads link to that domain: the brand itself, resellers, affiliates and anyone linking to a page hosted there (for `notion.so` that includes unrelated Notion-hosted sites). `matchedDomain` is set on each row; filter on `advertiserName` if you only want the brand |
| `region` | `anywhere` | ISO country code (`US`, `GB`, `IN`, ...). Filters to ads shown in that country |
| `format` | `all` | `text` / `image` / `video` |
| `platform` | `all` | `search` / `youtube` / `shopping` / `maps` / `play` |
| `dateFrom`, `dateTo` | none | `YYYY-MM-DD`. Ads that were served inside the window |
| `maxAdsPerAdvertiser` | 100 | Per advertiser ID (or per domain query). Newest activity first |
| `includeCreativeDetails` | false | Adds `regions` and, where possible, `text`. Costs 1-2 more requests per ad, so 100 ads take about 10 minutes at the default pace |
| `includeRaw` | false | Attach parsed upstream data |
| `minDelayMs` | 2500 | Pause between requests. Measured: one IP survives ~50 requests/min for only 1-2 minutes before Google's wall, but 20-25/min for hundreds of requests |
| `proxyConfiguration` | Apify datacenter | Sessions rotate automatically on a 429/captcha response |

At least one of `advertiserIds`, `advertiserNames`, `domains` is required.

### Pricing

Pay per event:

| Event | Price |
|---|---|
| Actor start | $0.005 per run |
| Ad scraped | $0.002 per ad row |

1,000 ads = about $2.00 plus a fraction of a cent for the start. Set "Maximum total charge" on the run to cap spend; the actor stops cleanly when it is reached and you keep everything already scraped. Details mode does not cost extra per ad, it only takes longer.

### Limitations

- **Ad text needs `includeCreativeDetails` and is not available for every ad.** Google stores many ads (especially older text ads and most image/video ads) only as a screenshot. Those rows have `previewImageUrl` but no `text`. There is no OCR.
- **No landing URLs, impressions or spend.** The Transparency Center does not publish them for regular ads. Political-ad spend data is out of scope.
- **Name search is Google's prefix search.** "Nike" matches 155 advertisers named Nike-something. The actor's ranking (verified, then ad volume) picks the right one in the cases tested (Nike, Shopify), but for exotic brands pass the domain or the advertiser ID instead. Check the log line that shows which advertiser was chosen.
- **Rate limits.** Google returns 429 or a captcha redirect once one IP exceeds roughly 50 requests/min for more than a minute; the wall then lasts about an hour for that IP. The actor paces requests (`minDelayMs`, default 2.5s = 24/min, measured safe for 200+ requests), uses HTTP/1.1 (Google walled the HTTP/2 client path much sooner in tests), and on a wall backs off (5s, 15s, 45s, 135s) and rotates to a new proxy session. Local runs without a proxy at the default pace are fine for a few hundred requests.
- One request fetches 40 ads, so 100 ads per advertiser is 3 requests. Domain queries with many advertisers can be large; use `maxAdsPerAdvertiser` and the filters.
- The protocol is Google's internal RPC (proto-JSON), documented at the top of `src/atc.js`. Field numbers can change without notice; the actor fails loudly (HTTP 400 in the log) rather than returning wrong data if that happens.

### FAQ

**Which advertiser did "advertiserNames" pick?** Read the run log: `"Shopify" -> Shopify Inc. (AR01625195283841286145, CA, ~80000 ads, verified)`. Every row also carries `query` so you can trace it back.

**Why fewer ads than the site shows?** `maxAdsPerAdvertiser` caps each target; region/format/platform/date filters apply at the source; and duplicate creatives (the same ad surfacing for two of your queries) are emitted once.

**Can I get the actual image or video file?** `previewImageUrl` is a direct image URL for screenshot previews. For HTML-rendered ads `creativeImageUrls` lists the images inside the ad (with details on). Video ads expose a thumbnail, not the video.

**How fresh is the data?** Live from Google at run time. `lastShown` of an active ad is usually today or yesterday.

**Does it use a browser?** No. Plain HTTP against `adstransparency.google.com/anji/_/rpc/...`, which is why it runs on 256 MB.

### Local development

```bash
npm install
## put your input in storage/key_value_stores/default/INPUT.json
apify run            # or: npm start
npm test             # offline parser checks + one live check (skips when offline / rate limited)
```

To exercise pay-per-event locally: `ACTOR_TEST_PAY_PER_EVENT=1 ACTOR_MAX_TOTAL_CHARGE_USD=4 npm start` charges $1 per event and stops after the start event plus three ads.

# Actor input Schema

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

Google advertiser IDs (start with AR...). Copy from an adstransparency.google.com/advertiser/AR... URL. Fastest and most precise option.

## `advertiserNames` (type: `array`):

Free-text advertiser names, e.g. "Shopify". Google's name search is a prefix match, so we fetch 50 candidates and pick the verified advertiser with the most ads (see maxAdvertisersPerName). The chosen advertiser is logged. For big brands prefer domains or advertiserIds.

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

Website domains, e.g. "notion.so". Returns ads from every advertiser whose ads link to that domain (matchedDomain is set on each row).

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

"anywhere" or an ISO 3166-1 alpha-2 country code (US, GB, IN, DE, ...). Only ads shown in that region are returned.

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

Only ads of this format (Google's classification). Text ads are often stored as screenshots.

## `platform` (type: `string`):

Only ads shown on this Google surface.

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

YYYY-MM-DD. Only ads that were shown on or after this date.

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

YYYY-MM-DD. Only ads that were shown on or before this date.

## `maxAdsPerAdvertiser` (type: `integer`):

Cap per advertiser ID (or per domain query). Ads are returned newest activity first.

## `maxAdvertisersPerName` (type: `integer`):

How many matching advertisers to scrape for each entry in advertiserNames.

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

Makes 1-2 extra requests per ad to fetch the regions the ad was shown in and, where Google renders the ad as HTML, the visible ad text and creative image URLs. Slower; roughly 3x the requests. Ads that Google only stores as a screenshot cannot yield text.

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

Attach the parsed upstream preview (and detail, if fetched) under `raw` for debugging.

## `minDelayMs` (type: `integer`):

Pause between requests. Measured: one IP survives about 50 requests/min for only 1-2 minutes before Google's 429/captcha wall, but 20-25/min for hundreds of requests. Lower only with residential proxies.

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

Apify datacenter proxy is on by default and is enough at the default delay. Switch to RESIDENTIAL if you see repeated 429s.

## Actor input object example

```json
{
  "advertiserIds": [
    "AR16735076323512287233"
  ],
  "region": "anywhere",
  "format": "all",
  "platform": "all",
  "maxAdsPerAdvertiser": 100,
  "maxAdvertisersPerName": 1,
  "includeCreativeDetails": false,
  "includeRaw": false,
  "minDelayMs": 2500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": []
  }
}
```

# Actor output Schema

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

One row per ad: advertiser, creative id, format, first/last shown, preview URL, ad text when available.

# 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 = {
    "advertiserIds": [
        "AR16735076323512287233"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": []
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ceo.sss/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 = {
    "advertiserIds": ["AR16735076323512287233"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": [],
    },
}

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

```

## MCP server setup

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