# Google Ads Transparency Scraper + Competitor Ad Report (`zerahdata/google-ads-intelligence`) Actor

Every Google ad a competitor runs, by domain, advertiser name or ID: text, image and video ads with first/last shown dates, plus a per-advertiser report by format, platform and country, longest-running ads, changes since the last run and competitor comparison.

- **URL**: https://apify.com/zerahdata/google-ads-intelligence.md
- **Developed by:** [Zerah Data](https://apify.com/zerahdata) (community)
- **Categories:** Marketing, Lead generation, 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 ad rows

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?

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 + Competitor Ad Report

See **every Google ad a competitor is running** - on Search, YouTube, Shopping, Maps and Google Play - and get a **ready-made report** on each advertiser: how many ads they run, which formats, platforms and countries they invest in, which ads have been running the longest (their likely winners), what is new, and what changed since your last check.

Data comes from Google's public [Ads Transparency Center](https://adstransparency.google.com). No Google account, no API key and no browser needed.

### What you get

| Row type | What it contains |
|---|---|
| `ad` | One row per ad: advertiser, format (text / image / video), first and last shown date, days running, days shown, running now (yes/no), preview image or preview link, link to the ad in the Transparency Center, and the headline, description and display URL when Google exposes them. |
| `report` | One per query: total ads (Google's count), split **by format, by platform and by country**, ads running now, new ads in the last 7 / 30 days, median days running, the **longest-running live ads**, newest ads, first and last seen dates, advertiser accounts behind the domain, and plain-English insights. |
| `comparison` | When you enter 2+ competitors: one side-by-side row (total ads, video share, YouTube share, Shopping share, new ads, longest-running ad, top country) with the leader in each. |
| `change` | With *Track changes* on: new ads, ads that stopped, restarted ads and the change in total ads since the previous run. Schedule it and you have a competitor ad monitor. |
| `info` / `error` | Clear messages for unknown countries, names with no advertiser, throttling or a spending limit - the run never fails silently. |

Optional **ad details** (one extra lookup per ad) add: every country where the ad ran with its own first/last dates, the Google platforms it ran on, **impression ranges** where Google discloses them (EU/EEA countries), topic category, the "paid for by" name (often the agency), number of variations and extra ad copy.

### Example report (real run, shortened)

```json
{
  "type": "report",
  "query": "nike.com",
  "totalAds": "60,000–70,000",
  "byFormat": { "TEXT": { "ads": "50,000–60,000", "sharePct": 83.3 }, "IMAGE": { "ads": "6,000–7,000", "sharePct": 9.8 }, "VIDEO": { "ads": "4,000–5,000", "sharePct": 6.8 } },
  "byPlatform": { "GOOGLE_SEARCH": { "ads": "50,000–60,000" }, "GOOGLE_SHOPPING": { "ads": "20,000–30,000" }, "YOUTUBE": { "ads": "10,000–20,000" }, "GOOGLE_MAPS": { "ads": "10,000–20,000" } },
  "byCountry": { "DE": { "ads": "10,000–20,000" }, "FR": { "ads": "10,000–20,000" }, "ES": { "ads": "9,000–10,000" }, "US": { "ads": "8,000–9,000" }, "JP": { "ads": "400–500" } },
  "startedLast30Days": 4,
  "longestRunningLiveAds": [{ "advertiserName": "Nike Retail BV", "format": "IMAGE", "firstShown": "2021-10-25", "runDays": 1828 }],
  "insights": [
    "nike.com: Google lists 60,000–70,000 ads (in all countries, all formats, all platforms, any date).",
    "Main format: TEXT (83.3% of ads); video is 6.8%.",
    "Platform mix: search 49.9%, shopping 22.7%, youtube 13.6%.",
    "Biggest of the checked countries: DE (10,000–20,000), FR (10,000–20,000), ES (9,000–10,000).",
    "4 new ads started in the last 30 days (1 in the last 7).",
    "Ads come from 9 advertiser accounts; the largest is Nike Retail BV."
  ]
}
```

With ad details on, an ad row also gets for example `"countriesShown": ["NL","ES","DE","LU","AT","FR","..."]`, `"platforms": ["GOOGLE_MAPS","GOOGLE_SEARCH","GOOGLE_SHOPPING"]`, `"impressionsMin": 20000`, `"paidBy": "Nike Retail BV"` and per-country first/last dates.

### How to use it

1. Enter one or more **domains** (`nike.com`), **advertiser names** (`Decathlon`) or **advertiser IDs** (`AR18378488041124659201`).
2. Optional filters: **countries** (any of 230+; `US`, `DE`, `JP`, `BR`, `KR`, `AE`, `IN`, `TR`, `PL`, `ID`, `VN`...), **format**, **platforms**, **date range** or *shown in the last N days*, *only ads running now*.
3. Choose how many ads per query and whether you want ad details.
4. Run. Open the **Ads**, **Reports**, **Comparison** and **Changes** tabs of the output.

The empty input `{}` runs a small example for `nike.com`.

#### Monitoring competitors

Turn on **Track changes**, give the watch list a **monitor name** and schedule the actor (daily or weekly). Each run adds a `change` row per competitor: new ads with their copy and links, ads that stopped running and how the total ad count moved.

### Use cases

- **Competitor research** - see a rival's whole Google Ads footprint in one run.
- **Creative inspiration** - ads that have run for months or years are usually the ones that work.
- **Market entry** - which competitors advertise in Japan, Brazil or the Gulf, and with which formats.
- **Agency pitches and audits** - a clear, data-backed snapshot of a prospect's or client's ad activity.
- **Brand protection** - find out who advertises on your brand's domain or name.
- **Lead generation** - find companies that are actively spending on Google Ads.

### Pricing

Pay only for what you get:

| Event | Price |
|---|---|
| Ad row | $0.0015 per ad |
| Ad details (optional) | $0.0015 per ad with details |
| Advertiser report | $0.005 per report |

Example: 3 competitors x 100 ads with reports = 300 x $0.0015 + 3 x $0.005 = **$0.47**. Comparison, change, info and error rows are free. The run stops cleanly at your spending limit.

### Input reference

| Field | Default | Notes |
|---|---|---|
| `queries` | `["nike.com"]` when empty | Domains, advertiser names or AR... IDs. Up to 50. |
| `countries` | all | 2-letter codes or English names. |
| `format` | `ALL` | `TEXT`, `IMAGE`, `VIDEO`. |
| `platforms` | all | `GOOGLE_SEARCH`, `YOUTUBE`, `GOOGLE_SHOPPING`, `GOOGLE_MAPS`, `GOOGLE_PLAY`. |
| `shownWithinDays` / `dateFrom` / `dateTo` | any time | Date the ad was shown. |
| `onlyActive` | `false` | Ads shown in the last 2 days. |
| `maxAdsPerQuery` | `20` | `0` = report only. |
| `includeAdDetails` | `false` | Countries, platforms, EU impressions, topic, payer, variations. |
| `reportDepth` | `full` | `full`, `standard`, `basic`, `none`. |
| `reportCountries` | 10 major markets | Countries counted in the report. |
| `trackChanges` / `monitorName` | off / `default` | Change history between runs. |
| `outputMode` | `all` | `adsOnly`, `reportsOnly`. |
| `maxRunSeconds` | `300` | The run stops cleanly at this limit and keeps everything found so far; skipped queries get a `TIME_LIMIT` row. |

### Good to know

- **Counts are Google's own ranges** (e.g. "8,000–9,000"); shares use the middle of each range.
- **"Running now"** means shown in the last 2 days. **Days running** is first to last shown; **days shown** is the number of days Google reports the ad actually ran.
- Google lists ads from advertisers that completed its verification. Very small advertisers may have no ads listed.
- Impression numbers are published by Google only for some regions (mainly the EU/EEA); platform lists per ad are also mostly published for EU/EEA ads.
- Google's history reaches back about 5 years, so the oldest ads show a first-shown date about 5 years ago.
- Video ads come with a preview link; image and text ads come with a preview image.
- A self-check (record `RUN_REPORT` in the key-value store) counts missing fields, duplicates and failed lookups on every run.

### FAQ

**Do I need a proxy?** Google limits how many requests one IP can make. Apify datacenter proxy (the default) is enough for most runs; the actor spreads requests across IPs, switches routes and retries automatically. If Google throttles every IP, you get a clear `BLOCKED` row instead of a silent failure - start the run again a few minutes later (a new run usually gets a fresh IP) or choose the RESIDENTIAL group for large runs.

**Can a run hang?** No. Every request has a timeout, paging has a hard cap, and the whole run is bounded by `maxRunSeconds` (default 5 minutes). Whatever was found before the limit is saved and charged; nothing after it is.

**Can I get all 60,000 ads of a big brand?** Use filters (country, format, platform, last N days) to focus. The report gives you the full counts without downloading every ad.

**Is this legal?** The actor reads only public information that Google publishes for transparency. Check your own use against your local rules.

# Actor input Schema

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

One per line. A website domain (nike.com), an advertiser name as Google shows it (Decathlon) or an advertiser ID from the Transparency Center (AR18378488041124659201). Two or more queries add a side-by-side comparison row.

## `countries` (type: `array`):

Only ads shown in these countries. 2-letter codes (US, DE, JP, BR, AE) or English names. Empty = all countries.

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

Only ads of this format.

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

Only ads shown on these Google platforms. Empty = all platforms.

## `shownWithinDays` (type: `integer`):

Only ads shown within this many days. 0 = any time (Google keeps several years of history). Ignored when a date range is set.

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

Optional start of the date range (YYYY-MM-DD).

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

Optional end of the date range (YYYY-MM-DD). Defaults to today when a start date is set.

## `onlyActive` (type: `boolean`):

Keep only ads shown in the last 2 days.

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

How many ad rows to return per domain/advertiser. 0 = report only (no ad rows). Big brands have tens of thousands of ads; Google returns the most recently shown first.

## `includeAdDetails` (type: `boolean`):

One extra lookup per ad: every country where the ad ran with first/last dates, the Google platforms it ran on, impression ranges where Google discloses them (EU/EEA), topic category, 'paid for by' name, number of variations and extra text copy. Charged as an extra event per ad.

## `maxDetailedAdsPerQuery` (type: `integer`):

Cap on detail lookups per query (only when ad details are on).

## `reportDepth` (type: `string`):

The report row per query: total ads, split by format, platform and country (Google's own counts), ads running now, new ads, longest-running ads, first/last seen and plain-English insights.

## `reportCountries` (type: `array`):

Countries counted in the full report. Default: US, GB, DE, FR, IN, BR, JP, AU, CA, ES. When you filter by two or more countries, those are used.

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

A name like 'Decathlon' matches several country accounts. This many (largest first) are included; the others are listed in the run warnings with their IDs.

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

Remember the ads of each query and add a 'change' row: new ads, ads that stopped, restarted ads and the change in total ads since the previous run. Schedule the run daily or weekly to monitor competitors.

## `monitorName` (type: `string`):

Keeps separate change history for different watch lists. Use the same name on every scheduled run.

## `outputMode` (type: `string`):

Which rows to write to the dataset.

## `maxRunSeconds` (type: `integer`):

The run stops cleanly after this many seconds and returns everything found so far, with a TIME_LIMIT row for anything skipped. Google slows down large runs, so raise this for many queries or thousands of ads.

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

Google throttles one IP after about 60-80 requests. Apify datacenter proxy is enough; the actor rotates IPs automatically.

## Actor input object example

```json
{
  "queries": [
    "nike.com",
    "adidas.com"
  ],
  "countries": [],
  "format": "ALL",
  "shownWithinDays": 0,
  "onlyActive": false,
  "maxAdsPerQuery": 20,
  "includeAdDetails": false,
  "maxDetailedAdsPerQuery": 50,
  "reportDepth": "full",
  "reportCountries": [],
  "maxAdvertisersPerName": 3,
  "trackChanges": false,
  "monitorName": "default",
  "outputMode": "all",
  "maxRunSeconds": 300,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per ad.

## `reports` (type: `string`):

One insight report per query, plus the comparison.

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

Counts, warnings, requests and data-quality checks.

# 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",
        "adidas.com"
    ],
    "countries": [],
    "reportCountries": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zerahdata/google-ads-intelligence").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",
        "adidas.com",
    ],
    "countries": [],
    "reportCountries": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("zerahdata/google-ads-intelligence").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",
    "adidas.com"
  ],
  "countries": [],
  "reportCountries": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call zerahdata/google-ads-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zerahdata/google-ads-intelligence"
        }
    }
}
```

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/g2L91d3qqawBLpoYf/builds/LFpTBrve5zE6LZiOX/openapi.json
