# Google Ads Transparency Scraper - New Competitor Ads Monitor (`neverempty/google-ads-transparency-scraper`) Actor

For marketers and agencies watching competitors: only the Google Ads Transparency Center ads that are new since your last run, by domain or advertiser ID, with image URL, first/last shown, countries and who paid. On nike.com a rerun returned 0 of 57 older ads as new (2026-09-24).

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

## Pricing

from $2.10 / 1,000 ad returneds

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 - New Ads Monitor

Watch your competitors in the **Google Ads Transparency Center** (adstransparency.google.com) and get **only the ads that are new since your last run**. Give it a website domain (`nike.com`) or an advertiser ID (`AR18378488041124659201`); schedule it daily or hourly; each run returns the ads Google started showing after your watch began and that you have not received yet.

Every ad row has the advertiser name and ID, creative ID, format (text, image, video), first and last shown time, days shown, a direct image URL, Google's preview URL and a link to the ad in the Ads Transparency Center. With **ad details** on (the default) it also adds every country the ad was shown in with first and last shown dates, who paid for the ad, its topic and the image of every variation.

Unofficial. It reads only what the public Ads Transparency Center shows, without logging in.

### What it is for

- **New competitor ads, every morning**: schedule it once a day for a list of competitor domains; each run returns only the ads that appeared since the last run.
- **Ad library export**: the first run for a target returns its newest ads (20 by default, up to 1,000) and remembers the rest.
- **One country or one format**: `region` and `format` are filters that Google applies itself. Measured on 2026-09-24: Nike Retail BV has about 80,000–90,000 ads anywhere and **19 in Japan** — the region filter returns those 19.

### How the "new ads" watch works

1. **First run for a target**: reads Google's list (up to `maxAdsScanned`, 40 per request), returns the `maxAdsFirstRun` ads with the most recent first-shown time, and remembers every ad it read.
2. **Every later run**: reads only ads Google lists as shown since shortly before the previous check, and returns ads that are **not remembered** and whose **first shown time is after the watch started**. When the first run read the whole list for the target, ads first shown up to 2 days before the watch started also count, because Google can list an ad a little after it first runs.
3. An ad that was already running when the watch started but was not read in the first run (Google does not list ads newest first, and the order changes between requests) is **remembered, not reported as new**. Measured on 2026-09-24: a second run 20 seconds after the first found 57 such ads for nike.com and returned 0 of them as new.
4. When Google has more ads than `maxAdsScanned` for a target, a free row says so, with Google's own estimate of the total. For very large advertisers, narrow the watch with `region` or `format`.

Watches are kept per target, region, format and `watchName`. Use a different `watchName` for two schedules that watch the same target. Do not let two runs of the same watch overlap in time: both would read the same new ads before either remembers them, and both would return (and charge) them; a free row says when a run could not save what it read.

When one run has a domain and that domain's advertiser ID as two targets, an ad found for both is returned once, for the first target.

### Input

| Field | What it does |
|---|---|
| `targets` | Domains, advertiser IDs or Ads Transparency Center links, one per line (up to 50). Empty = `nike.com`. |
| `region` | Two-letter country code (`US`, `GB`, `DE`, `JP` …) or `anywhere`. Empty = anywhere. |
| `format` | `all`, `text`, `image` or `video`. Empty = all. |
| `maxAdsFirstRun` | Ads to return the first time a target is checked (0–1,000). Empty = 20. |
| `maxAdsScanned` | Ads of Google's list to read per target per run (40–1,000). Empty = 400. |
| `includeAdDetails` | Countries with dates, who paid, topic and variation images for each returned ad. Empty = on. |
| `watchName` | Separate watches for the same target. Optional. |
| `resetMonitoringState` | Forget what the watch remembered and start again with a first check. |

### Output

One row per ad (`status: "ok"`, `changeType: "new"`):

| Column | Meaning |
|---|---|
| `advertiserName`, `advertiserId` | The advertiser as named in the Ads Transparency Center |
| `creativeId`, `adUrl` | The ad's ID and its page in the Ads Transparency Center |
| `format` | `TEXT`, `IMAGE` or `VIDEO` |
| `firstShownAt`, `lastShownAt` | First and last time Google shows for the ad (ISO time, UTC) |
| `daysShown` | Number of days shown, as Google reports it |
| `daysSinceFirstShown` | Added: whole days from first shown to the check |
| `imageUrl`, `imageWidth`, `imageHeight` | Direct image of the ad when Google archives one. Many text ads are archived by Google as an image of the ad (headline, text and URL as it appeared). |
| `previewUrl` | Google's own preview script for ads that are rendered, not archived as an image (many video and responsive ads). It is not an MP4 file. |
| `domain` | The domain Google shows for the ad (domain searches) |
| `isFirstCheck` | `true` on the first run for this watch |
| `sponsorName` | Who paid for the ad, as the ad's page in the Ads Transparency Center names it (with ad details on) |
| `adTopic` | Google's topic for the ad when it gives one |
| `regionsShown` | Every country the ad was shown in: `countryCode`, `firstShownDate`, `lastShownDate`, and impression bounds when Google gives them (mainly EU countries) |
| `variationsCount`, `variationImageUrls` | Number of variations and the image of each archived variation |
| `euImpressionsLowerBound`, `euImpressionsUpperBound` | Google's EU impression range when it gives one |
| `detailsRead` | `false` when the ad's detail page could not be read (its detail columns are empty) |

Rows that are not ads are **free** and say why: `no-new-ads`, `watch-started`, `no-ads` (Google lists nothing for this target — it gives the same answer for a domain it does not know), `partial-scan`, `blocked`, `unreadable`, `bad-input`, `budget-reached`.

### Pricing

- **Run start**: charged once per run in which Google's ad list was read for at least one target. Not charged when Google refused every request.
- **Ad returned**: charged per ad row. Free rows are never charged.
- A run whose maximum total charge has no room for the start fee plus one ad requests nothing and is charged nothing.
- Ad details are included in the ad price (one extra request per returned ad).

### Measured on 2026-09-24 (build 0.1.3)

- `nike.com`, first run: 400 ads read on 10 pages, 20 returned with details, in 22 to 65 seconds.
- Same watch 20 seconds later: 0 new ads, 57 older unseen ads remembered and not reported.
- `AR18378488041124659201` with region `JP`: 19 ads on 1 page, all 19 returned.
- Requests from Apify's servers: 34 of 34 answered through datacenter IPs and 33 of 34 through residential IPs (one connection failure); no check page in 102 requests.

### Limits

- Google does not list ads newest first. A watch sees every new ad only when the whole list for the target fits in `maxAdsScanned`; otherwise a free `partial-scan` row says so.
- `maxAdsScanned` is capped at 1,000 per target per run (25 requests).
- A domain Google does not know and a domain with no ads get the same empty answer from Google, so both come back as `no-ads`.
- If Google answers with its "unusual traffic" check page, this Actor does **not** solve it or keep asking from other IPs: the target comes back as a free `blocked` row and nothing is remembered, so the next run checks it again.
- Ad text is not transcribed: an ad comes as Google's archived image of it (`imageUrl`) or as Google's preview script (`previewUrl`).
- Political ads are a separate Google library and are not covered.

### Support

Something missing or wrong? Open an issue on the Issues tab with the run ID and the target.

# Actor input Schema

## `targets` (type: `array`):

Advertisers to watch, one per line (up to 50): a website domain (nike.com), an advertiser ID from the Ads Transparency Center (AR18378488041124659201), or an Ads Transparency Center link (https://adstransparency.google.com/advertiser/AR…). The first run for a target returns its newest ads; every later run returns only ads first shown after the watch started that it has not returned before. Empty = nike.com.

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

Only ads shown in this country: a two-letter code such as US, GB, DE or JP. Empty or "anywhere" = every region. Google applies this filter itself (for example Nike Retail BV: about 80,000 ads anywhere, 19 in Japan). A different region is a separate watch.

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

Only ads of this format, filtered by Google. Empty = all formats. A different format is a separate watch.

## `maxAdsFirstRun` (type: `integer`):

The first time a target is checked, return this many of its ads, newest first shown first, and only remember the rest. 0 = return none and start watching from now. Empty = 20. Later runs return every new ad found.

## `maxAdsScanned` (type: `integer`):

How many ads of Google's list to read for each target in each run (40 per request). Google does not list ads newest first, so a watch finds every new ad only when the whole list fits; a free row says when Google has more. Later runs read only ads shown since shortly before the previous check. Empty = 400.

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

On (or empty) = for each returned ad, also read its detail page: every country it was shown in with first and last shown dates, the name of who paid for it, its topic, and the image of every variation. One extra request per returned ad; not charged separately. Off = list data only.

## `watchName` (type: `string`):

Optional. Runs with the same watch name share what has already been returned. Give different names to two schedules that watch the same target for different purposes, so each gets every new ad. Letters, digits, dot, dash and underscore.

## `resetMonitoringState` (type: `boolean`):

Forget what this watch remembered for these targets at the start of this run, so this run is a first check again. Turn it off again for scheduled runs, or every run starts over and returns the same ads again.

## Actor input object example

```json
{
  "targets": [
    "nike.com"
  ],
  "region": "anywhere",
  "includeAdDetails": true
}
```

# Actor output Schema

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

One row per ad: advertiser name and ID, creative ID, format, first and last shown time, days shown, image URL, preview URL, Ads Transparency Center link, and (with ad details on) every country it was shown in with dates, who paid for it, its topic and the image of every variation. A run with no new ad, a target with no ads, a refused request or a run that hit its maximum charge comes back as a free row that says why.

# 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 = {
    "targets": [
        "nike.com"
    ],
    "region": "anywhere"
};

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

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

```

## MCP server setup

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