# Google Ads Transparency Scraper — Ads by Domain & Monitor (`tiger19935/google-ads-transparency-monitor`) Actor

Get every Google ad of any company by domain: ad text, landing URL, first/last shown and format. Bulk-check if companies advertise, or monitor new and stopped ads. From $1.50 per 1,000 ads.

- **URL**: https://apify.com/tiger19935/google-ads-transparency-monitor.md
- **Developed by:** [Andrew Boiko](https://apify.com/tiger19935) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 ad founds

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 - Ads by Domain & Monitor

Get **every Google ad of any company** from the public [Google Ads Transparency Center](https://adstransparency.google.com/) - by **domain**, advertiser ID or advertiser name:

- **Ads** - one row per ad: advertiser, ad format (text / image / video), first and last shown date, days shown, active flag, link to the ad page, and optionally the **ad text** (headline, description, button, landing URL).
- **Check** - one summary row per domain: *does this company advertise on Google at all?* How many ads, which advertisers, which formats, when was the last ad shown. Built for lists of thousands of domains.
- **Monitor** - schedule it and receive **only new and stopped ads** since the last run, per domain, with a summary row.

No Google account, no API key, no cookies. Filter by **country, platform (Search, YouTube, Shopping, Play, Maps), ad format and dates**.

### Who is it for?

- **PPC agencies and freelancers** - see what competitors run on Google before pitching or building a campaign; spot new competitor ads the day they appear.
- **Lead generation and sales** - "does this company run Google Ads?" for 5,000 domains in one run (a company that pays for ads has budget). Feed the answer to your CRM.
- **SaaS, market research and AI agents** - structured data on who advertises, since when, in which format and country.
- **Brand protection and compliance** - find who advertises on your brand or domain, and when those ads started and stopped.

### What data do you get?

Every row has a `recordType`: `ad`, `domainCheck` or `monitorSummary`.

| Field | Example |
|---|---|
| `query`, `queryType`, `resolvedFrom` | `hubspot.com`, `domain` (or `advertiserId`; for `advertiserName` the `query` is the advertiser ID found and `resolvedFrom` is the name you typed) |
| `advertiserId`, `advertiserName` | `AR10072600183532683265`, `Hubspot, Inc.` |
| `creativeId`, `adUrl` | `CR05575990503767277569`, link to the ad on the Transparency Center |
| `format`, `adDomain` | `text` / `image` / `video`, `hubspot.com` |
| `firstShown`, `lastShown`, `daysShown`, `isActive` | ISO dates, days with impressions, shown within the last 2 days |
| `imageUrl`, `previewUrl` | picture of image ads (text ads are often archived as pictures), Google's preview script |
| `headline`, `description`, `longHeadline`, `callToAction`, `visibleUrl`, `landingUrl`, `adTextStatus` | ad text (only with **Extract ad text**) |
| `advertises`, `adsCountMin`, `adsCountMax`, `advertisers[]`, `formatsOnFirstPage`, `latestShown` | domain check (Google reports a range such as 3,000-4,000) |
| `changeType` | `new` / `stopped` in monitor mode |

### How to use

1. Pick a **mode**: Ads, Check or Monitor.
2. Paste **domains** (full URLs are fine - `https://www.hubspot.com/pricing` becomes `hubspot.com`), and/or **advertiser IDs** (`AR...`, from the advertiser page URL) and **advertiser names**.
3. Optionally set **region**, **platform**, **ad format**, **dates** and **max ads per domain**.
4. Click **Start**, then download JSON, CSV or Excel - or schedule it.

#### Example input - all ads of a domain in Germany, with ad text

```json
{
  "mode": "ads",
  "domains": ["hubspot.com"],
  "region": "DE",
  "platform": "SEARCH",
  "maxAdsPerDomain": 50,
  "includeAdText": true
}
```

#### Example input - does each company advertise? (bulk check)

```json
{
  "mode": "check",
  "domains": ["hubspot.com", "https://www.shopify.com/pricing", "example.org"],
  "advertiserNames": ["Zendesk"]
}
```

#### Example input - monitor competitors every morning

```json
{
  "mode": "monitor",
  "domains": ["hubspot.com", "pipedrive.com"],
  "maxAdsPerDomain": 1000
}
```

#### Example output - an ad (with ad text)

```json
{
  "recordType": "ad",
  "query": "hubspot.com",
  "queryType": "domain",
  "resolvedFrom": null,
  "advertiserId": "AR10072600183532683265",
  "advertiserName": "Hubspot, Inc.",
  "creativeId": "CR05575990503767277569",
  "format": "video",
  "firstShown": "2026-07-01T16:31:20.000Z",
  "lastShown": "2026-10-06T04:02:48.000Z",
  "daysShown": 99,
  "isActive": true,
  "adDomain": "hubspot.com",
  "imageUrl": null,
  "previewUrl": "https://displayads-formats.googleusercontent.com/ads/preview/content.js?client=ads-integrity-transparency&creativeId=814653858379",
  "adUrl": "https://adstransparency.google.com/advertiser/AR10072600183532683265/creative/CR05575990503767277569?region=anywhere",
  "headline": "Follow up faster. Close more deals.",
  "description": "Get the context to close the deal. HubSpot keeps track of all your contact history.",
  "longHeadline": "Everything your team needs to grow. Get started free with HubSpot Smart CRM.",
  "callToAction": "Sign up",
  "visibleUrl": "hubspot.com",
  "landingUrl": "https://www.hubspot.com/crm/e010a",
  "adTextStatus": "ok",
  "region": "anywhere",
  "platformFilter": "all",
  "changeType": null,
  "scrapedAt": "2026-10-06T04:30:00.000Z"
}
```

#### Example output - a domain check

```json
{
  "recordType": "domainCheck",
  "query": "hubspot.com",
  "queryType": "domain",
  "resolvedFrom": null,
  "advertises": true,
  "adsCountMin": 3000,
  "adsCountMax": 4000,
  "advertisers": [
    { "id": "AR10072600183532683265", "name": "Hubspot, Inc.", "adsOnFirstPage": 6 },
    { "id": "AR15555902810561183745", "name": "Investiere Dich Frei GmbH", "adsOnFirstPage": 1 }
  ],
  "formatsOnFirstPage": { "text": 9, "image": 0, "video": 1 },
  "latestShown": "2026-10-06T04:11:11.000Z",
  "earliestShownOnFirstPage": "2021-10-25T07:00:00.000Z",
  "region": "anywhere",
  "platform": "all",
  "format": "all",
  "checkedAt": "2026-10-06T04:30:00.000Z",
  "status": "ok",
  "error": null
}
```

A domain without ads gives `"advertises": false` and `0` / `0`. If a domain could not be checked, the row has `"status": "error"` and an `error` text - and is **not charged**.

#### Example output - a monitor summary

```json
{
  "recordType": "monitorSummary",
  "query": "hubspot.com",
  "queryType": "domain",
  "resolvedFrom": null,
  "changeType": "update",
  "adsScanned": 412,
  "newAds": 2,
  "stoppedAds": 1,
  "activeAds": 398,
  "trackedAds": 450,
  "scanTruncated": false,
  "previousRunAt": "2026-10-05T04:30:00.000Z",
  "region": "anywhere",
  "platform": "all",
  "format": "all",
  "checkedAt": "2026-10-06T04:30:00.000Z",
  "status": "ok",
  "error": null
}
```

### Pricing

Pay per event - you pay only for results:

| Event | Price |
|---|---|
| Ad saved to the dataset (Ads mode; every new or stopped ad in Monitor mode) | **$0.0015** per ad (**$1.50 per 1,000**) |
| Ad text extracted (only ads whose headline or description was really read) | **$0.002** per ad (**+$2 per 1,000**) |
| Domain / advertiser checked (Check mode, and one summary row per target in Monitor mode) | **$0.003** each (**$3 per 1,000**) |

plus a small per-run start fee shown on the *Pricing* tab. Platform usage is included.

*Examples:* 1,000 ads = **$1.50**; the same 1,000 ads with ad text for every ad = $3.50 at most (text is never charged when it could not be read); checking 1,000 domains = **$3**; a daily monitor of 10 competitors with about 5 changes each = 10 x $0.003 + 50 x $0.0015 = about **$0.11 per day**.

**What is never charged:** invalid domains, domains that failed (rows with `status: "error"`), empty or blocked responses, duplicates (the same ad found twice in one run), ads beyond your *maximum cost per run*, and ad text that could not be extracted. The Actor checks your cost limit before every batch and stops softly with a note in `OUTPUT`.

### Filters

- **Region** - ads shown in one of 40 countries, or anywhere.
- **Platform** - Google Search, YouTube, Shopping, Play or Maps.
- **Ad format** - text, image or video.
- **Dates** (`dateFrom` / `dateTo`) - ads shown within the period (not used in Monitor mode).
- **Max ads per domain** - reads the newest ads first; the Transparency Center lists up to thousands of ads for big advertisers.

For domains, the Transparency Center returns ads shown *for* that website (the ad's landing/display domain), which may belong to several advertisers - an agency, a reseller, a partner. Use the `advertiserId` of a row to follow one exact advertiser, or pass **Advertiser IDs** directly.

### Monitoring: new and stopped ads

Set **Mode = Monitor** and add a **schedule** (daily or weekly).

1. **First run = baseline.** The Actor saves the ads that are running now, returns one `monitorSummary` row per domain (`changeType: "baseline"`) and charges only the check. No ad rows.
2. **Later runs** return `ad` rows with `changeType: "new"` (an ad that was not there before) or `"stopped"` (an ad that was running at the last run and has not been shown for more than 3 days), plus one `monitorSummary` row per domain with the counts.
3. A domain that fails (Google blocked, timeout) keeps its old state untouched and is reported with `status: "error"` - nothing is falsely reported as new or stopped, nothing is charged.
4. If your *maximum cost per run* stops a run early, the changes that did not fit are not lost: they stay out of the saved state and the next run reports them (the target's note in `OUTPUT` says how many).

State is kept in a named key-value store of your account, one record per domain. It belongs to this exact list of domains/advertisers plus region, platform and format: **changing the list starts a new baseline**, so use one saved task per list. Raise **Max ads per domain** for advertisers with thousands of ads - the scan reads newest ads first and the `OUTPUT` warns when the cap cut it short. Ads that were first shown long before the previous run and were outside an earlier capped scan are learned silently, not reported as new.

### Ad text (best effort)

Turn on **Extract ad text** to read headline, description, long headline, button text, visible URL and landing URL from Google's ad preview.

- Google **rate limits** the preview server hard: about **15-20 previews per minute** without a proxy. The Actor goes one at a time, waits and retries on HTTP 429 / 403, and stops extracting for the rest of the run if Google keeps refusing (`adTextStatus: "rate_limited"`) or the preview server keeps failing (`adTextStatus: "error"`). The ad rows are still saved and charged only as ads.
- Text ads are often archived by Google as **pictures** - they have no text to read (`adTextStatus: "no_text"`, `imageUrl` is set). Video and discovery ads usually have text.
- You are **never charged** for ad text that was not extracted. `adTextStatus` is `ok`, `no_text`, `rate_limited`, `error`, or `not_requested` (also for the *stopped* ads of Monitor mode, which never get ad text).
- With a **proxy configured** previews are requested through a new IP each time, which is much faster.

### Use with Google Maps Scraper

Scrape businesses with a Google Maps scraper, take the **website** column and pass it to **Domains** in *Check* mode: you get a yes/no "runs Google Ads" for every business, a strong buying signal for agencies. In Make, n8n or Zapier: *Maps scraper -> Iterator -> Google Ads Transparency Scraper -> filter `advertises = true` -> CRM*.

### API, MCP and automation

```bash
curl -X POST "https://api.apify.com/v2/acts/<USERNAME>~google-ads-transparency-monitor/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"mode":"check","domains":["hubspot.com","shopify.com"]}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("<USERNAME>/google-ads-transparency-monitor").call(run_input={"mode": "ads", "domains": ["hubspot.com"], "maxAdsPerDomain": 100})
for ad in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(ad["advertiserName"], ad["format"], ad["lastShown"], ad["adUrl"])
```

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_TOKEN>' });
const run = await client.actor('<USERNAME>/google-ads-transparency-monitor').call({"mode":"monitor","domains":["hubspot.com"],"maxAdsPerDomain":500});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

The Actor works with the **Apify MCP server**, so AI agents (Claude, ChatGPT, Cursor) can ask "does example.com advertise on Google?" directly. Results also go to Google Sheets, Airtable, Slack or your CRM through Make, Zapier and n8n integrations. Every run saves `OUTPUT` (counts, charged events, invalid inputs, skipped items, warnings) and `RESULTS_MD` (a short Markdown summary) to the key-value store.

### FAQ

**Where does the data come from? Is it legal?** From the public [Google Ads Transparency Center](https://adstransparency.google.com/), which Google publishes so anyone can see who advertises. No login is used and no personal data beyond what Google publishes about advertisers. This is an independent tool and is **not affiliated with, endorsed by or sponsored by Google**. You are responsible for using the data in line with the law and Google's terms.

**Does a domain with no ads cost anything?** In Check and Monitor modes the check itself is charged ($0.003) because the answer "no ads" is the result; in Ads mode nothing is charged when there are no ads. Failed lookups are never charged.

**Why do I get fewer ads than the "3,000-4,000" in the check?** Google reports a range for large advertisers; the Actor reads up to **Max ads per domain**, newest first (up to 10,000 per domain). The count range in `domainCheck` is Google's own.

**Can I get ads of one exact company only?** Yes: use its **Advertiser ID**, or run a domain once and take the `advertiserId` of the rows you want. **Advertiser names** are matched by text (up to 5 advertisers per name), so IDs are more precise.

**What is "region"?** The country filter of the Transparency Center: ads that were shown in that country. "Anywhere" returns all regions.

**What does "stopped" mean?** The ad's last impression is more than 3 days old. Google shows the date of the last impression, not an explicit end date.

**Why is `isActive` false for an old ad?** `isActive` means shown within the last 2 days of the run.

**Does it use proxies?** Not normally. If Google blocks direct requests, the Actor switches to Apify datacenter proxy automatically for the rest of the run (`proxy: "fallback"` in `OUTPUT`). You can set your own proxy in the input.

**Something is missing or broken?** Open an issue in the *Issues* tab. The Transparency Center is an internal Google interface that can change without notice; the Actor fails safe (errors, never wrong or charged data).

### Changelog and support

See [CHANGELOG](CHANGELOG.md). Questions and feature requests: Issues tab of this Actor.

***

### More tools by the same author

- [Keyword Search Volume & Ideas](https://apify.com/tiger19935/keyword-search-volume-api): Bulk Google search volume and keyword ideas
- [Keyword Autocomplete Explorer](https://apify.com/tiger19935/keyword-autocomplete-explorer): Google, YouTube, Amazon and Bing suggestions
- [Website Technology Detector](https://apify.com/tiger19935/website-technology-detector): Detect CMS, ecommerce platform, analytics and email provider
- [Domain to Company Enrichment](https://apify.com/tiger19935/domain-company-enrichment): Turn domains into company profiles

# Changelog

This Actor's version history is a separate document: https://apify.com/tiger19935/google-ads-transparency-monitor/changelog.md

# Actor input Schema

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

ads = one row per ad of each domain/advertiser. check = one summary row per domain that tells whether it advertises on Google (cheapest, for bulk lists). monitor = schedule it; the first run saves a baseline, later runs return only new and stopped ads.

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

Company websites to look up, e.g. hubspot.com. Full URLs are fine (https://www.hubspot.com/pricing becomes hubspot.com). Up to 10,000. Invalid entries are skipped, listed in the OUTPUT record and never charged.

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

Advertiser IDs from the Ads Transparency Center (AR followed by digits, e.g. AR10072600183532683265 - it is in the advertiser page URL). Use them to get the ads of one exact legal advertiser instead of everything shown for a domain.

## `advertiserNames` (type: `array`):

Company names to look up (e.g. HubSpot). Each name is resolved with the Transparency Center search to up to 5 advertisers whose name contains the text; each advertiser is then handled like an Advertiser ID. Prefer domains or IDs for exact results.

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

Only ads shown in this country. "Anywhere" returns ads from all regions.

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

Only ads shown on this Google surface. "All" returns every platform.

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

Only ads of this creative format. "All" returns text, image and video ads.

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

Only ads shown on or after this day (YYYY-MM-DD). Ignored in monitor mode. Leave empty for no start limit.

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

Only ads shown on or before this day (YYYY-MM-DD). Ignored in monitor mode. Leave empty for no end limit.

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

Upper limit of ads read for each domain or advertiser (newest first). In monitor mode it is the size of the scan; raise it for advertisers with thousands of active ads. Ads mode: you pay per ad saved.

## `includeAdText` (type: `boolean`):

Best effort: read headline, description, button text and landing URL from Google's ad preview. Google rate limits previews (about 15-20 per minute without a proxy), so this is slow, and only ads whose text was actually extracted are charged for it. Text ads shown as pictures have no text.

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

Not needed normally. If Google blocks the direct connection, the Actor switches to Apify datacenter proxy by itself. Set a proxy here to use it from the start (it also makes ad text extraction faster).

## Actor input object example

```json
{
  "mode": "ads",
  "domains": [
    "hubspot.com"
  ],
  "region": "anywhere",
  "platform": "all",
  "format": "all",
  "maxAdsPerDomain": 20,
  "includeAdText": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Ads, domain checks or monitor changes found in this run.

## `domainChecks` (type: `string`):

One row per domain: does it advertise on Google (check and monitor modes).

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

Counts, charged events, invalid inputs, skipped items with reasons, ad text status and warnings.

## `markdown` (type: `string`):

Short Markdown summary of the run for people and AI agents.

# 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 = {
    "mode": "ads",
    "domains": [
        "hubspot.com"
    ],
    "maxAdsPerDomain": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tiger19935/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 = {
    "mode": "ads",
    "domains": ["hubspot.com"],
    "maxAdsPerDomain": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("tiger19935/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 '{
  "mode": "ads",
  "domains": [
    "hubspot.com"
  ],
  "maxAdsPerDomain": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call tiger19935/google-ads-transparency-monitor --silent --output-dataset

```

## MCP server setup

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