# Google Ads Competitor Tracker (`agency-shift/google-ads-competitor-tracker`) Actor

Discover public Google ads by competitor domain. Export creative previews and first/last shown dates, or track newly observed ads and changes with persistent history.

- **URL**: https://apify.com/agency-shift/google-ads-competitor-tracker.md
- **Developed by:** [Valdeir Lima](https://apify.com/agency-shift) (community)
- **Categories:** Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.78 / 1,000 ad records

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

### What does Google Ads Competitor Tracker do?

**Find public Google ads associated with competitor domains and track observed creative changes over time.** Enter a domain such as `nike.com` to collect listing data from the [Google Ads Transparency Center](https://adstransparency.google.com/), including advertiser and creative IDs, formats, available text or images, source links, and first/last-shown dates when Google supplies them.

Use a one-time snapshot or repeat a saved monitor to receive new and changed observations with before-and-after values. Apify provides API access, scheduling, downloadable datasets, run monitoring, and integrations. No Google account, Google Ads API key, OCR, or separate ad-preview requests are required.

### Why use Google Ads Competitor Tracker?

Build a competitor creative watchlist, review changes to publicly visible ad messaging, or maintain an evidence trail for marketing research. Every record keeps the requested domain, observation time, creative identity, and source link so you can inspect the underlying ad.

Domain results can include agencies or partners advertising that domain. They are not proof of brand ownership. **New means first observed by your monitor**, not a verified launch date. This Actor does not provide spend, impressions, conversions, performance, or ROI estimates.

### How to use Google Ads Competitor Tracker

1. Open the Input tab and enter one to ten public domains or website URLs.
2. Choose **Snapshot** for current observations or **New and changed ads** for a persistent monitor.
3. Set the output and per-domain observation limits, then start the Actor.
4. Inspect the dataset and the **SUMMARY** record in the run's key-value store for coverage, limits, and errors.
5. For a recurring watch, save your input and use an Apify Schedule with the same monitor name. Connect Apify webhooks or integrations if you want to route results elsewhere; the Actor itself does not send notifications.

### Input

```json
{
  "domains": ["nike.com"],
  "mode": "changes",
  "monitorName": "competitor-watch",
  "emitInitialSnapshot": true,
  "maxResults": 100,
  "maxAdsPerDomain": 200,
  "format": "ALL"
}
```

| Field | Meaning | Default |
| --- | --- | --- |
| `domains` | Required array of 1–10 public domains or URLs; normalized to hostnames | — |
| `mode` | `snapshot` or `changes` | `snapshot` |
| `maxResults` | Maximum paid output records for the whole run, 1–10,000 | `1000` |
| `maxAdsPerDomain` | Maximum ads examined per domain, including unchanged ads, 1–1,000 | `200` |
| `format` | `ALL`, `TEXT`, `IMAGE`, or `VIDEO` | `ALL` |
| `monitorName` | History name, 1–50 letters, digits, underscores or hyphens; first character must be alphanumeric | `default` |
| `emitInitialSnapshot` | Output initial paid records when creating a Changes baseline | `true` |

Version 1 searches worldwide across all platforms. It does not offer region or platform filters. See the Input tab for validation and descriptions.

### Output

The default dataset contains paid ad observations and change events. The following simplified record is **illustrative**, with placeholder IDs and values:

```json
{
  "domain": "example.com",
  "advertiserId": "ILLUSTRATIVE_ADVERTISER_ID",
  "creativeId": "ILLUSTRATIVE_CREATIVE_ID",
  "advertiserName": "Example advertiser",
  "matchedDomain": "example.com",
  "format": "IMAGE",
  "adText": null,
  "imageUrl": null,
  "firstShown": null,
  "lastShown": null,
  "eventType": "changed",
  "changedFields": ["advertiserName"],
  "before": {"advertiserName": "Previous example name"},
  "after": {"advertiserName": "Example advertiser"},
  "observedAt": "2026-09-24T12:00:00.000Z",
  "eventId": "ILLUSTRATIVE_EVENT_ID"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel. **SUMMARY** holds run coverage and errors without an additional event charge. A fatal error, when recorded, is available under **ERROR**. Monitor history is stored separately in a named key-value store.

### Data table

| Fields | Meaning |
| --- | --- |
| `domain`, `matchedDomain` | Requested hostname and source-provided domain match, when available |
| `advertiserId`, `advertiserName`, `creativeId` | Public advertiser and creative identity |
| `format`, `adText`, `imageUrl` | Listing-level creative information; unavailable fields remain null |
| `previewUrl`, `creativeUrl` | Links to inspect the public preview or creative |
| `firstShown`, `lastShown` | Google-provided dates, when available; not campaign performance measures |
| `previewFingerprint` | Stable comparison fingerprint of listing preview information |
| `observedAt` | Time this Actor observed the record |
| `eventType` | `snapshot`, `initial`, `new`, or `changed` |
| `changedFields`, `before`, `after` | Comparison evidence for changes |
| `eventId` | Key consumers can use to deduplicate repeated deliveries |

Text is often unavailable because the listing exposes an image instead. The Actor preserves nulls rather than inventing copy or reading text from images.

### How much does it cost to track Google ads?

The price is **$0.78 per 1,000 delivered records** (`$0.00078` each), plus the standard **$0.00005 startup event**. Apify bills startup events per GB of Actor memory, with a minimum of one event. Platform compute is included. The minimum maximum-charge budget is **$0.01**.

At the default memory allocation, a run delivering 100 records costs approximately **$0.07805**; 1,000 records cost **$0.78005**. Setting a budget is a cap, not a prepaid fee or guaranteed output count.

Snapshots charge for each delivered record on each run. Changes mode charges for delivered initial, new, and changed records. Set `emitInitialSnapshot` to `false` to create a baseline without initial dataset records. Unchanged observations, SUMMARY, and stored monitor history have no additional record event charge; startup still applies when nothing changes.

### Tips and monitoring behavior

Reuse the same monitor name for repeated comparisons. Use another name for a separate baseline. Named history is isolated by Apify user and Actor, and a lock protects a monitor against overlapping runs.

The comparison tracks advertiser name, matched domain, format, available text, image, and a stable preview fingerprint. Routine `lastShown` date updates do not trigger change events. A missing value in a later response does not erase a previously known comparison value or manufacture a change.

The observation cap is separate from the output cap: examining 200 ads can produce zero changed records. Limits, partial responses, and result ordering mean a scan is **not a complete inventory of every ad**. Missing ads do not produce retirement, disappearance, or stopped-campaign events.

History is limited to 10,000 ads per target and a 16 MB checkpoint. Exceeding a history limit produces an error rather than silently discarding comparison history. Delivery is at least once: an interruption after writing a dataset record but before saving its checkpoint can repeat the same `eventId` and charge for the duplicate delivery. Downstream consumers should deduplicate by `eventId`.

### FAQ, limitations, and support

**Does it fetch complete ad copy or analyze images?** No. It reads public listing responses. Extra preview pages, OCR, video transcripts, and performance data are outside this version's scope.

**Can I prove a campaign is active or successful?** No. First/last-shown dates and observed creatives are public research signals. They do not establish current delivery, budget, conversions, or profitability.

**What if Google blocks a request or changes its response?** Review SUMMARY, ERROR when present, and the run log. Partial coverage is not evidence that previously observed ads were removed. Availability and fields depend on Google's public service.

**Can I automate exports?** Yes. Use Apify's API, schedules, webhooks, and integrations. Notifications are configured through those services, not sent directly by this Actor.

This is an independent tool, not affiliated with Google or the advertisers returned. Use public data in accordance with applicable terms and laws. Report reproducible problems or request additional formats and custom workflows through this Actor's Issues tab.

# Actor input Schema

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

One to ten public domains or website URLs, such as nike.com. URLs are normalized to hostnames. Results can include agencies or partners advertising the domain; a match does not prove ownership.

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

Snapshot returns observed ads on every run. Changes keeps a baseline and returns initial, new, or changed observations for this monitor.

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

Maximum paid dataset records across this run. Your maximum-charge budget may stop the run earlier.

## `maxAdsPerDomain` (type: `integer`):

Upper limit on ads examined for each domain, including unchanged ads. Limited observations do not establish that an absent ad stopped running.

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

Filter listing results by format. Version 1 searches worldwide across all platforms; there are no region or platform filters.

## `monitorName` (type: `string`):

In Changes mode, reuse this name to compare against the same history. Use a new name for a separate watch or baseline. History is isolated by Apify user and Actor.

## `emitInitialSnapshot` (type: `boolean`):

In Changes mode, output paid initial records while creating a new baseline. Turn off to seed a baseline without initial dataset records. Later new and changed records are still charged.

## Actor input object example

```json
{
  "domains": [
    "nike.com"
  ],
  "mode": "snapshot",
  "maxResults": 1000,
  "maxAdsPerDomain": 200,
  "format": "ALL",
  "monitorName": "default",
  "emitInitialSnapshot": true
}
```

# Actor output Schema

## `records` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `error` (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 = {
    "domains": [
        "nike.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("agency-shift/google-ads-competitor-tracker").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"] }

# Run the Actor and wait for it to finish
run = client.actor("agency-shift/google-ads-competitor-tracker").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"
  ]
}' |
apify call agency-shift/google-ads-competitor-tracker --silent --output-dataset

```

## MCP server setup

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

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/2KkJs2uN7NfqJ1nv5/builds/cIvmE43Jv3D0ikRTM/openapi.json
