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

Scrape ads from the Google Ads Transparency Center by keyword, domain, advertiser ID or URL. Get headlines, descriptions, images, YouTube links, first/last shown dates and regions for Search, YouTube, Shopping, Maps and Play ads. Filter by country, format, platform and date. No login needed.

- **URL**: https://apify.com/briskpebble/google-ads-transparency-scraper.md
- **Developed by:** [Brisk Pebble](https://apify.com/briskpebble) (community)
- **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 results

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

Extract ad creatives from the [Google Ads Transparency Center](https://adstransparency.google.com/). Search by **keyword**, **website domain**, **advertiser ID** or **Transparency Center URL** and get every ad an advertiser runs on Google Search, YouTube, Shopping, Maps and Play: headline, description, images, video links, first/last shown dates, and the regions it ran in.

The Actor calls the same JSON endpoints the Transparency Center web app uses, so it doesn't need a browser. That makes it fast and cheap to run.

### What you can do with it

- **Competitor research.** See what ads, offers and messages a competitor is running right now, and how long each one has been live.
- **Creative inspiration.** Pull headlines, descriptions, CTAs, images and videos at scale.
- **Monitoring.** Schedule daily runs with a date filter to catch new ads as they launch.
- **Political ad research.** Election ads include impression and spend ranges, targeting and per-region data.
- **Lead generation.** Check which domains advertise on Google at all, and how much.

### Input

| Field | Description |
|---|---|
| **Search Query** | `nike` (keyword), `nike.com` (domain), `AR16735076323512287233` (advertiser ID) or a URL like `https://adstransparency.google.com/advertiser/AR16735076323512287233?region=US`. |
| More search queries / URLs | Run many queries in one go. Each one gets its own **Max Results** budget. |
| **Max Results** | Maximum ads saved per query (`0` = no limit). Default `100`. |
| Region | Only ads shown in one country (`US`, `GB`, `DE`, …) or `anywhere`. |
| Platforms | `SEARCH`, `YOUTUBE`, `SHOPPING`, `MAPS`, `PLAY`. |
| Ad format | `TEXT`, `IMAGE`, `VIDEO`. |
| Shown from / until | Only ads shown within this date range. |
| Topic | `all` or `political` (political needs a region). |
| Advertisers per keyword | How many matching advertisers a keyword expands to (default 1, best match). |
| Extract ad text, images and videos | Opens each ad's preview to get the copy and media. On by default. |
| Include full ad details | Adds per-country dates and impressions, targeting, topic, payer and all variations. |

#### How queries are interpreted

| Query | Treated as |
|---|---|
| `AR` + 20 digits | Advertiser ID: all ads of that advertiser. |
| `nike.com`, `https://www.nike.com/us` | Domain: all ads that point to that domain, from any advertiser. |
| Transparency Center URL | Advertiser, domain or single ad. A `region` in the URL overrides the Region filter. |
| Anything else | Keyword: matched against advertiser names (the site's search box). Exact name matches win, then the advertisers with the most ads. If no advertiser matches, the first suggested domain is used. |

Example input:

```json
{
    "searchQuery": "nike.com",
    "searchQueries": ["adidas", "AR16735076323512287233"],
    "maxResults": 200,
    "region": "US",
    "platforms": ["YOUTUBE"],
    "format": "VIDEO",
    "dateFrom": "2026-09-01",
    "extractAdContent": true,
    "includeAdDetails": false
}
```

### Output

One dataset item per ad. Example (trimmed):

```json
{
    "query": "nike.com",
    "searchType": "domain",
    "region": "US",
    "advertiserId": "AR16735076323512287233",
    "advertiserName": "Nike, Inc.",
    "isAdvertiserVerified": true,
    "creativeId": "CR12733663556793270273",
    "format": "VIDEO",
    "adUrl": "https://adstransparency.google.com/advertiser/AR16735076323512287233/creative/CR12733663556793270273?region=US",
    "firstShown": "2026-10-19T15:25:01Z",
    "lastShown": "2026-10-19T17:01:44Z",
    "daysShown": 1,
    "headline": "Nike® Pegasus Plus 2",
    "description": "Comfort for your fastest miles. The Nike Pegasus Plus 2.",
    "displayUrl": "www.nike.com/",
    "callToAction": "Learn more",
    "landingPageUrl": null,
    "imageUrl": "https://tpc.googlesyndication.com/simgad/14944213133031273137",
    "imageUrls": ["https://tpc.googlesyndication.com/simgad/14944213133031273137"],
    "videoId": null,
    "videoUrl": null,
    "previewUrl": "https://displayads-formats.googleusercontent.com/ads/preview/content.js?...",
    "matchedDomain": null,
    "scrapedAt": "2026-10-19T18:00:00.000Z"
}
```

With **Include full ad details** turned on, each item also has `servedRegions`, `regionStats` (per-country first/last shown dates, impression ranges and per-platform impressions where Google publishes them), `euImpressionsLow/High`, `topic`, `targeting`, `paidBy`, `containsSyntheticMedia` and `variations`.

The dataset has two views: **Overview** (thumbnail, advertiser, copy, dates) and **Ad copy and media**. Export to JSON, CSV, Excel or XML, or read it through the Apify API.

#### Notes on the data

- **Text ads** are listed by Google as a rendered screenshot (`imageUrl`). With content extraction on, the Actor also reads the ad's renderer to get the real headline and description. Dynamic Search Ads show Google's own placeholder (`<dynamically generated based on landing page content>`) as the headline.
- **Impressions and spend** are only published for political ads, and for ads shown in the EU (with full details on).
- **Counts are approximate.** Google itself shows ranges ("~8,000 ads").
- Fields that Google does not provide for an ad are `null`, never guessed.

### Cost and performance

Each page of 40 ads is one request. Content extraction and full details each add about one request per ad, so turn them off when you only need IDs, dates and formats. Runs default to 1 GB memory and 10 parallel requests.

If you set a **maximum cost per run**, the Actor stops cleanly once it is reached. You are never charged for results it doesn't save.

#### Monetization (for the Actor owner)

The Actor works with Apify's pay-per-event setup out of the box:

| Event | Charged when |
|---|---|
| `apify-actor-start` | The run starts (synthetic, charged by the platform). |
| `apify-default-dataset-item` | Each ad is saved to the dataset (synthetic, charged by the SDK on every push). |

Optionally, define a custom event named `result` instead of the dataset-item event and the Actor charges it per ad. Either way, it reads the charge result on every push and stops as soon as the user's maximum cost per run is reached. Pay-per-result pricing is also supported (`ACTOR_MAX_PAID_DATASET_ITEMS`).

### Blocking and proxies

Google rate-limits by IP. The Actor rotates sessions (each with its own cookies and proxy IP), backs off on HTTP 429/403, CAPTCHA pages and `/sorry/` redirects, and retries with a fresh session. Apify Proxy is on by default. If a run logs many blocked requests, switch to **Residential** proxies or lower **Max concurrency**. An ad that can't be enriched after all retries is still saved with its basic fields and an `errors` note.

### Local development

```bash
npm install
npm test          # unit tests + an end-to-end run against a local mock of the endpoints
npm run build
apify run --input='{"searchQuery":"nike.com","maxResults":20}'
```

Deploy with `apify push`.

#### How it works

| Step | Endpoint |
|---|---|
| Keyword → advertisers/domains | `POST /anji/_/rpc/SearchService/SearchSuggestions` |
| List ads (40 per page, token pagination, filters) | `POST /anji/_/rpc/SearchService/SearchCreatives` |
| Ad details | `POST /anji/_/rpc/LookupService/GetCreativeById` |
| Ad copy and media | `GET` the ad's `content.js` preview renderer, and decode its `overlay=` parameter (base64 → gzip → protobuf) |

Bodies are form-encoded `f.req=<JSON>` with protobuf field numbers as keys. The field numbers and enums in `src/constants.ts` and `src/rpc.ts` (format: TEXT=1, IMAGE=2, VIDEO=3; platform: PLAY=1, MAPS=2, SEARCH=3, SHOPPING=4, YOUTUBE=5; dates as `YYYYMMDD`) come from the protobuf descriptors in the Transparency Center's own frontend bundle.

### Legal

This Actor only collects data Google publishes in its public Ads Transparency Center, about advertisers rather than private individuals. You are responsible for using the data in line with Google's terms and the laws that apply to you (for example GDPR). This Actor is not affiliated with or endorsed by Google.

# Actor input Schema

## `searchQuery` (type: `string`):

A keyword or advertiser name (e.g. <code>nike</code>), a website domain (e.g. <code>nike.com</code>), a Google advertiser ID (e.g. <code>AR16735076323512287233</code>) or a Transparency Center URL.

## `searchQueries` (type: `array`):

Optional. Scrape several advertisers, domains or keywords in one run. Each query gets its own Max Results budget.

## `startUrls` (type: `array`):

Optional. Paste advertiser, domain or single-ad URLs from adstransparency.google.com, e.g. <code>https://adstransparency.google.com/advertiser/AR16735076323512287233?region=US</code>. A <code>region</code> in the URL overrides the Region filter.

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

Maximum number of ads to save for each search query. Set to 0 for no limit.

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

Only ads shown in this country. "Anywhere" searches all regions.

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

Only ads shown on these Google platforms. Leave empty for all platforms.

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

Only ads of this format.

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

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

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

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

## `topic` (type: `string`):

Political ads include impression and spend ranges. Requires a specific Region.

## `maxAdvertisersPerKeyword` (type: `integer`):

When a query is a keyword (not a domain or ID), how many matching advertisers to scrape. Exact name matches come first, then advertisers with the most ads.

## `extractAdContent` (type: `boolean`):

Open each ad's preview to get the headline, description, display URL, image and YouTube video links. Adds one request per ad.

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

Also fetch each ad's detail page: regions it ran in with per-country dates and impressions, targeting (for political/EU ads), topic, who paid for the ad and all creative variations. Adds one request per ad.

## `deduplicateAds` (type: `boolean`):

Save each ad only once even if several queries find it.

## `includeRawData` (type: `boolean`):

Attach the unprocessed API response to every ad, for debugging or custom parsing.

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

Google rate-limits by IP address. Apify Proxy is recommended; switch to Residential proxies if you see many blocked requests.

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

Maximum number of parallel requests. Lower it if you see many blocked requests.

## `maxRequestRetries` (type: `integer`):

How many times to retry a failed or blocked request (each retry uses a fresh session and IP).

## Actor input object example

```json
{
  "searchQuery": "nike.com",
  "searchQueries": [
    "adidas.com",
    "AR16735076323512287233",
    "puma"
  ],
  "maxResults": 100,
  "region": "anywhere",
  "format": "ALL",
  "topic": "all",
  "maxAdvertisersPerKeyword": 1,
  "extractAdContent": true,
  "includeAdDetails": false,
  "deduplicateAds": true,
  "includeRawData": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 10,
  "maxRequestRetries": 5
}
```

# Actor output Schema

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

No description

## `adsWithContent` (type: `string`):

No description

# 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 = {
    "searchQuery": "nike.com",
    "maxResults": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("briskpebble/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 = {
    "searchQuery": "nike.com",
    "maxResults": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("briskpebble/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 '{
  "searchQuery": "nike.com",
  "maxResults": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call briskpebble/google-ads-transparency-scraper --silent --output-dataset

```

## MCP server setup

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