# Google Ads Transparency Scraper: Ad Library API (`varnmill/google-ads-transparency-scraper`) Actor

Every ad a company runs on Google Search, YouTube, Shopping, Maps and Play from the Google Ads Transparency Center: ad copy, images, videos, first and last shown dates, EU impressions and audience targeting, plus a daily new-ads watcher. Search by advertiser name, domain or ID; no login.

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

## Pricing

from $0.35 / 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: Ad Library API

Every ad a company runs on **Google Search, YouTube, Shopping, Maps and Play**, read from Google's public [Ads Transparency Center](https://adstransparency.google.com/): headline, copy, call to action, images or YouTube video, landing page, first and last shown dates, and for ads shown in the EU the impressions per country and audience targeting. Search by advertiser name, website domain, advertiser ID or URL. No login, no browser, no API key.

**Try it:** Click Start with the default input (`nike.com`, 20 most recently shown ads): **20 ads in 7 seconds for about $0.01** (run on October 21, 2026). Each ad comes with its advertiser, format, dates, days shown and a link to the ad on the Transparency Center; text ads that Google stores as screenshots come with the screenshot URL.

Run on Apify on **October 14, 2026 (19:51 UTC)**. Input: `grammarly.com`, region Germany, platform YouTube, format video, 8 ads, per-country details on. It finished in 18 seconds.

| # | Headline | CTA | YouTube video | First shown | Days shown | Countries | EU impressions |
|---|---|---|---|---|---|---|---|
| 1 | Ideas to essays | Download | JQX7SdUJGeo | 2026-03-25 | 205 | 22 | 125K–150K |
| 2 | Write with purpose, work with pride | Download | G6wkI_fhsCY | 2026-03-25 | 205 | 18 | 20K–25K |
| 3 | Edit. Get set. Send. | Download | G6wkI_fhsCY | 2026-05-28 | 139 | 12 | under 1K |
| 4 | Work leveled up | Download | G6wkI_fhsCY | 2026-03-25 | 200 | 22 | 300K–350K |
| 5 | Think. Write. Fix. Done. | Download | QoI7QEvAljo | 2026-03-25 | 205 | 22 | 1M–1.25M |
| 6 | Check originality | Download | VBAQr43v5hc | 2026-03-25 | 205 | 22 | 225K–250K |
| 7 | Instant citations | Download | daGUD3CB7UU | 2026-03-25 | 152 | 9 | under 1K |
| 8 | Edit. Get set. Send. | Download | uN1N6VCtQvY | 2026-05-27 | 140 | 12 | under 1K |

**Pricing:** $0.50 per 1,000 ads, plus $0.50 per 1,000 for the optional per-country details add-on and $0.50 per 1,000 advertiser rows; $0.00005 per run start. Platform usage is included. Failed runs, error rows and ads skipped by the watcher are not charged.

### Real example in full

Row 5 of the table above (per-country list cut to 3 of 22, variations to 2 of 5):

```json
{
  "type": "ad",
  "advertiserId": "AR05288677867637440513",
  "advertiserName": "Superhuman Platform Inc.",
  "creativeId": "CR17624851031638671361",
  "format": "video",
  "targetDomain": "grammarly.com",
  "firstShown": "2026-03-25",
  "lastShown": "2026-10-14",
  "daysShown": 205,
  "adUrl": "https://adstransparency.google.com/advertiser/AR05288677867637440513/creative/CR17624851031638671361?region=DE",
  "adType": "display_template",
  "headline": "Think. Write. Fix. Done.",
  "description": "Watch how Humanizer makes AI text sound authentic and human.",
  "headlines": ["Think. Write. Fix. Done.", "Humanizer transforms AI text into writing that feels natural and authentic"],
  "displayUrl": "grammarly.com",
  "landingUrl": "https://www.grammarly.com/a",
  "callToAction": "Download",
  "imageUrl": "https://tpc.googlesyndication.com/simgad/7959286080260022750",
  "videoId": "QoI7QEvAljo",
  "videoUrl": "https://www.youtube.com/watch?v=QoI7QEvAljo",
  "videoDuration": "1:05",
  "channelName": "Grammarly",
  "paidBy": "Superhuman Platform Inc.",
  "regionCount": 22,
  "impressionsMin": 1000000,
  "impressionsMax": 1250000,
  "platformImpressions": [{"platform": "YouTube", "impressionsMin": 1000000, "impressionsMax": 1250000}],
  "regions": [
    {"region": "DE", "regionName": "Germany", "firstShown": "2026-03-25", "lastShown": "2026-10-14", "impressionsMin": 250000, "impressionsMax": 300000},
    {"region": "SE", "regionName": "Sweden", "firstShown": "2026-03-25", "lastShown": "2026-10-14", "impressionsMin": 225000, "impressionsMax": 250000},
    {"region": "IE", "regionName": "Ireland", "firstShown": "2026-03-25", "lastShown": "2026-10-14", "impressionsMin": 200000, "impressionsMax": 225000}
  ],
  "audienceSelection": {"demographicInfo": "included", "geographicLocations": "included and excluded", "contextualSignals": "included and excluded", "topicsOfInterest": "included and excluded"},
  "variationCount": 5,
  "variations": [
    {"headline": "Think. Write. Fix. Done.", "callToAction": "Download", "videoId": "QoI7QEvAljo"},
    {"headline": "Sound like you", "callToAction": "Download", "videoId": "QoI7QEvAljo"}
  ],
  "region": "DE",
  "platform": "YouTube",
  "input": "grammarly.com",
  "scrapedAt": "2026-10-14T19:51:07Z"
}
```

Other runs the same day:

- **Load test**: `temu.com` in the US, 500 ads with ad content, in 2 minutes 24 seconds (312 requests, 2 retries). The 500 ads were 182 video, 162 image and 156 text ads.
- **Who advertises a domain** (advertisers mode): the 300 most recent ads pointing to `temu.com` came from two advertisers, ELEMENTARY INNOVATION PTE. LTD. (280 ads: 169 text, 68 video, 43 image) and Whaleco Inc. (20 ads).
- **Watcher**: `Booking.com` in the UK, shown in the last 2 days, 40 ads. The first run returned 40 ads; the second run returned none, skipped the 40 it already knew and charged $0.

### What you can search

Put one item per line in **searches**. You can mix kinds in one run.

| You enter | Example | You get |
|---|---|---|
| Website domain | `nike.com` | Every ad that points to the domain, from any advertiser (brands, agencies, resellers) |
| Advertiser name | `Nike, Inc.` or `Nike` | Ads of the advertiser with that name and the most ads (or all same-name advertisers, see *Use every advertiser with the same name*) |
| Advertiser ID | `AR16735076323512287233` | All ads of that advertiser |
| Transparency Center URL | `https://adstransparency.google.com/advertiser/AR…/creative/CR…` | That ad (or that advertiser, or that domain search) |

Filters: **region** (any country, or anywhere), **platform** (Search, YouTube, Shopping, Maps, Play), **ad format** (text, image, video), and a **date window** (shown in the last N days, or between two dates). Results come most recently shown first. **Max ads per input** caps how many ads are read per line.

### Modes

- **Ads** (default): one row per ad, as in the example.
- **Advertisers**: find advertiser IDs. For a name you get every advertiser Google suggests, with registered country, ad count range and whether the name matches exactly. For a domain you get every advertiser running ads that point to it, with how many of the scanned ads are theirs and their formats.

### Watching for new ads

Turn on **Only ads not seen before**, give the task a **watcher name**, set **Shown in the last N days** to 2 or 3 and schedule the actor daily. Every run returns only ads that earlier runs under the same watcher name did not return, and skipped ads cost nothing. The memory is kept in the named key-value store `google-ads-transparency-state`. Use `firstShown` to tell an ad that really just launched from one that was running before the watcher first saw it.

```json
{"searches": ["gymshark.com"], "region": "GB", "lastDays": 3, "maxAdsPerSearch": 100,
 "onlyNew": true, "watcherName": "gymshark-uk-new-ads"}
```

Set **Max ads per input** above the number of ads the advertiser shows in that window; the run log prints it, as in `gymshark.com: about 64-64 ads match`. The watcher reads only the newest ads up to that cap, so for a very large advertiser it returns ads it simply had not read yet. On 2026-10-16, booking.com showed 100,000 to 200,000 ads in the UK over two days. A second run capped at 100 ads returned 100 ads the first run had not read, and the newest of them was first shown in August 2026. Gymshark showed 64 ads over the same two days, so a 200-ad cap covers all of them and the watcher returns only new ones.

A real catch: a daily watcher on `gymshark.com` in the UK (`lastDays: 2`, `maxAdsPerSearch: 200`, watcher name `readme-gymshark`) started on 2026-10-16 with 64 ads. Its run at 07:21 UTC on 2026-10-19 returned 2 ads, skipped the 69 it already knew, and charged for the 2. One was a YouTube ad launched the day before:

```json
{
  "type": "ad",
  "advertiserName": "Gymshark Ltd",
  "creativeId": "CR01419556965582897153",
  "format": "video",
  "targetDomain": "gymshark.com",
  "firstShown": "2026-10-18",
  "lastShown": "2026-10-18",
  "daysShown": 1,
  "videoUrl": "https://www.youtube.com/watch?v=2O4ncz2TQh0",
  "region": "GB",
  "adUrl": "https://adstransparency.google.com/advertiser/AR05501100765344694273/creative/CR01419556965582897153?region=GB"
}
```

The other was an image ad first shown on 2026-05-12 that had just come back into the 2-day window. That is why `firstShown` matters: filter on it to keep only launches.

### Pricing

Pay per event. Platform usage (compute, proxy, storage) is included.

| Event | Price | When |
|---|---|---|
| Ad | $0.50 per 1,000 | One ad row, including headline, copy, images and video where Google has them |
| Ad details add-on | $0.50 per 1,000 | Only with *Per-country stats* on: countries, EU impressions, targeting, variations |
| Advertiser | $0.50 per 1,000 | One advertiser row in advertisers mode |
| Actor start | $0.00005 | Once per run |

Examples: all 500 recent temu.com ads cost $0.25. 1,000 ads with per-country stats cost $1.00. A daily watcher on 10 competitors that finds 30 new ads a day costs about $0.45 a month. Error rows, "no results" rows and ads skipped by the watcher are never charged. The six most used Google Ads Transparency actors charge $1.00 to $2.00 per 1,000 ads on the free plan, and one charges $3.00 per 1,000 for details on top.

### Accuracy and limits

- **Most text ads are stored as pictures.** For most Search text ads Google only keeps a screenshot of the ad: in our tests about two thirds of temu.com's text ads and all of booking.com's and nike.com's (US) text ads; in the default `nike.com` run on October 21, 2026, 13 of 20 ads. Those rows have `adType: "archived_image"` and an `imageUrl` of the screenshot, but no headline text. Turning on *Per-country stats* sometimes recovers the text from the ad's other variations.
- **Impressions and targeting are EU only.** Google publishes impressions ranges and audience selection under the EU Digital Services Act, so they exist only for ads shown in EU countries. Elsewhere `impressionsMin`/`impressionsMax` are null. Impressions are ranges (e.g. 1M–1.25M), as on the website, and Google updates them with a delay.
- **Some previews do not render.** When Google cannot render an ad's preview either, the row says `adType: "preview_unavailable"` and keeps the dates, IDs and links. Video ads sometimes come with only a YouTube thumbnail in the list (`video_thumbnail`); turn on *Per-country stats* to read the full ad from its variations.
- **No spend data.** Google publishes spend only for political ads, which this actor does not cover.
- **Names are matched loosely by Google.** The advertiser-name search ignores legal suffixes (Inc, Ltd, BV...). If the wrong advertiser comes back, run advertisers mode first and use the ID.
- **Landing URLs** come from the ad preview. Search text ads show the display URL but not the landing URL.
- **Date window** means the ad was shown at least once in the window; it can have started earlier.
- Big advertisers have a lot of ads (Google lists 9,000–10,000 for Nike, Inc. and 200,000–300,000 ads point to temu.com in the US), so set *Max ads per input*.

### FAQ

**Is this data public?** Yes. Google publishes it in the Ads Transparency Center for anyone to view, without signing in. The actor reads the same data the website shows.

**Can I use it from my code or an AI agent?** Yes. Call it through the Apify API or the Apify MCP server. A run for one domain and 20 ads takes about 7 seconds.

**How do I find an advertiser's ID?** Run advertisers mode with the brand name or domain, or copy it from the advertiser's page URL on adstransparency.google.com (it starts with `AR`).

**Why does a domain search show agencies?** Agencies and resellers that run ads pointing to a brand's site appear under their own advertiser names, which is often exactly what competitor research needs.

**Does it need a proxy?** No. Requests go directly from the Apify server. If Google rate limits them, the actor retries and moves to the Apify datacenter proxy automatically.

Watcher scheduling: use a separate `watcherName` for each independent workflow and keep its filters/location consistent. Runs using the same name wait up to 50 seconds for the previous run; if it is still busy, retry after it finishes. Local and cloud baselines are separate. A failed or interrupted delivery can appear again on retry, so deduplicate downstream by creative ID and observation time. Existing names keep their baselines, but a baseline previously shared by colliding names cannot be disentangled automatically.

### Changelog

- **0.1.11 (2026-10-21)**: README restructured: default-input timing, pricing line and a ready-to-schedule watcher input at the top. No code changes.
- **0.1.10 (2026-10-19)**: Watcher runs with the same name now wait for each other before loading and saving their baseline. Losing the lock stops the run without saving stale state. Cancelled runs keep only confirmed output in watcher state.
- **0.1.9 (2026-10-19)**: Spending-limit fix: a row is charged and remembered only when it is delivered; add-ons are charged only with their row; watcher names with special characters no longer collide.
- **0.1.8 (2026-10-19)**: README shows a real watcher run that caught a newly launched ad. No code changes.
- **0.1 (2026-10-14)**: First release. Ads and advertisers modes, region, platform, format and date filters, ad content (headline, copy, images, video, app info, sitelinks), per-country details with EU impressions and audience selection, new-ads watcher.

# Actor input Schema

## `searches` (type: `array`):

What to look up. Each line is one of: an advertiser name as Google shows it (e.g. 'Nike, Inc.' or just 'Nike'), a website domain (e.g. 'nike.com' — returns every ad that points to it, from any advertiser), an advertiser ID (AR followed by digits), or an Ads Transparency Center URL of an advertiser, a domain search or a single ad.

## `mode` (type: `string`):

'ads' returns one row per ad. 'advertisers' returns advertiser rows instead: for a name, every advertiser Google suggests with its ad count; for a domain, every advertiser running ads that point to it, with how many ads each.

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

Only ads shown in this country. 'Anywhere' covers all countries. Impressions and audience data exist only for ads shown in the EU (Google publishes them under the Digital Services Act).

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

Only ads shown on this Google product.

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

Only ads of this format.

## `lastDays` (type: `integer`):

Only ads shown at least once in the last N days (1 = today). 0 turns this off. Overrides the start and end dates.

## `startDate` (type: `string`):

Only ads shown at least once between this date and the end date, as YYYY-MM-DD. Leave empty for no date filter.

## `endDate` (type: `string`):

End of the date window, as YYYY-MM-DD. Defaults to today when only a start date is set.

## `maxAdsPerSearch` (type: `integer`):

How many ads to read for each line in 'searches' (most recently shown first). Big brands run tens of thousands of ads, so set this to what you need. In advertisers mode with a domain, this is how many ads are scanned to find advertisers.

## `includeAdContent` (type: `boolean`):

Read each ad's preview to get the headline, description, display URL, call to action, image URLs and YouTube video ID. One extra request per ad, no extra charge.

## `includeDetails` (type: `boolean`):

Add the countries each ad ran in with first and last shown dates per country, impressions ranges per country and per platform (ads shown in the EU), audience selection (demographics, locations, contextual signals...) and every variation of the ad's copy. Charged as an extra 'ad-details' event per ad.

## `allAdvertiserMatches` (type: `boolean`):

When you enter a name, Google often has several advertisers with it (e.g. one per country). Off: only the one with the most ads. On: all of them.

## `onlyNew` (type: `boolean`):

Remember every ad returned under the watcher name and output only ads that earlier runs did not return. Schedule the actor daily with 'Shown in the last N days' set to 2 or 3 to get new competitor ads as they launch. Skipped ads are not charged.

## `watcherName` (type: `string`):

Name of the watcher memory (kept in the named key-value store 'google-ads-transparency-state'). Use a different name for each scheduled task so they do not share memory.

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

Requests in flight at once. Higher is faster but more likely to be rate limited.

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

Not needed normally. Requests go straight from the Apify server and switch to an Apify datacenter proxy automatically if Google rate limits them.

## `autoProxyFallback` (type: `boolean`):

If Google keeps answering 'too many requests', retry through the Apify datacenter proxy.

## Actor input object example

```json
{
  "searches": [
    "nike.com"
  ],
  "mode": "ads",
  "region": "anywhere",
  "platform": "any",
  "adFormat": "any",
  "lastDays": 0,
  "maxAdsPerSearch": 20,
  "includeAdContent": true,
  "includeDetails": false,
  "allAdvertiserMatches": false,
  "onlyNew": false,
  "watcherName": "default",
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "autoProxyFallback": true
}
```

# Actor output Schema

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

No description

## `adsCsv` (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 = {
    "searches": [
        "nike.com"
    ],
    "maxAdsPerSearch": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("varnmill/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 = {
    "searches": ["nike.com"],
    "maxAdsPerSearch": 20,
}

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

```

## MCP server setup

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