# Google Ads Transparency Scraper (`brilliant_gum/google-ads-transparency-scraper`) Actor

Track any advertiser's Google ads: real headline, description, CTA and the landing page they send traffic to, impressions by country, and how long each ad has run. Search by brand name — no login. Flags what's new since your last run.

- **URL**: https://apify.com/brilliant\_gum/google-ads-transparency-scraper.md
- **Developed by:** [Yuliia Kulakova](https://apify.com/brilliant_gum) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Scraper — Ad Copy, Landing Pages & Impressions

![Google Ads Transparency Scraper](https://i.imgur.com/4GA0djx.png)

See every ad a competitor runs on Google — the actual headline, the call to action, the landing page they send traffic to, how many countries it runs in, and how long they have been paying for it.

Search by brand name. No login, no API key, no Google account.

***

### Why this one

Most Google Ads Transparency scrapers hand you a list of creative IDs and preview links, then leave you clicking through them one by one. This one returns the ad itself.

| | Typical scraper | This scraper |
|---|---|---|
| Find an advertiser | Paste a Transparency Center URL | Type `Booking.com` |
| Ad headline & description | Preview link only | **Extracted as text** |
| Landing page | — | **Real destination URL** |
| Countries | Raw IDs like `2246` | **`FI` — Finland** |
| Impressions | Blank when open-ended | **`10M+`** |
| How long it has run | — | **`daysRunning` + longevity tier** |
| Changes since last run | — | **New / ended ads flagged** |

#### The signal nobody else gives you

Google publishes no spend or performance data. But an advertiser only keeps paying for an ad that works — so **how long a creative has been running is the closest thing to a performance signal there is.**

Every ad is tagged:

| Tier | Meaning |
|---|---|
| `EVERGREEN` | 365+ days — a proven winner |
| `LONG_RUNNER` | 90+ days |
| `ESTABLISHED` | 30+ days |
| `RECENT` | 7+ days |
| `FRESH` | under a week — a live experiment |

Filter to `minDaysRunning: 90` and you are looking at exactly the creatives your competitor has validated with real budget.

***

### Quick start

```json
{
  "advertiserNames": ["Booking.com"],
  "maxAdsPerAdvertiser": 50
}
```

That is the whole input. Names resolve to Google advertiser IDs automatically.

***

### What you get

A real record, exactly as returned:

```json
{
  "advertiserId": "AR02934798844673654785",
  "advertiserName": "Booking.com",
  "payer": "Booking.com B.V.",
  "adFormat": "TEXT_AD",
  "headline": "Buche mit kostenloser Stornierung",
  "longHeadline": "Buche die meisten Unterkünfte mit kostenloser Stornierung auf Booking.com",
  "description": "Buche in 110.000 Reisezielen weltweit",
  "callToAction": "Jetzt buchen",
  "displayUrl": "booking.com",
  "landingPageUrl": "http://www.booking.com/index.html?aid=2344050",
  "youtubeUrl": "https://www.youtube.com/watch?v=q9G0MVtU4z0",
  "impressionsLabel": null,
  "countriesShown": ["CH"],
  "regionCount": 1,
  "firstShownDate": "2026-06-18",
  "lastShownDate": "2026-08-02",
  "daysRunning": 46,
  "longevity": "ESTABLISHED",
  "isActive": true,
  "adDetailsUrl": "https://adstransparency.google.com/advertiser/AR029.../creative/CR020...",
  "scrapedAt": "2026-08-02T07:35:49.590Z"
}
```

This is an unedited record. Note `impressionsLabel: null` — Google withholds impressions for most creatives, and we leave the field empty rather than invent a number.

Ad copy comes back in whatever language it ran in — German, Japanese, Spanish, Chinese — correctly decoded.

***

### Field reference

#### Always present

| Field | Description |
|---|---|
| `advertiserId`, `advertiserName` | Google advertiser identity |
| `creativeId`, `adFormat` | `TEXT_AD`, `IMAGE_AD` or `VIDEO_AD` |
| `firstShownDate`, `lastShownDate` | Run window |
| `daysRunning`, `longevity`, `isActive` | Derived performance signals |
| `regions`, `regionCount`, `countriesShown` | Countries with ISO codes and names |
| `adDetailsUrl`, `scrapedAt` | Source link and collection time |

#### Depends on the advertiser

Google does not disclose the same things about every advertiser, and creative
templates differ between campaigns. These are real measurements from 25-ad
samples, not a best case:

| Field | Booking.com | Shopify | Nike | Adidas |
|---|---|---|---|---|
| `headline`, `description`, `callToAction` | 56% | 96% | 0% | 0% |
| `landingPageUrl`, `displayUrl` | 76% | 96% | 4% | 28% |
| `impressionsLabel` | 16% | 28% | 0% | 4% |
| `payer` | 100% | 12% | 100% | 0% |

**Read this before you buy.** Some advertisers publish a lot, others almost
nothing. Run the free trial against *your* target advertiser first and check the
coverage you actually get — do not assume the numbers from another brand carry
over. Every record always includes the identity, dates, longevity and country
fields above, whatever else is missing.

**`adCopySource` tells you how the copy was obtained:**

| Value | Meaning |
|---|---|
| `template` | Read from the ad's own structured data. Exact headline, description and CTA. |
| `rendered` | Reconstructed from the rendered creative because the ad declares no structured copy. Usually app-install ads, where the only text is the app name and the button — expect short, sometimes fragmentary values. |
| `null` | No text found; media and statistics are still present. |

Long-running ads skew toward `rendered`, so if you filter with `minDaysRunning` expect that label to dominate. Landing pages are recovered either way.

The remaining media fields:

| Field | When present |
|---|---|
| `youtubeId`, `youtubeUrl` | Video-backed creatives |
| `thumbnailUrl` | ~96% of image ads — the banner image itself |
| `imageUrls` | Creative media pulled from the preview |
| `isNewSinceLastRun` | From the second run on, with `trackChanges` |

***

### Filters — pay only for what you need

Big advertisers run millions of creatives. Booking.com alone has roughly 16 million. Filters run before results are billed.

**Sent to Google directly:** `adFormat`, `platform`, `region`, `dateFrom`/`dateTo`.

**Applied to assembled records:** `minDaysRunning`, `maxDaysRunning`, `minImpressions`, `maxImpressions`, `activeOnly`, `keywords`, `excludeKeywords`, `regions`, `requireAdCopy`, `requireLandingPage`.

#### Find a competitor's proven winners

```json
{
  "advertiserNames": ["Airbnb"],
  "minDaysRunning": 90,
  "activeOnly": true,
  "requireAdCopy": true,
  "maxAdsPerAdvertiser": 100
}
```

#### Catch new campaigns early

```json
{
  "domains": ["shopify.com"],
  "maxDaysRunning": 14,
  "adFormat": "TEXT",
  "maxAdsPerAdvertiser": 50
}
```

#### Monitor a market weekly

```json
{
  "advertiserNames": ["Nike", "Adidas", "Puma"],
  "trackChanges": true,
  "activeOnly": true,
  "maxAdsPerAdvertiser": 100
}
```

Schedule it. From the second run on, every ad carries `isNewSinceLastRun`, and the log reports how many ads launched and how many stopped.

***

### Use cases

- **Competitive intelligence** — the exact copy, offers and landing pages rivals are spending on.
- **Creative research** — filter to `EVERGREEN` and read the messaging that survived a year of optimisation.
- **Agency reporting** — show a client what their category is running, with dates and reach.
- **Affiliate and partner tracking** — landing URLs keep their tracking parameters, so partner IDs are visible.
- **Market entry** — see which countries a brand actually advertises in before you launch.
- **Ad archiving** — a dated record of creatives, which disappear from Google once campaigns end.

***

### Honest limitations

Everything below is a property of Google's data, not a gap we plan to close. No scraper can give you these from the Transparency Center.

**Image ads carry no text.** For a banner, the words are painted into the picture — there is no text layer to read. You get `thumbnailUrl` (the image itself) plus the full statistics, but no `headline` or `landingPageUrl`. Text and video ads have both. If you need copy specifically, set `adFormat: "TEXT"`.

**Coverage varies a lot between advertisers.** In our samples ad copy ranged from 96% of creatives (Shopify) to 0% (Nike, Adidas), and `payer` from 100% to 0%. This is Google's disclosure, not our extraction — the same code returns everything for one brand and little for another. Test your own targets on the free trial.

**Impressions are missing for most ads.** Google publishes them for a minority of creatives and stays silent on the rest — the aggregate and the per-country numbers are both blank there. We report what exists and leave the rest `null` rather than guessing.

**Impressions are ranges, never exact.** `700K–800K` is Google's own bucket. The top bucket is open-ended, reported as `10M+`.

**No spend, clicks, CTR or conversions.** Not published for commercial advertisers at all. `daysRunning` is the proxy.

**No keyword targeting.** Google never discloses which keywords an advertiser bids on.

**Advertiser matching picks the busiest match.** Search a common brand name and several advertisers may match; the one running the most ads wins. For guaranteed precision, pass `advertiserIds` directly — you can copy them from a Transparency Center URL.

**Google rate-limits heavy use.** The actor rotates connections and backs off automatically. On very large runs it may still slow down; if a run reports throttling, re-run it a few minutes later.

***

### FAQ

**Do I need a Google account or API key?**
No. The Transparency Center is public. No credentials anywhere.

**Are proxies included?**
Yes, configured automatically. Leave the Proxy field empty.

**How fast is it?**
About 25 ads per 10 seconds with full enrichment — roughly 7 minutes per 1,000 ads.

**Can I get the ad copy for image ads?**
No — the text is baked into the image. Use `thumbnailUrl` to view or archive the creative.

**Why does one advertiser return fewer ads than the Transparency Center shows?**
`maxAdsPerAdvertiser` caps the run so you do not pay for millions of records. Raise it to go deeper; ads come back newest first.

**How do I track a competitor over time?**
Keep `trackChanges: true` and put the actor on a schedule. Each run flags new ads and logs the ones that stopped.

**Does it work outside the US and EU?**
Yes. Regions decode to ISO country codes worldwide — a single creative commonly runs in 100+ countries.

**Can I search by keyword instead of by advertiser?**
Not directly — Google's index is organised by advertiser. Pass the advertisers you care about, then use `keywords` to filter their ads by wording.

***

Maintained by **brilliant\_gum**. Issues and feature requests are welcome on the Issues tab.

# Actor input Schema

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

Search by brand name, e.g. \["Nike", "Booking.com"]. Names are resolved to Google advertiser IDs automatically — no need to look anything up first. When several advertisers share a name, the one running the most ads is used.

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

Search by advertiser domain, e.g. \["nike.com"]. Useful when a brand name is ambiguous.

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

Google advertiser IDs, e.g. \["AR02934798844673654785"]. Visible in the URL on adstransparency.google.com. Fastest option — skips name resolution.

## `adFormat` (type: `string`):

TEXT = search ads. IMAGE = display/banner. VIDEO = YouTube. Text and video ads carry readable copy and a landing page URL; image ads have their text painted into the picture, so for those you get the image plus full statistics. Switch to "All formats" once you know which you need.

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

Where the ad was shown.

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

ISO 2-letter country code to restrict ads by where they ran, e.g. "US", "DE". Leave blank for all countries.

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

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

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

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

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

Large brands run millions of creatives — this caps what you pay for. Ads are returned newest first.

## `fetchAdCopy` (type: `boolean`):

Pulls the actual headline, description, call-to-action, display URL and the real landing page URL for each ad. Adds about a second per ad. This is what makes the output useful — keep it on unless you only need IDs.

## `fetchDetails` (type: `boolean`):

Pulls the payer name, estimated impression range, and per-country impressions with first/last shown dates. Adds about a second per ad.

## `trackChanges` (type: `boolean`):

Remembers which creatives were seen last time, then marks each ad as new or unchanged and logs which ads stopped running. Ideal for scheduled competitor monitoring.

## `activeOnly` (type: `boolean`):

Keep only ads with an impression in the last 7 days. Google's data lags a day or two, so the window is deliberately generous.

## `minDaysRunning` (type: `integer`):

Advertisers keep paying only for ads that work, so a long run is the closest thing to a performance signal. Try 90 to see a competitor's proven winners.

## `maxDaysRunning` (type: `integer`):

Use a small value to catch fresh experiments, e.g. 14.

## `minImpressions` (type: `integer`):

Keep ads whose estimated impression range reaches at least this number. Requires impressions extraction.

## `maxImpressions` (type: `integer`):

Keep ads below this estimated impression level.

## `keywords` (type: `array`):

Keep ads whose copy, brand or landing URL contains at least one of these. Case-insensitive.

## `excludeKeywords` (type: `array`):

Drop ads matching any of these words.

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

ISO 2-letter codes. Applied to the per-country impression breakdown, so it needs impressions extraction enabled.

## `requireAdCopy` (type: `boolean`):

Drop creatives that carry no headline or description — a few pure-video placements have none.

## `requireLandingPage` (type: `boolean`):

Drop creatives whose destination URL could not be recovered.

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

Optional. Proxies are included and configured automatically — leave this empty.

## Actor input object example

```json
{
  "advertiserNames": [
    "Nike",
    "Adidas"
  ],
  "domains": [
    "nike.com",
    "apple.com"
  ],
  "advertiserIds": [
    "AR02934798844673654785"
  ],
  "adFormat": "TEXT",
  "platform": "ALL",
  "region": "US",
  "dateFrom": "2026-01-01",
  "dateTo": "2026-06-30",
  "maxAdsPerAdvertiser": 25,
  "fetchAdCopy": true,
  "fetchDetails": true,
  "trackChanges": true,
  "activeOnly": false,
  "minDaysRunning": 90,
  "minImpressions": 10000,
  "keywords": [
    "free shipping",
    "discount"
  ],
  "excludeKeywords": [
    "job",
    "career"
  ],
  "regions": [
    "US",
    "GB"
  ],
  "requireAdCopy": false,
  "requireLandingPage": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing all scraped ad creatives. One item per ad with advertiser info, creative metadata, targeting regions, dates, and media URLs.

## `datasetId` (type: `string`):

ID of the default dataset containing scraped ad records.

## `consoleUrl` (type: `string`):

Link to this actor run in Apify Console.

# 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 = {
    "advertiserNames": [
        "Booking.com"
    ],
    "adFormat": "TEXT",
    "maxAdsPerAdvertiser": 25,
    "fetchAdCopy": true,
    "fetchDetails": true,
    "trackChanges": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("brilliant_gum/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 = {
    "advertiserNames": ["Booking.com"],
    "adFormat": "TEXT",
    "maxAdsPerAdvertiser": 25,
    "fetchAdCopy": True,
    "fetchDetails": True,
    "trackChanges": True,
}

# Run the Actor and wait for it to finish
run = client.actor("brilliant_gum/google-ads-transparency-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "advertiserNames": [
    "Booking.com"
  ],
  "adFormat": "TEXT",
  "maxAdsPerAdvertiser": 25,
  "fetchAdCopy": true,
  "fetchDetails": true,
  "trackChanges": true
}' |
apify call brilliant_gum/google-ads-transparency-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=brilliant_gum/google-ads-transparency-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/sqPIhkLtOQeapn5bk/builds/Bx9yPVOMntSuoJqyx/openapi.json
