# Google Ads Monitor — Competitor Ads Tracker (`violet_lab/google-ads-monitor`) Actor

Google Ads Transparency scraper and competitor ad monitor. Get ad text, images, videos, impressions by country and platform, targeting and dates. Monitoring mode returns only new, stopped and resumed ads and new countries. Pay only per result.

- **URL**: https://apify.com/violet_lab/google-ads-monitor.md
- **Developed by:** [Violet Lab](https://apify.com/violet_lab) (community)
- **Categories:** Marketing, SEO tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 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.

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

<p align="center"><img src="https://api.apify.com/v2/key-value-stores/RHce21MZcES5eSUcD/records/google-ads-monitor-banner" alt="Google Ads Monitor by Violet Lab" width="100%"></p>

## Google Ads Monitor — Competitor Ads Tracker

Track every ad your competitors run on **Google Search, YouTube, Shopping, Maps and Play**, straight from Google's official public ad library (Google Ads Transparency Center). No login, no cookies, no browser.

Schedule it and get **only what changed since the last run**: new ads, stopped ads, resumed ads and ads expanding to new countries. It works as an alert feed for Slack, email, Google Sheets, Make, Zapier or n8n.

### What you get

- 🔎 **Every ad for a domain** (`nike.com`), brand name or advertiser ID, across all advertiser accounts
- 🔔 **Monitoring mode**: change events `NEW`, `STOPPED`, `RESUMED`, `NEW_REGION`
- 📊 **Summary per competitor**: active ads, launched in the last 7 days, formats, top countries, number of changes
- 📝 **Ad copy for text ads**: headline, description, display URL. Google publishes most text ads only as a screenshot, so we read the text from it (`textSource: "ocr"`). Where Google provides the text itself, we use it as is (`textSource: "google"`).
- 🌍 **Impressions by country and platform**: Google's published ranges, e.g. Germany 7k–8k, of which Search 5k–6k
- 🎯 **Targeting**: whether demographics, geography, contextual signals or customer lists were used
- 🎬 Video ad YouTube links, image URLs, all ad variations, first/last shown dates, days served
- 🌐 Filters: country, platform, date range

### Input examples

**Daily competitor alerts** (create an Apify Schedule, e.g. every morning):

```json
{
  "domains": ["competitor1.com", "competitor2.com"],
  "region": "US",
  "onlyNewAds": true,
  "includeSummary": true
}
```

**Full export with ad copy, impressions and targeting:**

```json
{
  "domains": ["booking.com"],
  "region": "DE",
  "includeAdDetails": true,
  "maxAdsPerTarget": 500
}
```

**By brand name or advertiser ID:**

```json
{ "brandNames": ["Nike"], "advertiserIds": ["AR16735076323512287233"] }
```

### Output example (ad with details)

```json
{
  "recordType": "ad",
  "advertiserName": "Booking.com",
  "legalName": "Booking.com B.V.",
  "creativeId": "CR01370996166958776321",
  "format": "TEXT",
  "headline": "2 Hotels in Brüel - Unsere Top 10 Hotels Entdecken",
  "description": "Hotels in Brüel, Deutschland. Schnell und sicher online buchen",
  "displayUrl": "www.booking.com/",
  "textSource": "ocr",
  "firstShown": "2023-03-01T00:00:00+00:00",
  "lastShown": "2026-11-02T00:00:00+00:00",
  "numServedDays": 1343,
  "totalImpressions": { "lowerBound": 30000, "upperBound": 35000 },
  "regionStats": [
    {
      "regionCode": "DE", "regionName": "Germany",
      "impressions": { "lowerBound": 7000, "upperBound": 8000 },
      "platformStats": [
        { "platform": "SEARCH", "impressions": { "lowerBound": 5000, "upperBound": 6000 } },
        { "platform": "YOUTUBE", "impressions": { "lowerBound": 0, "upperBound": 1000 } }
      ]
    }
  ],
  "targeting": { "demographics": {"1": true, "2": false}, "geography": {"1": true, "2": false},
                 "contextual": {"1": true, "2": true}, "customerLists": {"1": true, "2": false} },
  "previewImageUrl": "https://tpc.googlesyndication.com/archive/simgad/...",
  "adUrl": "https://adstransparency.google.com/advertiser/AR.../creative/CR..."
}
```

**Monitoring event:** the same record plus `"changeType": "NEW" | "STOPPED" | "RESUMED" | "NEW_REGION"` (and `newRegions` for `NEW_REGION`).

**Summary record** (`includeSummary: true`, free):

```json
{
  "recordType": "summary", "query": "booking.com",
  "adsChecked": 15, "activeAds": 15, "launchedLast7Days": 0,
  "estimatedTotalAdsInLibrary": { "min": 14300000, "max": 14400000 },
  "byFormat": { "TEXT": 15 },
  "topRegions": [{ "regionCode": "FR", "ads": 9 }, { "regionCode": "DE", "ads": 7 }],
  "changes": { "NEW": 2, "STOPPED": 1, "RESUMED": 0, "NEW_REGION": 0 }
}
```

Filter the dataset by `recordType` to separate ads from summaries.

### How monitoring works

1. Turn on **Monitoring mode** and add your competitors.
2. The first run records the ads that are live right now as a baseline. It outputs nothing and charges only the competitor check.
3. Create a Schedule. Each later run outputs only changes. An ad counts as `STOPPED` when it has not been shown for longer than *active within days* (default 2).
4. `NEW_REGION` needs **Include ad details** to be on.

State is kept per domain/advertiser + region + platform. Changing the filters starts a new baseline.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Competitor check (each domain / advertiser per run) | $0.01 |
| Ad returned | $0.0012 ($1.20 / 1,000) |
| Ad details (impressions, targeting, ad copy, video), optional | $0.002 ($2.00 / 1,000) |
| Summary records | free |

Examples:

- Monitoring 5 competitors daily ≈ $1.50 per month plus new ads only.
- Exporting 1,000 ads with full details = $3.20.

### Notes and limits

- Data comes from Google's public transparency library. Google decides which ads and fields are published. Impressions are published only for some ads (mostly in the EU), as ranges.
- Ad copy from screenshots is read with OCR. It is very accurate for Latin-script languages; diacritics may occasionally be lost. CJK scripts are not supported yet.
- A domain search returns every ad that points to that domain, including ads from other advertiser accounts (resellers, agencies, sites hosted on that domain). Use `advertiserIds` or `brandNames` to track one company only.
- Age-restricted ads are not visible without a Google login and are not included.
- Proxy: starts on Apify datacenter proxy and switches to residential automatically if Google blocks it. This costs you nothing extra.
- Not affiliated with or endorsed by Google. Google Ads is a trademark of Google LLC; the logo is used only to identify the data source.
- Use the data responsibly and in line with applicable laws.

***

<p align="center"><b>Built by Violet Lab</b> · small, reliable data tools with pay-per-result pricing.<br>
Found a bug or need a field? Open an <b>Issue</b> on this Actor. We usually reply within 24 hours.</p>

# Actor input Schema

## `domains` (type: `array`):

Domains the ads point to, e.g. nike.com. Finds ads from every advertiser account that promotes the domain.

## `brandNames` (type: `array`):

Advertiser name as shown in Google Ads Transparency Center. Resolved to the best-matching verified advertiser.

## `advertiserIds` (type: `array`):

IDs like AR16735076323512287233 (from the advertiser URL in Ads Transparency Center).

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

ISO country code where ads were shown, e.g. US, DE, GB. Empty = all regions.

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

Optional: only ads shown on this Google platform.

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

Optional start date.

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

Optional end date.

## `onlyNewAds` (type: `boolean`):

Remember ads between runs and output only changes: NEW ads, STOPPED ads (not shown for more than 'active within days'), RESUMED ads and — with ad details on — NEW_REGION (ad started showing in a new country). Combine with an Apify Schedule (e.g. daily).

## `activeWithinDays` (type: `integer`):

An ad not shown for longer than this is reported as STOPPED.

## `skipExistingOnFirstRun` (type: `boolean`):

In monitoring mode, the first run stores currently live ads without outputting (or charging for) them.

## `includeAdvertiserDetails` (type: `boolean`):

Adds the advertiser's verified legal name and country.

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

For each ad: impressions by country and platform (ranges published by Google), targeting categories, days served, all variations, ad text when available, video link. One extra request per ad; charged as 'Ad details'.

## `includePreviewText` (type: `boolean`):

When Google does not expose the ad copy directly, read visible texts from the rendered preview (adTexts). Slower.

## `ocrTextAds` (type: `boolean`):

For text ads Google often publishes only a screenshot. Read the headline, display URL and description from it (textSource = 'ocr'). Included in the 'Ad details' price.

## `includeSummary` (type: `boolean`):

Adds one free 'summary' record per domain/advertiser: active ads, launched in last 7 days, formats, top advertisers, top countries and (in monitoring) counts of changes.

## `maxAdsPerTarget` (type: `integer`):

Stop after this many ads for each domain / advertiser.

## `maxAdsTotal` (type: `integer`):

Stop after this many ads in total across all targets (cost cap).

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

Google rate-limits single IPs after a few dozen requests. Apify Proxy rotates sessions automatically when blocked.

## `requestIntervalSecs` (type: `number`):

Pause between requests to Google. Higher = slower but less likely to be rate-limited.

## `diagnostic` (type: `boolean`):

Internal: probe proxies and store raw Google responses. Not for normal use.

## `residentialFallback` (type: `boolean`):

Starts on cheap datacenter proxy; if Google blocks it, switches to Apify RESIDENTIAL proxy automatically (no extra charge to you).

## `detailConcurrency` (type: `integer`):

How many ads are enriched with details at the same time.

## `downloadImages` (type: `boolean`):

Copy ad images (up to 5 per ad) to this run's key-value store and return permanent links in storedImageUrls. Google preview links can expire.

## Actor input object example

```json
{
  "domains": [
    "nike.com"
  ],
  "onlyNewAds": false,
  "activeWithinDays": 2,
  "skipExistingOnFirstRun": true,
  "includeAdvertiserDetails": false,
  "includeAdDetails": false,
  "includePreviewText": true,
  "ocrTextAds": true,
  "includeSummary": false,
  "maxAdsPerTarget": 20,
  "maxAdsTotal": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "requestIntervalSecs": 1,
  "diagnostic": false,
  "residentialFallback": true,
  "detailConcurrency": 5,
  "downloadImages": false
}
```

# Actor output Schema

## `overview` (type: `string`):

Dataset items. recordType='ad' rows are ads (in monitoring mode only new/stopped/resumed/new-region changes); recordType='summary' rows are per-competitor summaries.

## `images` (type: `string`):

Ad images copied to this run's key-value store when 'Save ad images' is enabled (keys img-<creativeId>-<n>).

# 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 = {
    "domains": [
        "nike.com"
    ],
    "maxAdsPerTarget": 20,
    "maxAdsTotal": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("violet_lab/google-ads-monitor").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 = {
    "domains": ["nike.com"],
    "maxAdsPerTarget": 20,
    "maxAdsTotal": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("violet_lab/google-ads-monitor").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 '{
  "domains": [
    "nike.com"
  ],
  "maxAdsPerTarget": 20,
  "maxAdsTotal": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call violet_lab/google-ads-monitor --silent --output-dataset

```

## MCP server setup

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

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/C1ZwDC5sqaxMGdBCT/builds/EDQYcr6pdVFDYtkme/openapi.json
