# Google Ads Transparency Scraper - Competitor Ads & Spend (`santhej/google-ads-transparency-scraper`) Actor

See every ad a competitor runs, from Google's Ads Transparency Center: creatives, formats, first/last shown dates and ad vertical. EU/UK markets also return per-region impression ranges and targeting signals. Search by advertiser domain or name. No API key.

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

## Pricing

Pay per event

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 — Competitor Ads & Spend

**See every ad a competitor is running on Google. Creatives, formats, first and last shown dates, per-region impression ranges, targeting signals and ad vertical — searchable by advertiser domain or by name. No API key, no login, no ad account.**

Google publishes every ad every verified advertiser runs, in its Ads Transparency Center. It is a goldmine and it is almost unusable by hand: no bulk export, no filtering across advertisers, one creative at a time. This Actor turns it into a clean table you can sort, diff and feed into a spreadsheet or a workflow.

Point it at `competitor.com` and get back their live ad library.

### What you get per ad

| Field | What it tells you |
|---|---|
| `advertiser_name` | The advertising entity, as Google verified it (often the legal entity, e.g. "Nike Retail BV") |
| `advertiser_domain` | The advertiser's website |
| `advertiser_id` | Google's stable advertiser identifier |
| `creative_id` | Stable identifier for this specific ad |
| `creative_url` | Direct link to the ad in Google's Ads Transparency Center |
| `format` | `text`, `image` or `video` |
| `first_shown` | The day this ad first ran |
| `last_shown` | The day it most recently ran — still today means it is live |
| `impressions_range_low` / `_high` | The impression band Google reports for the ad |
| `ad_vertical` | Google's own category for the ad, e.g. "Apparel", "Travel & Tourism", "Autos & Vehicles" |
| `ad_type` | `Commercial`, `Political`, `Shopping`, `Travel and hotel` or `Local services` |
| `targeting_demographics` | Whether demographic criteria were used to pick who saw it |
| `targeting_geo` | Whether location targeting was used |
| `targeting_contextual` | Whether contextual signals were used |
| `targeting_topics` | Whether topic-of-interest audiences were used |
| `targeting_customer_list` | Whether a customer list was used |
| `targeting_excluded` | Which of those criteria were used to **exclude** people |
| `regions` | Per-country delivery: country, impression band, first and last shown **per region** |
| `variant_count` | How many creative variants sit under this ad |
| `preview_url` / `preview_html` | The renderable ad preview |
| `region_code` | The market this run read |
| `scraped_at` | When the row was collected |

Plus one `advertiser_summary` row per advertiser — `total_ads_bucket_low`/`_high` (how many ads they are running in total), `advertiser_country`, `advertiser_legal_name`, `advertiser_url`, and `duns_number` for joining to business records — and a `run_summary` row with counts and the billing breakdown.

### Important: what Google publishes where

Read this before you plan a run, because it decides which fields you get.

**Everywhere:** advertiser, creative, format, first/last shown, variants, previews, per-region last-shown.

**European markets only:** `impressions_range_low`/`_high`, every `targeting_*` field, `ad_vertical`, and the impression bands inside `regions`. Those disclosures exist because the EU Digital Services Act compels them. Choose **Germany** or **France** and a global brand's ads come back with impressions and targeting attached. Choose **United States** and those fields are `null` — not because the scrape failed, but because Google does not publish them there.

The practical trick: most global advertisers run the same campaigns worldwide, so **query a European market to read the impression and targeting data, even for a brand you care about in the US.**

`duns_number` is populated for advertisers Google has business-verified — good coverage on large US advertisers (State Farm, Expedia), sparse elsewhere. It is `null` when Google has no mapping.

### Worked example

Input:

```json
{
  "advertiserDomains": ["nike.com", "zalando.de"],
  "advertiserNames": ["Booking.com"],
  "regionCode": "2276",
  "maxCreativesPerAdvertiser": 60
}
```

One output row:

```json
{
  "record_type": "ad_creative",
  "advertiser_id": "AR18378488041124659201",
  "advertiser_name": "Nike Retail BV",
  "advertiser_domain": "nike.com",
  "creative_id": "CR13736597046712860673",
  "creative_url": "https://adstransparency.google.com/advertiser/AR18378488041124659201/creative/CR13736597046712860673?region=DE",
  "format": "text",
  "first_shown": "2025-10-22",
  "last_shown": "2026-08-29",
  "impressions_range_low": 600000,
  "impressions_range_high": 700000,
  "ad_vertical": "Apparel",
  "ad_type": "Commercial",
  "targeting_demographics": true,
  "targeting_geo": true,
  "targeting_contextual": true,
  "targeting_topics": false,
  "targeting_customer_list": false,
  "targeting_excluded": [],
  "regions": [
    { "region_code": 2724, "region": "ES", "impressions_low": 600000, "impressions_high": 700000, "first_shown": "2025-10-22", "last_shown": "2026-08-28" },
    { "region_code": 2276, "region": "DE", "impressions_low": null, "impressions_high": 1000, "first_shown": "2025-10-24", "last_shown": "2026-08-25" },
    { "region_code": 2250, "region": "FR", "impressions_low": null, "impressions_high": 1000, "first_shown": "2025-10-23", "last_shown": "2026-08-28" }
  ],
  "variant_count": 1,
  "region_code": 2276,
  "scraped_at": "2026-08-29T06:51:55.876Z"
}
```

That single row says: Nike has been running this text ad for ten months, it is still live today, it did 600–700k impressions in Spain, and it is targeted on demographics, location and context but not on customer lists.

### Use cases

- **Competitive ad intelligence** — pull a competitor's entire live ad library and see exactly what they are saying, in which formats, and for how long.
- **Creative research** — sort by `first_shown` and `last_shown`: ads that have run for a year are the ones that work. Ads that ran for a week were killed.
- **Spend and scale estimation** — impression bands per country tell you where the budget is actually going, not where the marketing page says it goes.
- **Market-entry checks** — the `regions` array shows every country an advertiser is buying in, so you can see who is already in your market.
- **Agency pitches** — walk into a meeting with the prospect's own ad library and their competitors' side by side.
- **Ad-copy and landing-page inspiration** — `preview_html` and `preview_url` give you the actual creative.
- **Compliance and brand monitoring** — catch resellers, affiliates or impostors bidding on your brand.

### Pricing

Pay per event. No monthly fee, no setup fee, no minimum.

| Event | Price |
|---|---|
| Actor start | $0.001 per run |
| Ad creative | **$0.0003 per ad — $0.30 per 1,000** |

**You are charged only for ad creatives actually returned.** An advertiser with no ads in the selected market costs nothing, and failed lookups cost nothing.

Worked cost: **3 competitors × 100 ads each = 300 ads = $0.001 + 300 × $0.0003 = $0.091.** A full 1,000-ad deep dive on a single advertiser is **$0.301**.

### FAQ

**Do I need an API key or a Google Ads account?** No. Add a domain and run. The data comes from Google's public Ads Transparency Center.

**Domain or name — which should I use?** Domain, whenever you know it. It is unambiguous. Names are matched against Google's advertiser directory and the largest matching advertiser is chosen, which is right for big brands but can be ambiguous for small ones.

**Why is the advertiser name not the brand name?** Google verifies the legal entity that pays for the ads, so `nike.com` comes back as "Nike Retail BV". The domain is the reliable identifier.

**Why are impressions and targeting empty?** You queried a non-European market. See the section above — run the same advertiser against Germany or France to get them.

**Why did an advertiser return fewer ads than `total_ads_bucket_low`?** That bucket is the advertiser's total across all time and markets; a run returns the most recent ads for the market you selected, up to your limit.

**Can one brand have several advertiser IDs?** Yes — large companies register separate entities per region. Searching by domain returns the entity serving the market you selected.

**How many advertisers per run?** Up to 25, and up to 1,000 ads for each of them.

**Do I need a proxy?** No. It is off by default and that is the fastest, cheapest setting.

**Can I export it?** JSON, CSV, Excel, or straight into n8n, Make or Zapier via the Apify API.

### Related actors

- [Meta Ads Library Scraper](https://apify.com/santhej/meta-ads-library-scraper) — the Facebook and Instagram sibling of this Actor. Run both to see a competitor's paid presence across Google and Meta.
- [Similarweb Scraper](https://apify.com/santhej/similarweb-traffic-scraper) — how much traffic those ads are actually driving.
- [Google Search Results Scraper](https://apify.com/santhej/google-search-results-scraper) — who is winning the organic side of the same keywords.

***

*Tags: google ads transparency, ads transparency center, competitor ads, ad library, ad spy tool, google ad library, competitive intelligence, ad intelligence, creative research, ad monitoring, competitor analysis, ppc research, paid search intelligence, display ads, video ads, advertiser lookup, ad impressions, ad targeting, marketing research, adtech*

# Actor input Schema

## `advertiserDomains` (type: `array`):

Advertiser websites to pull ads for. Enter bare domains (nike.com) or full URLs — the scheme, path and www are stripped for you. This is the most reliable way to target a specific brand.

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

Advertiser names to look up, for when you do not know the domain. Each name is matched against Google's advertiser directory and the largest matching advertiser is used. Combine freely with domains.

## `regionCode` (type: `string`):

Which market's ad library to read. An advertiser's ads differ per country, and impression ranges plus targeting signals are published only for European markets.

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

How many ad creatives to collect for each advertiser. Ads are returned newest-first in pages of 40, so 100 covers recent activity while 1000 gives a deep archive.

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

Optional, and off by default. This Actor does not need a proxy, and leaving it off is the fastest and cheapest setting. Turn on a residential proxy only if you are collecting very large advertiser lists in a single run.

## Actor input object example

```json
{
  "advertiserDomains": [
    "nike.com"
  ],
  "advertiserNames": [],
  "regionCode": "2840",
  "maxCreativesPerAdvertiser": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Every ad creative found, with format, dates, impression ranges, targeting signals and ad vertical.

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

Advertisers processed, creatives collected, error count and billing breakdown for the run.

# 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 = {
    "advertiserDomains": [
        "nike.com"
    ],
    "advertiserNames": [],
    "regionCode": "2840",
    "maxCreativesPerAdvertiser": 100,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("santhej/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 = {
    "advertiserDomains": ["nike.com"],
    "advertiserNames": [],
    "regionCode": "2840",
    "maxCreativesPerAdvertiser": 100,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("santhej/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 '{
  "advertiserDomains": [
    "nike.com"
  ],
  "advertiserNames": [],
  "regionCode": "2840",
  "maxCreativesPerAdvertiser": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call santhej/google-ads-transparency-scraper --silent --output-dataset

```

## MCP server setup

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