# Google Ads Transparency Advertiser Leads Scraper (`scrapersdelight/adstransparency-advertiser-leads-scraper`) Actor

Build a lead list of businesses buying Google ads. Search the Ads Transparency Center by trade or brand keyword and get one row per advertiser: name, advertiser ID, verified country, how many ads they run, and when their ads were last seen. Or go in reverse - give a website, get its advertisers.

- **URL**: https://apify.com/scrapersdelight/adstransparency-advertiser-leads-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Lead generation, Marketing, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 advertiser scrapeds

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?

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

## Google Ads Transparency Advertiser Leads Scraper

Turn the [Google Ads Transparency Center](https://adstransparency.google.com) into a **lead list of businesses that are actively buying Google ads**. Search by trade or brand keyword, get **one row per advertiser** — name, advertiser ID, verified country, how many ads they have run, and when their ads were last seen.

Or run it backwards: hand it a **website** and get the advertisers whose ads point at it.

### Why this one

Every other Ads Transparency actor on the Store returns **ad creatives** — one row per banner, with previews and formats. That is a creative-research product. This is a **prospecting** product: it collapses to the advertiser, so the row you get is a company you can sell to, not an image you have to de-duplicate.

The Transparency Center is the only public register that tells you a business is *paying Google right now*. For anyone selling to advertisers — agencies, PPC freelancers, landing-page and CRO tools, call-tracking, martech — that is a pre-qualified buying signal: they already have a budget and they already spend it on ads.

**Measured 2026-09-02:** five trade keywords (`plumbing`, `roofing`, `dental`, `law firm`, `landscaping`) returned **1,000 distinct advertisers**, and **100% of them parsed** with a name, advertiser ID, country and ad count. A single keyword can return up to **3,000** advertisers in one request.

### Quick start

```jsonc
{
  "keywords": ["plumbing", "roofing", "hvac"],
  "countries": ["US"],
  "minAds": 20,              // skip one-off and hobby advertisers
  "activeWithinDays": 30,    // only businesses still running ads
  "resultsPerKeyword": 500,
  "maxItems": 1000
}
```

Reverse lookup — who is buying ads for a competitor's site:

```jsonc
{ "domains": ["shopify.com", "hubspot.com"] }
```

### Output

One row per advertiser, **19 fields**.

```json
{
  "advertiserName": "Dental Hub",
  "advertiserId": "AR03454010070923214849",
  "country": "IN",
  "countryName": "India",

  "adCountMin": 6,
  "adCountMax": 6,
  "adCountExact": true,

  "lastAdSeenAt": "2026-09-01T17:29:08.000Z",
  "daysSinceLastAd": 0,
  "firstAdSeenAt": "2025-10-17T07:00:00.000Z",
  "adHistoryComplete": true,
  "creativesSampled": 6,

  "advertiserUrl": "https://adstransparency.google.com/advertiser/AR03454010070923214849?region=IN",
  "searchMode": "keyword",
  "matchedKeyword": "dental",
  "matchedDomain": null,
  "domainCreativesMatched": null,
  "domainCreativesSampled": null,
  "scrapedAt": "2026-09-02T04:25:04.360Z"
}
```

#### Measured field fill

Parser validated offline against captured live bytes on **2026-09-02**.

**1,000 advertisers across 5 keywords:**

| Field | Fill |
| --- | --- |
| `advertiserName`, `advertiserId`, `advertiserUrl`, `matchedKeyword`, `searchMode` | **100%** |
| `country`, `countryName` | **100%** |
| `adCountMin`, `adCountMax` | **100%** |
| `adCountExact` — the count is a single number rather than a band | **98.7%** |

**38 of those advertisers enriched with ad-activity dates:**

| Field | Fill | Why |
| --- | --- | --- |
| `lastAdSeenAt`, `daysSinceLastAd`, `firstAdSeenAt` | **94.7%** | 2 of the 38 (5.3%) are listed in the directory but expose **no retrievable creative**. Their dates come back `null` rather than invented. |
| `adHistoryComplete` | 100% true on this sample | `true` means the whole ad history fitted in one page, so `firstAdSeenAt` is genuinely their first ad rather than the oldest of the most recent 100 |
| `creativesSampled` | 100% | how many ads the dates were computed from |

#### The fields that do the work

- **`daysSinceLastAd`** is the qualification field. An advertiser last seen 0 days ago is spending today; one last seen 365 days ago churned a year ago. Set `activeWithinDays: 30` and the list only contains businesses currently in market.
- **`adCountMin` / `adCountMax`** size the account. The median advertiser on a trade keyword has run **5** ads; `minAds: 20` strips the hobbyists, `maxAds: 500` strips the national brands. Below roughly 1,000 ads Google reports an exact number (98.7% of our sample); above that it reports a band such as 9,000–10,000.
- **`firstAdSeenAt`** with `adHistoryComplete: true` gives real tenure — how long this business has been buying ads.
- **`advertiserUrl`** is Google's own page for that advertiser, region-pinned to their verified country, so every row is one click from the source.

### Two search modes

**Keyword mode** matches the **advertiser's registered name**, not what they sell. `plumbing` returns *KS Plumbing*, *Plumbing MD*, *Buzz Plumbing*. This is a feature for trade prospecting — small service businesses put the trade in the company name — but it means `dentist` and `dental` return different lists. Search several variants and let the actor de-duplicate by advertiser ID.

**Domain mode** goes the other way: it samples the ads pointing at a website and reports who is running them. Measured 2026-09-02 over 40 sampled ads per domain:

| Domain | Advertisers found | Top advertiser |
| --- | --- | --- |
| `shopify.com` | 2 | Shopify Inc. (39 of 40 sampled ads) |
| `nike.com` | 6 | Nike Retail BV (25), Nike, Inc. (11) |
| `hubspot.com` | 11 | Hubspot, Inc. (28), plus 10 affiliates and resellers |

That third column is the interesting one: `hubspot.com` shows 11 distinct advertisers, i.e. the affiliates and resellers bidding on someone else's brand.

Google's domain response carries no country, so in domain mode the actor recovers it with one extra name lookup per advertiser, **matched back by advertiser ID and never by name** — two companies can register the same advertiser name, and a guessed country would be worse than a blank one. Measured on a live run of `shopify.com` + `hubspot.com`: 15 advertisers, **country resolved on 15 of 15**, 23 seconds.

### Filters

| Input | Effect |
| --- | --- |
| `keywords` | advertiser-name search terms, one request each |
| `domains` | reverse lookup: website → its advertisers |
| `countries` | ISO-2 filter on the advertiser's verified location. A 1,000-row `plumbing` sample was 70% US, 13% AU, 7% CA, 5% GB |
| `minAds` / `maxAds` | account-size band |
| `activeWithinDays` | recency — drops anyone whose last ad predates the window |
| `enrichWithAdActivity` | off = names only, one request per keyword and no dates; on (default) = one extra request per advertiser |
| `resultsPerKeyword` | up to 3,000 per keyword (Google's own ceiling) |
| `maxItems` | total row cap, and therefore your billing cap |

### Pricing

Pay per advertiser delivered. No subscription, no charge for a run that finds nothing.

| Event | Price | When it fires |
| --- | --- | --- |
| `advertiser-scraped` | **$0.001** | One per advertiser pushed to the dataset |

That is **$1 per 1,000 advertisers** — cheaper per row than every Ads Transparency actor on the Store at time of writing (the next cheapest charges $1.50/1,000, and the nearest advertiser-level event charges $3.00/1,000).

Rows are billed through Apify's gated `pushData`, so at a spend cap the run stops cleanly and you are never charged for rows you did not receive. Rows dropped by `activeWithinDays` are filtered **before** delivery and are not billed.

Worked examples: 10 trade keywords × 500 advertisers, US-only and active in the last 30 days ≈ **1,200 rows ≈ $1.20**. A one-off competitor sweep of 50 domains ≈ **$0.30**. A live run measured 2026-09-02 — 2 keywords, US-only, 20+ ads, active in 30 days — delivered **43 advertisers in 30 seconds and billed exactly 43**.

### Notes and honest limits

- **The advertiser's website is not available.** Google does not expose it from an advertiser ID, and this actor does not guess one. Measured: `LookupService/GetAdvertiserById` answers HTTP 200 with a **zero-byte** body; the creatives endpoint carries no destination or display URL; and fetching a rendered creative preview (313 KB) yielded links only to `google`/`gstatic`/`googlesyndication` hosts. The domain relationship works **in reverse only**, which is why `domains` is an input mode rather than an output column. If you need websites, run this actor for the advertiser list and match the names against a company database.
- **No emails, phones or people.** This is a company-level register: business name, country, ad volume, ad dates. Contact data is not in the source and is not fabricated here.
- **Keyword search is name-matching, not semantic.** `nike.com` as a keyword matches *NIKETA'S COMPANY S.R.L.* Use domain mode for domains.
- **Ad counts above ~1,000 are bands, not exact numbers** — Google itself reports them that way (e.g. 9,000–10,000). Both ends ship, plus `adCountExact` so you know which you have.
- **Google rate-limits by IP.** A flagged address gets a reCAPTCHA "unusual traffic" page instead of data — an unproxied IP hit exactly that during development, while every Apify Proxy session returned results. Apify Proxy is therefore ON by default. Blocked requests are **counted and named in the run's status message**; they are never silently reported as "no advertisers found". If you push very hard, supply your own residential proxy.
- **The endpoint returns one undocumented boolean** on ~0.7% of rows (7 of 1,000 sampled). Its meaning could not be established from the data, so it is deliberately **not shipped under a guessed label** rather than being passed off as a "verified" flag.
- **Ad creatives are a different actor.** Nothing here emits an individual ad, preview or format — this is the advertiser directory. Dates are computed from the creative feed in aggregate only.
- **`adstransparency.google.com` serves no `robots.txt`** (HTTP 404, checked 2026-09-02). The data is Google's own public advertiser-transparency disclosure, published without a login. The actor makes one request per keyword and one per advertiser, at low concurrency.
- **Verify anything you act on.** Rows are a snapshot at `scrapedAt`; advertiser identities are as Google records them, and a business may trade under a different name than the one on its ad account.

# Actor input Schema

## `keywords` (type: `array`):

Search terms matched against the ADVERTISER'S NAME, not against what they sell. `plumbing` returns every advertiser whose registered name contains it (KS Plumbing, Plumbing MD, Buzz Plumbing…). Use trade words (plumbing, roofing, dental, law), brand words, or a company-name fragment. One request per keyword, deduplicated by advertiser ID across all of them.

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

Website domains such as `shopify.com`. For each one the actor returns the advertisers whose Google ads point at that site — the reverse lookup, used to put a name to a competitor's ad spend. `https://` and `www.` are stripped for you. Note: this direction only works from domain to advertiser; Google does not expose an advertiser's website the other way round.

## `resultsPerKeyword` (type: `integer`):

How many advertisers to request for each keyword. Google's endpoint caps out at 3000 and returns nothing above it, so that is the enforced maximum. Ranked by Google, so the first results are the closest name matches.

## `countries` (type: `array`):

Keep only advertisers whose verified location is in these countries, e.g. `US`, `GB`, `AU`, `CA`. Leave empty for every country. A 1,000-row `plumbing` sample was 70% US, 13% AU, 7% CA, 5% GB, so this filter matters on generic trade keywords.

## `minAds` (type: `integer`):

Only keep advertisers who have run at least this many ads. The median advertiser on a trade keyword has run 5, so setting 20-50 here is the fastest way to strip out one-off and hobby advertisers. 0 = no minimum.

## `maxAds` (type: `integer`):

Only keep advertisers who have run at most this many ads — use it to exclude national brands and keep the list to small and mid-size businesses. 0 = no maximum.

## `enrichWithAdActivity` (type: `boolean`):

Attach when each advertiser's ads were last and first seen (lastAdSeenAt, daysSinceLastAd, firstAdSeenAt) plus their exact ad count. This is what separates an advertiser still spending this week from one who stopped two years ago. Costs one extra request per advertiser; switch off for a faster, name-only list.

## `activeWithinDays` (type: `integer`):

Drop any advertiser whose most recent ad was last seen more than this many days ago — 30 gives you a currently-spending list. Requires the ad-activity dates, which are switched on automatically when you set this. 0 = no recency filter.

## `maxItems` (type: `integer`):

Stop after this many advertiser rows in total, across every keyword and domain. This is also the billing ceiling, since you are charged per advertiser delivered. 0 = no limit.

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

Leave Apify Proxy ON. Google rate-limits the Ads Transparency endpoint by IP and answers a flagged address with a reCAPTCHA page instead of data — a plain unproxied IP was blocked during development while every Apify proxy session returned results. You can substitute your own residential proxy for very large runs.

## Actor input object example

```json
{
  "keywords": [
    "plumbing"
  ],
  "domains": [],
  "resultsPerKeyword": 25,
  "countries": [],
  "minAds": 0,
  "maxAds": 0,
  "enrichWithAdActivity": true,
  "activeWithinDays": 0,
  "maxItems": 25,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `advertisers` (type: `string`):

The dataset of advertisers found (one item per advertiser, deduplicated by advertiser ID).

# 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 = {
    "keywords": [
        "plumbing"
    ],
    "resultsPerKeyword": 25,
    "maxItems": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/adstransparency-advertiser-leads-scraper").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 = {
    "keywords": ["plumbing"],
    "resultsPerKeyword": 25,
    "maxItems": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/adstransparency-advertiser-leads-scraper").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 '{
  "keywords": [
    "plumbing"
  ],
  "resultsPerKeyword": 25,
  "maxItems": 25
}' |
apify call scrapersdelight/adstransparency-advertiser-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/adstransparency-advertiser-leads-scraper"
        }
    }
}
```

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/d4KOIePHpwLPjGnOt/builds/vxRIkLrvXJleyICUM/openapi.json
