# Google Ads Transparency Scraper: Payer, OCR Text, EU Reach (`oswaldocarabano/google-ads-transparency-scraper`) Actor

Scrape Google Ads Transparency Center ads for any domain or advertiser: text, image and video ads with dates, archived image, ad text read by OCR, and per ad the payer, EU impressions by country and platform, targeting criteria and topic. Advertiser rows too. From $0.0015 per ad.

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

## Pricing

from $1.50 / 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 — every Google ad of a domain or advertiser, with the payer and the ad text

Extract ads from the **Google Ads Transparency Center** for any website or advertiser:
text, image and video ads with first and last shown dates, days served, the archived
image, the product and merchant of Shopping ads and **the ad text itself** — headline, display URL and description, read by OCR
when Google only publishes the ad as an image. Turn on details and every ad also
carries **who paid for it**, **EU impressions by country and by Google platform**, the
**audience selection** (targeting criteria) and the **topic**. Advertiser rows give the
legal name, verification, billing country, ad count and DUNS number when published.

**$0.0015 per ad.** No login, no cookies, no proxy setup — it is all included.

| Use it for | What you get |
|---|---|
| **Competitor ad monitoring** | every ad a rival runs, when it started, whether it is still live, and only the new ones on each scheduled run |
| **Ad copy research** | the actual headline and description of search ads, even the 96 % that Google only shows as a picture |
| **Agency and media-buyer intelligence** | the payer behind each ad — often an agency, not the brand |
| **EU / DSA research** | impressions ranges per EU country and per platform, and the targeting criteria the advertiser used |
| **Brand protection** | every advertiser sending traffic to your domain, verified or not |

***

### What actually arrives in a row

Every percentage on this page was measured on **13 Oct 2026** against the live
Transparency Center, with its sample size. A field whose fill rate we have not measured
is not promised.

**Ads** (`outputType: "ad"`, n = 2,369 ads across 22 domains in 10 countries):

| Field | Filled | Notes |
|---|---:|---|
| `creativeId`, `advertiserId`, `advertiserName`, `domain`, `format` | 100 % | stable Google IDs |
| `firstShown`, `lastShown`, `daysServed`, `isActive` | 100 % | `daysServed` is Google's own count of days on air |
| `advertiserVerified` | 100 % | 4.9 % of ads come from unverified advertisers |
| `archivedImageUrl` + `imageWidth`/`imageHeight` | 50.4 % | text ads 95.7 %, image 46.3 %, video 6.5 % |
| `previewContentJsUrl` | 49.6 % | Google's preview for HTML5 and video ads |
| **ad text** (`ocrText` or `headline`/`description`) | **94.0 % of text ads** | n = 298 text ads in US, ES, BR and PT |
| `displayUrl` | 86.2 % of text ads | |
| `ocrConfidence` | median 92 | p10 84 (n = 276) |
| **Shopping ads**: `productTitle`, `merchantName`, `productImageUrl` | **86.3 % of image ads** | n = 117 image ads, nike.com + zara.com in US and ES, 13 Oct 2026 |
| `locations` (store addresses attached to the ad) | 11.7 % of text ads | n = 274 text ads, same sample |
| `usesKeywordInsertion` | 8.4 % of text ads | the headline shows the advertiser's default text |
| `advertiserMatchesDomain` | 100 % of domain searches | true for 62.7 % — legal names often differ from the brand |

**Ads with details** (`outputType: "ad-detail"`, n = 132 detail pages):

| Field | Filled |
|---|---:|
| `variations` (every version of the ad) | 100 % (73.5 % have more than one) |
| `regions` (every country, with first/last day) | 100 % — median 1 country, p90 14, max 129 |
| **`payerName`** | **78 %** — different from the advertiser in 14 of 103 |
| `audienceSelection` (demographics, geography, contextual signals, topics of interest, customer lists — each included/excluded) | 50.8 % |
| `topicName`, `topicSource` | 50.8 % |
| `euImpressionsLow`/`High`, `euFirstShown`/`LastShown` | 46–50 % (ads shown in the EU) |
| `euPlatforms` (impressions by Google Search, YouTube, Shopping, Maps, Play) | 42.4 % |

Impressions are **ranges** published by Google under the EU Digital Services Act, so
they exist for EU/EEA countries only.

**Advertisers** (`outputType: "advertiser"`, n = 40): `name`, `disclosedName` and
`isVerified` 100 %, `legalName` 97.5 %, `nameType` 92.5 %, `billingCountry` 100 %,
plus `adCount` from Google's search and `dunsNumber` when Google publishes it (rare).

***

### Compared with the other Ads Transparency actors (14 Oct 2026)

Competitors read from each actor's input and dataset schema through the Apify API — not from
their READMEs — on 13 Oct 2026 (10 actors, 2,726 users in the last 30 days). Our numbers are
measured on **4,344 rows from 50 test runs on Apify on 14 Oct 2026** (50 of 50 succeeded,
rows charged = rows delivered in all 50). For competitors we count every declared data field
as always filled, which is their best case.

| | Market leader (918 users / 30 d) | Best typed output in the market | **This actor** |
|---|---|---|---|
| Price per ad | $0.002 | $0.002 | **$0.0015** |
| Filled data fields per ad row | at most 13 | at most 21 | **23.5 on average (median 25)** |
| Filled data fields per detailed ad row | — | at most 17 | **37.9 on average (median 41)** |
| Payer of the ad | — | — | **94 % of detailed ads** |
| Ad text (OCR or structured) | — | — | **85 % of all ad rows; 70 % read by OCR in English/Spanish/Portuguese** |
| Product title and merchant of Shopping ads | — | — | **yes (13 % of all ad rows)** |
| EU impressions by country **and** platform | partial | — | **75 % of detailed ads** |
| Audience selection (targeting) | — | — | **80 % of detailed ads** |
| Only new ads (monitoring) | yes | — | **yes** |
| Same ad searched in several regions | — | — | **delivered once, deduplicated** |
| Charges error or empty rows | — | — | **never** |

No competitor in the measured set declares a payer field; only one runs OCR and only one
exposes audience selection.

***

### Input

```json
{
  "outputType": "ad",
  "domains": ["nike.com"],
  "regions": ["US"],
  "maxResults": 100
}
```

- **domains**, **advertiserIds** (`AR` + 20 digits, or an advertiser page URL) and
  **advertiserNames** can be combined. A brand name usually maps to several legal
  entities; every one whose name contains all your words is included (up to 10).
- **regions**: one search runs per region and the same ad is delivered once. Leave it
  empty to search anywhere. All 244 regions of the Transparency Center are available.
- **formats** (text, image, video), **platforms** (Google Search, YouTube, Shopping,
  Maps, Play) and **dateFrom** / **dateTo** narrow the search.
- **onlyOwnAdvertiser** keeps only ads whose advertiser name contains the domain brand.
  It is off by default because legal names often differ: zara.com advertises as
  *Industria de Diseño Textil* and mercadolibre.com.mx as *Deremate*.
- **onlyNewAds** remembers what earlier runs with the same input delivered and returns
  only new ads — schedule it daily for alerts.
- **ocrText** (on by default) reads the ad text. **ocrAllFormats** also reads banners.
- **maxResults** is a hard cap, split fairly across your queries and regions.

If you leave domains, advertiser IDs and advertiser names empty, the run uses
`nike.com` as an example, so a test run always returns data.

### Output example (one ad)

```json
{
  "outputType": "ad",
  "creativeId": "CR01338187633339138049",
  "advertiserId": "AR10072600183532683265",
  "advertiserName": "Hubspot, Inc.",
  "advertiserVerified": true,
  "domain": "hubspot.com",
  "format": "text",
  "firstShown": "2022-06-29",
  "lastShown": "2026-10-13",
  "daysServed": 1527,
  "isActive": true,
  "archivedImageUrl": "https://tpc.googlesyndication.com/archive/simgad/3060238887385681912",
  "displayUrl": "www.hubspot.com/",
  "adTextSource": "ocr",
  "ocrText": "Sponsored HubSpot www.hubspot.com/ Free Email Signature Generator Create a free branded email signature for your business or team.",
  "ocrConfidence": 92,
  "ocrLanguage": "eng",
  "advertiserMatchesDomain": true,
  "region": "DE",
  "query": "hubspot.com",
  "matchedBy": "domain"
}
```

With `outputType: "ad-detail"` the same row adds `payerName`, `regions[]` (with
impressions and platforms per EU country), `euImpressionsLow`/`High`, `euPlatforms[]`,
`audienceSelection`, `topicName` and `variations[]`.

***

### Pricing

Pay per event, charged only **after** a row is delivered:

| Event | Price |
|---|---:|
| Ad | **$0.0015** |
| Ad with details | **$0.004** |
| Advertiser | **$0.002** |
| Actor start (platform minimum) | $0.00001 |

1,000 ads cost $1.50, OCR included. Error rows, empty results and anything that could not
be delivered are **never charged**. Within a run the same ad is never delivered or charged
twice; across runs, use **onlyNewAds** to pay only for new ads. If you set a maximum cost
for the run, the actor stops cleanly when it is reached.

### FAQ

**Is the ad text exact?** For about 4 % of text ads Google publishes the text as data and
you get it exactly (`adTextSource: "content.js"`, with sitelinks). The rest are images and
the text is read by OCR (`adTextSource: "ocr"`) with a confidence score; median confidence
was 92 out of 100 on 276 ads.

**Why are impressions empty for US ads?** Google publishes impression ranges only for ads
shown in the EU/EEA (Digital Services Act). Dates and countries are published everywhere.

**How far back does it go?** The Transparency Center keeps about a year of history after an
ad last ran.

**What if a search finds nothing?** You get one error row saying so, the run finishes green
and nothing is charged. Every problem is also listed in the run's `ERRORS` record.

**Do I need a proxy?** No. It is included in the price.

### Privacy

Advertiser and payer names are published by Google by law. Turn on **redactPersonNames**
to replace names that look like a private individual with `[person]`. Phone numbers and
emails inside an ad are kept for companies and redacted when the advertiser looks like a
private person. To ask for data about you to be removed from this actor's outputs, write to
privacy@actorstack.dev.

### Disclaimer

This actor reads **publicly available** data from the Google Ads Transparency Center, with
no login, no cookies and no account. It is not affiliated with or endorsed by Google. You
are responsible for how you use the data, including compliance with applicable law.

# Actor input Schema

## `outputType` (type: `string`):

ad = one row per ad (cheapest). ad-detail = the same ad plus the creative detail page: the payer, EU impressions by country and platform, audience selection (targeting criteria), topic and every variation (one extra request per ad, so it is priced separately). advertiser = one row per advertiser (legal name, verification, billing country, ad count, DUNS when published).

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

Websites whose ads you want, e.g. nike.com. Protocol, www and paths are removed for you. Google returns every ad that links to the domain, including resellers and agencies — see onlyOwnAdvertiser. If domains, advertiser IDs and advertiser names are all empty, the run uses nike.com as an example so a test run always returns data.

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

Advertiser IDs from the Ads Transparency Center (AR followed by 20 digits), or advertiser page URLs that contain one.

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

Advertiser names to look up, e.g. Nike. A brand name usually maps to several legal entities (Nike, Inc., Nike Retail BV…); every entity whose name contains all the words you typed is included, up to 10 per name.

## `regions` (type: `array`):

Countries where the ads were shown. One search runs per region and duplicates are removed, so the same ad is delivered once. Leave empty to search all regions at once (anywhere).

## `formats` (type: `array`):

Leave empty for all formats.

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

Where the ad ran, as the Ads Transparency Center filters it. Leave empty for all platforms.

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

Only ads shown on or after this date (YYYY-MM-DD). Google keeps about one year of history after an ad last ran.

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

Only ads shown on or before this date (YYYY-MM-DD).

## `onlyOwnAdvertiser` (type: `boolean`):

For domain searches, keep only ads whose advertiser name contains the domain brand (Nike, Inc. for nike.com) and skip resellers and agencies. Off by default because legal names often differ from the brand: measured on 13 Oct 2026, zara.com advertises as Industria de Diseño Textil and mercadolibre.com.mx as Deremate. Every row carries advertiserMatchesDomain either way.

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

Monitoring mode: remembers the ads delivered by earlier runs with exactly the same input (in a key-value store named google-ads-transparency-monitor in your account) and delivers only new ones. The first run delivers everything.

## `ocrText` (type: `boolean`):

For text ads, read the headline, display URL, description, sitelinks and store locations; for Google Shopping ads (image format), read the product title, product image and merchant. About 96 % of text ads are only published as an image, so their text is read with OCR (ocrText, ocrConfidence, ocrLanguage); the rest come as structured text. Included in the price.

## `ocrAllFormats` (type: `boolean`):

Run OCR on image ads too (banners). Slower; text ads are always read when Read the ad text is on.

## `ocrLanguages` (type: `array`):

Language models the OCR may use. The model is picked by the ad region and switched when the text is detected to be in another of these languages.

## `redactPersonNames` (type: `boolean`):

Replace advertiser and payer names that look like a private person (not a company) with \[person]. Google publishes these names by law; this option is for buyers who must not store them.

## `maxResults` (type: `integer`):

Hard cap on rows delivered (and charged) in this run, split fairly across your queries and regions. Error rows are never charged.

## `maxConcurrency` (type: `integer`):

Requests in flight at once. 6 was measured clean on 13 Oct 2026 (120 of 120 requests, about 11 per second); higher values finish sooner on big runs but more requests need a retry.

## Actor input object example

```json
{
  "outputType": "ad",
  "domains": [
    "nike.com"
  ],
  "regions": [
    "US"
  ],
  "formats": [],
  "platforms": [],
  "onlyOwnAdvertiser": false,
  "onlyNewAds": false,
  "ocrText": true,
  "ocrAllFormats": false,
  "ocrLanguages": [
    "eng",
    "spa",
    "por"
  ],
  "redactPersonNames": false,
  "maxResults": 20,
  "maxConcurrency": 6
}
```

# Actor output Schema

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

Every row of this run as JSON. Use the dataset views (Ads, Ads with details, Advertisers) for a table, or export to CSV or Excel.

## `errors` (type: `string`):

Rows that could not be delivered and why (never charged). Always present, empty when nothing went wrong.

## `summary` (type: `string`):

Counts for this run: delivered, charged, skipped as duplicates or already seen, requests and timing.

# 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"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("oswaldocarabano/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"],
    "maxResults": 20,
}

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

```

## MCP server setup

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