# Google Ads Domain Checker — Advertiser Lookup (`monkey.d.scraping/google-ads-domain-checker`) Actor

Check website domains for Google Ads Transparency Center observations. Get advertiser names, ad dates and evidence links in one row per domain. Compare snapshots for PPC research and CRM enrichment.

- **URL**: https://apify.com/monkey.d.scraping/google-ads-domain-checker.md
- **Developed by:** [Monkey D. Scraping](https://apify.com/monkey.d.scraping) (community)
- **Categories:** Marketing, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.50 / 1,000 confirmed domain observations

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 Domain Checker — Advertiser Lookup

Look up Google Ads advertisers associated with website domains using public Google Ads Transparency Center observations. Get one structured row per domain with advertiser names, dates, source links and a comparison with an earlier observation. For PPC agencies, CRM enrichment and competitor watchlists. No Google login or AI key needed.

Cloud checks include a 50-domain sample, budget handling and interrupted-run recovery. These checks do not establish large-scale or long-term reliability.

### Coverage: a sample, not a complete advertiser list

This Actor is a domain screening and observation tool. It reads up to two pages (200 ads) and can miss advertisers, particularly when one account dominates the results. In repeated checks of monday.com, the sample returned fewer advertiser IDs than a competing checker. Do not use it as an exhaustive advertiser inventory or as proof that a company is not advertising.

The default table states whether the sample hit its limit, a continuation failed, or no further page was returned. Even the last case does not certify completeness. Profile enrichment covers at most ten observed IDs per domain. Country describes Google's advertiser disclosure, not where ads were shown. Confirmed domain observations are billable even if the sample or profiles are partial; inconclusive domains are free.

### Quick start

1. Paste websites into **Website domains or URLs**.
2. Keep the 30-day activity window, or select another period.
3. Run and open **Domain results**. Download JSON, CSV or Excel through Apify.

```json
{"websites": ["buffer.com", "nomadgoods.com"]}
```

### Input settings

| Setting | Default | When to change it |
| --- | --- | --- |
| `websites` | Two example websites; required | Paste your own domains or HTTP(S) URLs. Paths and `www` are normalized; duplicate domains produce one row. |
| `lookbackDays` | 30 | Choose 1–365 days to classify the dates in the ad sample. |
| `previousSnapshotStoreId` | Empty | Optional: select an earlier run's storage to compare confirmed observations. |

For a first check, only edit the website list. Scope is worldwide (`anywhere`); country filtering is not implemented. The 1,000-domain input limit is not a verified scale benchmark.

### Runtime and list size

Processing is sequential. Before profile enrichment was added, in one selected 50-domain cloud sample, all domains were processed in 85 seconds: 43 had confirmed observations and seven were inconclusive. This is an example, not an accuracy or speed guarantee. Source retries and storage latency can increase runtime substantially.

For larger lists, increase the run timeout in Apify run options before starting. The current 120-second default can interrupt a large list. If interrupted, use **Resurrect** on the same run to continue from delivered results; do not create a new run with the full list unless you want a fresh observation. A 1,000-domain cloud run has not been validated.

### Real output example

Excerpt from a local source check on 25 October 2026. Values can change; this example is not a cloud performance benchmark.

```json
{
  "domain": "buffer.com",
  "status": "recent_ads_observed",
  "recentAdsObserved": true,
  "lookbackDays": 30,
  "advertiserNames": [
    "Buffer Inc"
  ],
  "sampleSize": 25,
  "hasMoreResults": true,
  "latestShownInSample": "2026-10-25T09:59:56+00:00",
  "checkedAt": "2026-10-25T12:16:31.549432+00:00",
  "changeType": "baseline",
  "sourceUrl": "https://adstransparency.google.com/?region=anywhere&domain=buffer.com"
}
```

Open **Domain results** for status and explicit coverage, **Advertiser profiles** for a flat table of advertiser names, legal names, countries and evidence links, **Compare observations** for changes, or **Evidence and coverage** to inspect sampling and source links. These views contain the same domains with different columns; the comparison view is not filtered to changed rows. Full records remain available in the dataset.

### What the results mean

| Field | Meaning |
| --- | --- |
| `status` | Recent ads observed, historical ads observed, or a clearly identified inconclusive result |
| `recentAdsObserved` | Whether at least one sampled ad was shown within the selected window; null when inconclusive |
| `adsCountLower`, `adsCountUpper` | The source's reported count interval for the domain, not a recent-window count |
| `advertisers` | Per-advertiser name, observed aliases, sampled ad count, latest sampled date and evidence/profile links. Up to ten profiles are enriched by exact ID with legal name and disclosure country where available; see profile status and limitations below. |
| `advertiserNames`, `advertiserIds` | Advertisers associated with the domain in the observed sample |
| `latestShownInSample` | Latest source date among the ads sampled |
| `sampleSize`, `hasMoreResults` | Sample coverage; more results may exist |
| `evidence` | Up to three examples with the latest dates in the sample with advertiser IDs, dates and source links |
| `changeType` | Baseline, unchanged, newly observed advertiser, activity-observation change, or comparison unavailable |
| `newlyObservedAdvertiserIds` | IDs not present in the earlier observation history |
| `checkedAt`, `sourceUrl` | Observation time and the public source page |

For example, a local observation of `buffer.com` on 25 October 2026 found recent ads associated with `Buffer Inc`, with source count bounds of 76–76. This is an observed snapshot, not a promised current result.

Increasing the sample from earlier 25-ad builds to 100 ads can reveal additional advertiser IDs. Such changes reflect expanded observation, not necessarily new advertising activity.

### Compare two runs

Select the earlier run's storage in **Compare with an earlier run**, or copy the earlier run's **Input for the next comparison** into the JSON input editor. Keep the same observation window. The earlier storage remains unchanged; the new run produces a new snapshot.

The first confirmed observation creates a baseline. A later identical observation is `unchanged`. A previously unseen advertiser is `new_advertiser_observed`. Missing IDs are not reported as removed: samples may differ. Inconclusive observations preserve the last confirmed baseline.

This version compares explicitly selected snapshots. It does not automatically discover the previous run, create schedules or send notifications. To build a scheduled workflow, pass the previous successful run's storage through your integration.

### Use cases

- **PPC agency prospecting:** add evidence of observed advertising to an existing domain list.
- **CRM enrichment:** keep advertiser names and dated source links beside company records.
- **Competitor watchlists:** compare observations without processing a dataset containing every ad.

The dataset and snapshots can be read through the Apify API and used in n8n, Make or spreadsheet workflows. No external workflow has been created for you.

### Coverage and limitations

- Ads are associated with a domain. This does not verify that the website owner paid for them. Affiliates, agencies and unrelated advertisers can appear.
- This is a bounded sample, not a complete advertiser inventory. Historical-only results mean no recent ad was found in that sample, not proof that a business has stopped advertising.
- Empty source responses are inconclusive. They are never converted to a definitive “no ads” result.
- No spend, revenue, campaign performance or purchase-intent estimates are produced.
- Up to 1,000 input websites per run; duplicates are normalized. This input limit is not a verified cloud scale benchmark.
- A run with no confirmed observations fails after saving diagnostics. A mixed run exposes partial coverage in `SUMMARY`.

### Pricing and release status

The Actor is configured at **$2.50 per 1,000 confirmed domain observations** ($0.0025 each). Both recent and historical confirmed observations count. Inconclusive results and source errors do not trigger the event. No startup event is configured; run platform usage is included in the event price. Apify storage access after the run may incur platform charges under your plan.

One event is sent only after a confirmed result has been delivered. A repeated check in a new run is a new observation and can be charged again; resurrecting the same run does not intentionally charge delivered observations again. If the API response is lost, the ledger reserves that event and does not automatically retry it.

Set a maximum cost in Apify run options to cap a run. Once another confirmed result cannot fit, collection stops and `SUMMARY` lists `remainingDomains` with `stopReason: budget_exhausted`. Some domains may therefore remain unprocessed. Without an explicit cap, the internal maximum is one event per unique input domain. To process remaining domains with a different budget, start a new run with those domains; changing a billed run's budget during resurrection is not supported.

Private owner-run checks verified event counts, exclusion of one inconclusive result, budget stopping and resurrection without extra events. These are not external customer invoices, proof of payout or the official Store test.

### Frequently asked questions

#### Can this check whether a website runs Google Ads?

It reports ads associated with the domain in Google's public transparency source. `recentAdsObserved: true` means at least one sampled ad has a date inside your chosen window. It does not prove that the website owner purchased those ads or that a campaign is live at this instant.

#### Is this a Google Ads pixel detector?

No. Results come from advertising transparency observations, not tracking tags installed on a website. A pixel alone would not prove advertising activity.

#### Does an empty result mean the company does not advertise?

No. `empty_response_unconfirmed` means the source returned no usable observations after a bounded retry. Keep it separate from confirmed historical observations. Recheck later or inspect the public source manually.

#### Does the activity window filter all ads from the past 30 days?

No. The window classifies dates in a bounded sample of up to 200 ads across two pages per domain. Source count bounds describe the source's domain result set, not the number of ads inside that window.

#### Can I get ad spend, keywords or campaign performance?

No. This Actor provides observed advertisers, dates, evidence links and comparisons. It does not estimate budgets, conversions or search keywords, or download a complete creative archive.

#### Do I need OpenRouter, a Google account or an AI API key?

No. The collector uses public observations and deterministic comparison rules. It does not send data to an LLM. Running it on Apify requires an Apify account; programmatic platform access uses your Apify credentials.

#### Can I monitor competitors automatically?

The comparison code accepts an earlier snapshot explicitly. Automatic selection of the preceding run and scheduled workflows are not implemented. A saved input with a fixed storage ID will keep comparing against that same baseline.

#### What should I do if a run is incomplete?

Read **Coverage and errors**, then inspect each domain's `status`. Source errors and empty responses remain inconclusive; they do not erase the last confirmed baseline. A successful run can still contain inconclusive domains: `processingComplete` means all requested domains were processed, while `complete` also requires confirmed observations for every domain.

### Reading results and handling interrupted runs

**Open example ad** is a clickable link to one dated creative; the full `evidence` array remains in the dataset. **Meaning of comparison** explains why an observed ID or activity-class change is not proof of a campaign launch or stop.

`SUMMARY` and `SNAPSHOT` are saved before collection and after every delivered row. `processed`, `processingComplete` and `remainingDomains` distinguish interrupted collection from completed collection with inconclusive results. `complete` additionally requires every domain to have a confirmed observation.

If a run times out or is aborted, use **Resurrect** on that same run. The collector reads its existing dataset, rebuilds progress and skips domains already delivered. It keeps the original observations, including inconclusive rows; retry those domains in a new run if you want a fresh check. Request counters in SUMMARY describe only the current process; domain and result counts cover the full run.

Keep the input and earlier comparison snapshot unchanged. If either changes, or the dataset contains duplicate, unexpected or malformed rows, the run stops with an explanatory error. Runs created before resume support cannot be upgraded by resurrection; start a new run on the new build.

Dataset delivery and progress writes are separate operations. On restart, delivered rows are the recovery source, so a stale checkpoint does not by itself cause a repeated domain. This assumes a single writer and the original dataset/storage remain available and unmodified. Storage responses are bounded at 8 MB; oversized recovery data fails explicitly. A new run uses a new dataset and does not automatically merge previous runs. No schedule or automatic retry loop is created.

An error while saving progress fails the run rather than silently continuing with missing history. Earlier-run snapshots remain read-only. Progress writes add storage cost per processed domain; the technical price benchmark is based on small cloud samples, not a guaranteed cost at scale.

#### Bounded pagination

The checker reads up to two pages of 100 ads per domain and removes repeated creative IDs. `pagesRead` counts validated pages, `paginationStatus` is `source_exhausted`, `page_limit`, or `partial`, and `paginationError` explains an unsuccessful continuation. A failed second page preserves the confirmed first-page observations and is still one billable confirmed domain. No extra pagination fee applies. `source_exhausted` only means no continuation token was returned; it does not certify a complete advertiser inventory. Unknown results remain uncharged.

Recovery reads dataset state in pages of 100 rows and fetches only the fields required to rebuild comparison and billing state. Existing detailed output stays in the dataset. This avoids downloading all advertiser profiles on restart; it does not certify a 1,000-domain scrape within the default timeout.

#### Advertiser profile details

For up to ten advertisers per domain, prioritizing those with more ads in the sample, the checker retrieves Google's public profile by exact advertiser ID. Each nested advertiser includes `profileName`, `legalName`, `disclosureCountryCode`, `identityVerified`, `profileStatus`, and `profileError`. Country is the disclosure country reported by Google, not targeting geography or an inferred headquarters. Missing values stay null. Identity verification stays null when the source omits the flag; it is distinct from ownership of the searched domain. `limit_reached` and `unavailable` distinguish profiles not fetched from unsuccessful requests. These optional details do not change the confirmed observation or add a separate fee. The run caches at most 200 compact profiles.

# Actor input Schema

## `websites` (type: `array`):

Add one website per line, for example buffer.com or https://buffer.com. Duplicate domains are checked once. Start with the two example websites. For larger lists, increase the run timeout in Apify run options; the 1,000-item limit is not a speed guarantee.

## `lookbackDays` (type: `integer`):

Default 30 days. Classifies the dates of sampled ads; it does not retrieve every ad from this period.

## `previousSnapshotStoreId` (type: `string`):

Leave empty for your first run. To compare later, use Input for the next comparison from the previous run, or select its storage here. Keep the same activity window.

## Actor input object example

```json
{
  "websites": [
    "buffer.com",
    "nomadgoods.com"
  ],
  "lookbackDays": 30
}
```

# Actor output Schema

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

No description

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

No description

## `snapshot` (type: `string`):

No description

## `nextInput` (type: `string`):

No description

## `comparison` (type: `string`):

No description

## `evidence` (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 = {
    "websites": [
        "buffer.com",
        "nomadgoods.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("monkey.d.scraping/google-ads-domain-checker").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 = { "websites": [
        "buffer.com",
        "nomadgoods.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("monkey.d.scraping/google-ads-domain-checker").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 '{
  "websites": [
    "buffer.com",
    "nomadgoods.com"
  ]
}' |
apify call monkey.d.scraping/google-ads-domain-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,monkey.d.scraping/google-ads-domain-checker"
        }
    }
}
```

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/fwGRo5e5gCKnvw4G8/builds/X6PzBi79jU3ZBMxwz/openapi.json
