# Google Ads Transparency Scraper, Checker & Competitor Monitor (`signalgrid/google-ads-transparency-monitor`) Actor

Google Ads checker and ad library scraper: check if a company or domain is running Google Ads (yes/no per domain), export competitor Search, YouTube and Display ads, or get only NEW ads since your last run. No login. Pay per result.

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

## Pricing

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

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 Monitor: new competitor ads only, as diff alerts

**Get an alert-ready list of the Google ads your competitors launched since your last check, and pay only for what's new.** Most Google Ads Transparency Center scrapers re-download and re-bill every ad on every run. This Actor remembers what you've already seen. Schedule it with `onlyNewAds: true`, and each run returns **only new creatives**, plus one summary row per advertiser with new, dropped and active counts. On quiet weeks a run costs a few cents.

It covers Search text, Display/image and YouTube video ads from the public Google Ads Transparency Center. No login, no cookies, no proxies. By Signalgrid.

### 30-second start (diff alerts)

```json
{"targets": ["hubspot.com", "salesforce.com", "Notion Labs"], "region": "US",
 "maxAdsPerTarget": 100, "onlyNewAds": true, "monitorId": "my-competitors"}
```

Save it as a **Task** and add a weekly **Schedule**. Connect the dataset to Slack, Sheets, email or a webhook. The first run sets the baseline. After that, every run is a "new ads this week" diff.

### New in 0.5

- **Bulk domain list plus ad activity tier.** Paste up to 200 prospect or competitor domains into `domainList` and set `activityTierOnly: true`. You get one row per advertiser with active ads, ad count, oldest and latest shown dates, the longest-running ad and a `adActivityTier` of `none`, `low`, `medium` or `high`. You pay only $0.003 per advertiser and nothing per ad. It helps you qualify which prospects actually spend on Google Ads before a sales call.
- **No silent zeros.** When Google returns an empty response or 0 ads for a resolved advertiser, you get an explicit, **free** `error` row (`empty-upstream-response` or `no-ads-returned`) instead of a misleading "0 ads" summary. You're never charged for it.

### Three ready-made tasks (copy the input, save as a Task)

**1. Weekly new-competitor-ads alert.** Only ads launched since last week; schedule weekly.

```json
{"targets": ["hubspot.com", "salesforce.com", "pipedrive.com"], "region": "US", "maxAdsPerTarget": 100, "onlyNewAds": true, "monitorId": "crm-competitors"}
```

**2. Bulk ad export (one-off library pull).** Every recent ad for a few brands, with previews.

```json
{"targets": ["Notion Labs", "asana.com"], "region": "ANY", "maxAdsPerTarget": 500}
```

**3. Google Ads checker for a prospect list (is this company running Google Ads?).** One flat row per domain, $0.003 each, no per-ad charge.

```json
{"domainList": "hubspot.com\nmonday.com\nexample-local-plumber.com", "activityTierOnly": true, "region": "US"}
```

Each domain returns `runsGoogleAds` (true / false / null = unknown, retry), `adActivityTier`, `activeAdsNow` and `transparencyUrl` (a link to check by hand). Domains with no advertiser come back as free rows with `runsGoogleAds: false`. Temporary failures have `retryable: true` and are never reported as "no ads".

### Use it from AI agents (Claude, MCP, scripts)

The output is small, predictable JSON with stable field names, so it works well as an agent tool.

- **Apify MCP server:** add `signalgrid/google-ads-transparency-monitor` as a tool in the Apify MCP server (mcp.apify.com) and ask Claude: *"Check which of these 30 domains are running Google ads and rank them by ad activity tier."* Or: *"What new ads did hubspot.com launch since last week?"*
- **One HTTP call:**

```bash
curl -X POST "https://api.apify.com/v2/acts/signalgrid~google-ads-transparency-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN&maxTotalChargeUsd=0.50" \
  -H 'Content-Type: application/json' \
  -d '{"domainList":"hubspot.com\nmonday.com\nasana.com","activityTierOnly":true,"region":"US"}'
```

- **Agent tips:** filter rows by `type` (`ad`, `advertiser-summary` or `error`). Always pass `maxTotalChargeUsd`. Reuse one `monitorId` per watchlist, so the diff memory persists. A `SKILL.md` for agent frameworks is included in the source.

### Input

| Field | Example | Notes |
|---|---|---|
| `targets` | `["hubspot.com", "Notion Labs", "AR10072600183532683265"]` | Domain, brand name or advertiser ID |
| `domainList` | `"a.com\nb.com\nc.com"` | Bulk paste, one per line. Merged with `targets` (max 200 total) |
| `activityTierOnly` | `true` | Summary rows only, with no per-ad charges |
| `region` | `US` | ANY, US, GB, CA, AU, DE, FR, IN, BR, ES, IT, NL, MX, JP |
| `maxAdsPerTarget` | `100` | Newest first. You pay per ad returned |
| `onlyNewAds` | `true` | Repeat runs return only ads you haven't seen |
| `monitorId` | `competitors-q4` | Runs with the same ID share "seen" memory |

### Output

**Ad row**

```json
{"type":"ad","advertiserName":"monday.com LTD","format":"VIDEO","firstShown":"2026-10-13T20:24:53Z",
 "lastShown":"2026-10-20T03:30:11Z","daysShown":7,"isActive":true,"isNewSinceLastRun":true,
 "adUrl":"https://adstransparency.google.com/advertiser/AR15972262369074085889/creative/CR04944652816883908609?region=US",
 "advertiserId":"AR15972262369074085889","creativeId":"CR04944652816883908609","region":"US"}
```

**Advertiser summary row** (one per advertiser per run)

```json
{"type":"advertiser-summary","input":"hubspot.com","advertiserName":"Hubspot, Inc.","adsFetched":200,
 "activeNow":187,"startedLast7Days":12,"newSinceLastRun":12,"noLongerReturnedSinceLastRun":4,
 "adActivityTier":"high","activeAdsNow":187,"oldestFirstShown":"2021-10-25T07:00:00Z",
 "latestLastShown":"2026-10-20T16:14:32Z","longestRunningAdDays":1822,"formatMix":{"TEXT":120,"IMAGE":50,"VIDEO":30}}
```

**Error row (free)**

```json
{"type":"error","input":"example.com","errorType":"empty-upstream-response","error":"... This is NOT the same as 'no ads'; retry later. You were not charged for this advertiser."}
```

### Pricing (pay per event)

- **$1.50 per 1,000 ads** returned
- **$0.003 per advertiser summary**
- Error rows are free

Two examples:

- A weekly diff on 10 competitors that finds 40 new ads costs 10 × $0.003 + 40 × $0.0015 = **$0.09**.
- A ad-activity-tier check of 100 prospect domains costs **$0.30**.

### Fair charging

- Hit your max charge per run? The Actor stops cleanly, and undelivered new ads stay unseen, so they come back on the next run.
- Unknown advertiser, outage or empty upstream response: you get a free `error` row. If every target fails upstream, the run fails and you're charged nothing.
- Ad-activity-tier runs don't consume your "new ads" memory.

### Monitor vs. one-shot scraper

| | Typical scraper | This monitor |
|---|---|---|
| Repeat runs | Re-return and re-bill all ads | Only new ads |
| Per-advertiser diff | No | New, dropped, active, last 7 days |
| Bulk domain ad-activity check | No | Yes, summary-only pricing |
| Empty or blocked upstream | Often a silent "0 results" | Explicit free error row |

### FAQ

**How do I get alerts when a competitor launches new Google ads?** Create a Task with `onlyNewAds: true` and a fixed `monitorId`, schedule it, and connect it to Slack or a webhook.

**Can I see ad spend?** No. Google doesn't publish spend for commercial ads. `adActivityTier` counts public ads only (rule: none = 0 ads; high = 20+ active or 200+ ads; medium = 5+ active or 40+ ads; else low). It is not a spend estimate. The old `budgetSignal` field and `budgetSignalOnly` input still work as aliases.

**How do I track several clients separately?** Use one `monitorId` per client.

### Limits

- Data covers only what Google publishes. Coverage varies by region.
- A domain resolves to the advertiser that owns most of the ads pointing at it. Other advertisers are listed in `otherAdvertisersOnDomain`.
- Not affiliated with Google.

### Changelog

- 0.7 (Oct 20 2026): flat per-domain answer (`runsGoogleAds`, `transparencyUrl`), `retryable` on error rows, three example tasks.
- 0.6 (Oct 20 2026): renamed budget signal to `adActivityTier` with its rule documented (old names still work); fuller dataset schema for agents.
- 0.5 (Oct 20 2026): bulk `domainList`, `activityTierOnly` mode with ad-activity-tier fields, explicit free error rows for empty upstream responses, agent/MCP docs and SKILL.md.
- 0.4 (Oct 2026): output schema and listing updates.
- 0.2 (Oct 2026): budget-safe charging, free error rows, input dedupe.
- 0.1 (Oct 2026): first version.

# Actor input Schema

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

Brand names (e.g. 'Notion Labs'), website domains (e.g. 'hubspot.com'), or advertiser IDs starting with AR. Or paste a long list into 'Bulk domain list' below.

## `domainList` (type: `string`):

Paste up to 200 domains or brand names, one per line (commas/semicolons also work). Merged with 'Advertisers to monitor'. Great for checking a prospect list.

## `activityTierOnly` (type: `boolean`):

Return one row per advertiser with ad counts, active ads, oldest/latest shown dates, longest-running ad and an ad activity tier (none/low/medium/high, based on public ad counts, not spend). You pay only the per-advertiser summary, no per-ad charges.

## `budgetSignalOnly` (type: `boolean`):

Deprecated alias of activityTierOnly, kept so existing API calls keep working.

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

Only ads shown in this country. ANY = all regions.

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

Most recent ads first. You pay per ad returned.

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

Schedule this Actor (daily/weekly) with the same Monitor ID; repeat runs return and charge only for ads you have not seen before. The summary row is always returned.

## `monitorId` (type: `string`):

Name for this watchlist. Runs that share a Monitor ID share 'seen ads' memory.

## Actor input object example

```json
{
  "targets": [
    "hubspot.com",
    "Notion Labs"
  ],
  "activityTierOnly": false,
  "region": "US",
  "maxAdsPerTarget": 20,
  "onlyNewAds": false,
  "monitorId": "default"
}
```

# Actor output Schema

## `ads` (type: `string`):

Ad rows: advertiser, format, first/last shown, active, new since last run, ad link, preview image.

## `summaries` (type: `string`):

One row per advertiser: ads fetched/delivered, active now, started last 7 days, new and dropped since last run, format mix.

## `errors` (type: `string`):

Targets that were not found or could not be fetched.

# 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": [
        "hubspot.com",
        "Notion Labs"
    ],
    "maxAdsPerTarget": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalgrid/google-ads-transparency-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 = {
    "targets": [
        "hubspot.com",
        "Notion Labs",
    ],
    "maxAdsPerTarget": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("signalgrid/google-ads-transparency-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 '{
  "targets": [
    "hubspot.com",
    "Notion Labs"
  ],
  "maxAdsPerTarget": 20
}' |
apify call signalgrid/google-ads-transparency-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,signalgrid/google-ads-transparency-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/4a7y0M7Xv4jtasxPb/builds/Ub0l32b526fAZc5Yu/openapi.json
