# Competitor Ad Monitor — Google Ads Change Tracker & Alerts (`mochiboo/competitor-ad-monitor-google`) Actor

Monitor a competitor's Google ads and get only what's NEW or CHANGED since the last run — new creatives, refreshed/scaled creatives, removed ads — as a clean change-feed. Google Ads change detection, competitor ad tracking & monitoring alerts. Schedule daily; company-level only.

- **URL**: https://apify.com/mochiboo/competitor-ad-monitor-google.md
- **Developed by:** [mochi](https://apify.com/mochiboo) (community)
- **Categories:** Automation, E-commerce, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 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/platform/actors/running/actors-in-store#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

## Competitor Ad Monitor — Google Ads Change Tracker & Alerts

**Stop re-scraping. Get only what changed.** Point this at a competitor and every run reports
just the **new**, **changed**, and **removed** Google ads since the last run — a clean
**change-feed**, not another full dump you have to diff by hand. Built for **competitor ad
monitoring, Google Ads change detection, ad tracking, and ad-change alerts** across
**Google Search, Shopping, Display & YouTube**.

> **Schedule it and forget it.** Run it daily (or hourly) with Apify's native scheduler; each run
> compares the competitor's *current* ads against the snapshot it saved last time and pushes only
> the differences. The **first run** gives you a full baseline of their live ads, so you get value
> immediately.

### Monitor a competitor daily — 3 steps

1. **Set the competitor.** `Watch by` = `domain`, `queries` = `["yourcompetitor.com"]` (or use an
   advertiser ID / keyword — see below). Optionally set a `region`.
2. **Run it once.** The first run saves a baseline and returns every ad the competitor is currently
   running, each flagged `changeType: first_run_baseline`.
3. **Schedule it.** In the actor's **Schedules** tab, add a daily (or hourly) schedule with the same
   input. From then on, each run's dataset contains only what's **new / changed / removed** since the
   previous run.

### What each row tells you: `changeType`

| `changeType` | Meaning |
|---|---|
| `first_run_baseline` | First run for this competitor — a full snapshot of their current live ads. |
| `new` | A creative that appeared since the last run (a fresh ad launched). |
| `changed` | A creative still live but a watched field moved — see `changedFields` (e.g. it was refreshed, or `variantCount` scaled up/down). |
| `removed` | A creative that was in the last snapshot but is gone now (paused / expired). |
| `unchanged` | *(only if `includeUnchanged` is on)* — a still-live, unchanged ad, for a full daily snapshot. |

`changed` rows include a **`changedFields`** array — `[{ field, from, to }]` — so you (or an LLM /
alert step) can see exactly what moved without comparing rows yourself.

### What you get per ad

| Field | Description |
|---|---|
| `changeType` | `new` / `changed` / `removed` / `first_run_baseline` / `unchanged` |
| `changedFields` | On `changed` rows: which fields moved, with `from` / `to` |
| `advertiserName` | The advertiser (company / organisation) |
| `advertiserDomain` | The advertiser's verified domain (domain-mode watches; `null` otherwise) |
| `advertiserId` / `creativeId` | Stable Google IDs (`AR...` / `CR...`) |
| `adFormat` | `text`, `image`, or `video` |
| `variantCount` | Relative breadth signal — how many served variants/placements Google reports for the creative. A breadth proxy to rank which ads they're running hardest — **not** impressions or spend (the public Center exposes neither). |
| `firstShown` / `lastShown` | Active-window start / end (ISO 8601 UTC); `*Ts` twins are unix-seconds keys |
| `previewUrl` / `imageUrl` | Direct creative URL / direct image URL (image ads) |
| `adLibraryUrl` | Deep link back to the ad on Google's Transparency Center |
| `watchTarget` / `watchMode` / `watchRegion` | Which watch produced the row |
| `monitoredAt` | When this run checked |

All data is **company / advertiser-level only** — no personal data (PDPA / GDPR-friendly by design).

Each run also writes a **`RUN_SUMMARY`** to the key-value store (`eventsPushed`, `changeTotals` per
type, `errors`) so you can wire an alert ("email me when `changeTotals.new > 0`") off a single value.

### Three ways to identify a competitor (`Watch by`)

1. **`domain`** *(default)* — a brand / domain, e.g. `["nike.com"]`. Simplest for most competitors.
2. **`advertiser`** — exact advertiser IDs (`AR...`) for the tightest, most exhaustive watch,
   e.g. `["AR16735076323512287233"]`.
3. **`keyword`** — a keyword; the actor resolves the top advertisers Google associates with it and
   monitors each one, e.g. `["running shoes"]`. Tune with `maxAdvertisersPerKeyword`.

Each competitor (advertiser / domain) is snapshotted **separately**, and `region` is part of a
watch's identity — so watching the same competitor in `US` and `UK` gives you two independent feeds.
**Tip:** one schedule per competitor keeps each change-feed clean and easy to alert on.

### Example — watch a competitor's US ads daily

Input:

```json
{
  "searchMode": "domain",
  "queries": ["nike.com"],
  "region": "US",
  "maxAdsPerWatch": 20
}
```

Run 1 returns Nike's current US ads flagged `first_run_baseline`. Scheduled daily, later runs return
only new creatives, changed creatives (with `changedFields`), and any that dropped off — e.g.:

```json
{
  "changeType": "changed",
  "advertiserName": "Nike, Inc.",
  "creativeId": "CR03002423070389436417",
  "adFormat": "image",
  "variantCount": 512,
  "changedFields": [
    { "field": "variantCount", "from": 466, "to": 512 },
    { "field": "lastShownTs", "from": 1779663267, "to": 1779749667 }
  ],
  "watchTarget": "nike.com",
  "watchRegion": "US",
  "monitoredAt": "2026-07-27T06:00:00.000Z"
}
```

### Why this actor

- **Change-feed, not a re-scrape** — only new / changed / removed ads each run; no manual diffing.
- **Immediate value on run 1** — full baseline of the competitor's live ads.
- **Schedule-native** — designed for Apify's scheduler; snapshots persist across runs automatically.
- **Alert-ready** — per-type `changeTotals` in `RUN_SUMMARY`; `changedFields` on every changed row.
- **Reliable at scale** — automatic retries with backoff and residential proxy IP rotation.
- **Company-level only** — safe to use, no personal data.

### Pricing

**Pay per result** — $0.001 per change event returned, plus a $0.00005 actor-start event. No subscription.

You pay for *changes*, not for re-reading the same ad list every day. A competitor that launched 5 new
creatives costs about half a cent; on a quiet day with nothing new the run returns zero rows and costs
essentially only the start event. Watching one competitor daily for a month typically lands in **cents**,
not dollars.

### Notes & limits

- Only ads currently surfaced by Google's public Ads Transparency Center are considered (Google
  shows ads active within roughly the last year for most regions).
- A `removed` event means the ad is no longer surfaced by Google — usually paused/expired.
- To avoid false "removed" signals, if a run's fetch errors for a competitor, that watch is skipped
  and its snapshot is preserved (no destructive diff on a transient failure).
- `keyword` mode depends on Google's own advertiser suggestions; for an exact competitor, use
  `domain` or `advertiser` mode.
- Reads only public, advertiser-level transparency data; use responsibly and per Google's terms.

### Use with AI agents (MCP)

This actor is available to any MCP-compatible agent (Claude, ChatGPT, Cursor, LangChain) via the hosted
**[Apify MCP server](https://mcp.apify.com)** — connect once, then ask the agent to run
`mochiboo/competitor-ad-monitor-google`.

**Want only this actor as a tool, with no store search in the way?** Point the agent at
`https://mcp.apify.com/?actors=mochiboo/competitor-ad-monitor-google` and it loads as a single named tool —
the agent calls it directly instead of searching for it and possibly picking someone else.

It is a natural fit for autonomous monitoring loops:

- **Returns only the delta** — an agent re-reading the same full ad list every day wastes its context; this returns just what changed.
- **`changeType` is filterable** — ask for `new` and `changed` only, and ignore `removed` / `first_run_baseline`.
- **Small default run** (20 ads per watch) so a scheduled agent stays cheap.
- **Flat output**, one level deep, no decode logic.

### Related ad-intelligence actors

- **[Google Ads Transparency Scraper](https://apify.com/mochiboo/google-ads-transparency-scraper)** — the full snapshot this monitor diffs against; use it for the initial deep pull.
- **[Competitor Ad Intel — All Platforms](https://apify.com/mochiboo/competitor-ad-intel-all-platforms)** — Google **and** Bing in one normalized dataset.
- **[Bing Ads Library Scraper](https://apify.com/mochiboo/bing-ads-library-scraper)** — the Microsoft/Bing Ads Library (EEA scope).
- **[YouTube Ads Scraper](https://apify.com/mochiboo/youtube-ads-transparency-scraper)** — video ads with real YouTube video IDs and watch URLs.
- **[Winning Ad Creatives](https://apify.com/mochiboo/winning-ad-creatives-scraper)** — ranks a competitor's ads by how long they have survived, so you get the proven creatives instead of the full list.

### FAQ

**What happens on the very first run?**
There is nothing to compare against yet, so every ad comes back as `first_run_baseline`. That run
establishes the snapshot; from the second run onward you get only real changes.

**How often should I schedule it?**
Daily is the usual choice for creative-refresh tracking. Use the Apify
[scheduler](https://docs.apify.com/platform/schedules) — no code needed. Weekly is fine for slower niches.

**How do I get alerted instead of checking the dataset?**
Attach a [webhook or integration](https://docs.apify.com/platform/integrations) to the run — Slack,
email, Zapier, Make, or your own endpoint — so a run with changes pushes straight to you.

**Do I need a Google account?**
No. It reads the public [Google Ads Transparency Center](https://adstransparency.google.com) — no login, no cookies.

**Does this collect personal data?**
No. Output is advertiser/company-level only (PDPA / GDPR-friendly by design).

### Support & feedback

Found a bug or a missing field? Open an issue on the actor's **Issues** tab — this actor is actively maintained.
If it saved you time, an **honest review on the Apify Store** helps other people find it.

# Actor input Schema

## `searchMode` (type: `string`):

How to identify the competitor(s) to watch. 'domain' = brand/domain (e.g. nike.com). 'advertiser' = exact advertiser IDs (AR...). 'keyword' = a keyword; the actor watches the top advertisers Google associates with it.

## `queries` (type: `array`):

Domains, advertiser IDs, or keywords to monitor — must match 'Watch by'. Examples: domain -> \["nike.com"]; advertiser -> \["AR16735076323512287233"]; keyword -> \["running shoes"]. Tip: one schedule per competitor keeps each change-feed separate.

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

Restrict to ads shown in a country. Use a name/code (e.g. "US", "United Kingdom", "DE") or "anywhere" for global. Unknown values fall back to anywhere. The region is part of a watch's identity, so the same competitor in two regions is tracked separately.

## `maxAdsPerWatch` (type: `integer`):

How many of the competitor's current ads to fetch and diff each run. A daily change-feed only needs the active set, so the default is low (fast + cheap). Raise it for advertisers with very large libraries. 0 = no limit.

## `adFormats` (type: `array`):

Only watch these creative formats. Leave empty for all.

## `includeUnchanged` (type: `boolean`):

Off (default): the feed contains ONLY changes — new, changed, and removed ads. On: also emit ads that did not change this run (flagged changeType=unchanged), for a full daily snapshot. Costs more per run.

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

Keyword mode only: how many of the top resolved advertisers to monitor, per keyword.

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

Optional. Only consider ads whose active period overlaps on/after this date (YYYY-MM-DD). Usually left blank for monitoring.

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

Optional. Only consider ads whose active period overlaps on/before this date (YYYY-MM-DD). Usually left blank for monitoring.

## Actor input object example

```json
{
  "searchMode": "domain",
  "queries": [
    "nike.com"
  ],
  "region": "anywhere",
  "maxAdsPerWatch": 20,
  "adFormats": [],
  "includeUnchanged": false,
  "maxAdvertisersPerKeyword": 3,
  "dateFrom": "",
  "dateTo": ""
}
```

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

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

# Run the Actor and wait for it to finish
run = client.actor("mochiboo/competitor-ad-monitor-google").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "nike.com"
  ],
  "region": "anywhere"
}' |
apify call mochiboo/competitor-ad-monitor-google --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=mochiboo/competitor-ad-monitor-google",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/BW9E8l7pBGKDFnQrg/builds/B97NDOZTgyDwbJNXA/openapi.json
