# Google Ads Scraper (`autofacts/google-ads-scraper`) Actor

Every ad an advertiser runs on Google: Search, YouTube, Shopping, Maps and Play. Search by advertiser, domain or brand; filter by country, platform, format and date. Each ad: first and last shown, days shown, preview image; details add countries, EU impressions and audience targeting.

- **URL**: https://apify.com/autofacts/google-ads-scraper.md
- **Developed by:** [Richard Feng](https://apify.com/autofacts) (community)
- **Categories:** Marketing, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 ads

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 Scraper — Every Ad by Advertiser, Domain or Brand

[![Every ad your competitor runs on Google — Search, YouTube, Shopping, Maps and Play — by domain, advertiser or brand, with run dates, EU impressions and what each ad says.](https://api.apify.com/v2/key-value-stores/5n5rqLmrzilgrZyPW/records/readme-hero-v1.png)](https://console.apify.com/actors/9DESS7DuLH2zaxcIU/input)

Every ad a company runs on Google — Search, YouTube, Shopping, Maps and Play — as Google's own
public ad library lists it. Look ads up by advertiser, by the website they lead to, or by brand
name; narrow them by country, platform, format and date.
Each ad comes back with who paid for it, when it first and last ran, on how many days, and an image
of the ad where Google archives one. Turn on details for every country it ran in and, in the
European Union, how many times it was shown and which kinds of audience it targeted; turn on
content for the words, products and videos in the ad.

### 📖 What it does

- 🔍 **Four ways in**: advertiser IDs, domains, brand names, or page URLs copied from
  your browser — mixed freely in one run.
- 📊 **One row per ad**: advertiser and verified legal name, format, first and last shown, days
  shown, a public preview image and the ad's own page at Google.
- 🌍 **Reach and targeting** (details): every country the ad ran in with its first and last day;
  for EU ads the impression range in total, per country and per platform, and whether the
  advertiser included or excluded demographic, location and contextual criteria.
- 📦 **What the ad says** (content): a text ad's headline and description, a Shopping ad's
  products, the YouTube video a video ad plays, and the landing page where the preview links to
  one.
- 📈 **Built for monitoring**: a date filter such as "30 days" moves with a scheduled run, and an
  ad reached twice in one run for the same country — by its domain and its advertiser — is stored
  and charged once.

### 🚀 Quick start

1. Put a competitor's website in **Domains**, for example `nike.com`.
2. Pick a **Country**, or leave it on *Anywhere*.
3. Run. Each ad Google shows for that website is one row in the dataset.

To look at one advertiser instead, copy its page from adstransparency.google.com into
**Start URLs** — the filters you set on the page come with it.

### 💡 What people use it for

- **Competitor ad monitoring** — every ad a rival runs, with the day it started and stopped, on
  a schedule, so new campaigns show up in the next run.
- **Creative research** — headlines, descriptions, products and videos across a whole market, to
  see which messages advertisers keep running for hundreds of days.
- **Market and media planning** — EU impression ranges per country and platform, to size how
  hard a competitor is pushing where.
- **Agency and brand-safety checks** — which legal entities and agencies pay for ads that lead to
  a website, and whether Google has verified them.
- **Lead generation** — which companies advertise on Google at all, and since when.

### 📋 Input

| Field | What it does |
| :--- | :--- |
| Advertiser IDs | Every ad of these advertisers. The ID is in the advertiser's page URL, e.g. `AR01371945870127267841`; the whole URL works too. |
| Domains | Every ad Google matches to this website, from every advertiser running one. |
| Advertiser names | A company or brand name, looked up like Google's advertiser search box does: the advertiser holding that name as whole words with the most ads — "Nike" finds Nike, Inc., not a one-ad advertiser called Nike. |
| Start URLs | Pages from adstransparency.google.com: an advertiser page, a single ad's page, or a domain search. Filters in the URL override the ones in the form, for that URL. |
| Country | Only ads shown in this country, or *Anywhere*. |
| Platform | Google Search, YouTube, Google Shopping, Google Maps or Google Play. |
| Ad format | Text, image or video. |
| Shown on or after / on or before | A date (`2026-09-01`) or a span back from today (`30 days`, `2 weeks`). |
| Maximum ads | The hard cap on ads stored — and on what the run can charge. Default 100. |
| Maximum ads per search | Stops each advertiser, domain, name or URL after this many, so one big advertiser can't use up the run. |
| Include ad details | Countries, EU impressions and audience selection. Charged as an extra per ad. |
| Include ad content | Headlines, descriptions, products, videos and landing pages. Charged as an extra per ad, only when something is found. |
| Proxy | Apify residential proxies by default. |

Example: every video ad Nike, Inc. ran on YouTube in the US over the last 90 days.

```json
{
    "startUrls": [{ "url": "https://adstransparency.google.com/advertiser/AR16735076323512287233?region=US&platform=YOUTUBE" }],
    "adFormat": "VIDEO",
    "dateFrom": "90 days",
    "includeAdContent": true
}
```

### 📤 Output

One row per ad. A text ad Nike Retail BV ran in the EU, with details and content on (the
countries list is cut to two):

```json
{
    "recordType": "ad",
    "creativeId": "CR04380650295827365889",
    "adUrl": "https://adstransparency.google.com/advertiser/AR18378488041124659201/creative/CR04380650295827365889?region=NL",
    "advertiserId": "AR18378488041124659201",
    "advertiserName": "Nike Retail BV",
    "advertiserLegalName": "Nike Retail BV",
    "advertiserCountry": "NL",
    "advertiserVerified": true,
    "advertiserUrl": "https://adstransparency.google.com/advertiser/AR18378488041124659201?region=NL",
    "format": "TEXT",
    "domain": null,
    "firstShown": "2025-10-22T15:05:47.575Z",
    "lastShown": "2026-09-28T13:07:21.342Z",
    "daysShown": 342,
    "previewImageUrl": "https://tpc.googlesyndication.com/archive/simgad/3104440278062597240",
    "region": "NL",
    "platform": null,
    "queryType": "advertiserId",
    "query": "AR18378488041124659201",
    "details": {
        "topicId": 10021,
        "variationCount": 2,
        "variationImageUrls": ["https://tpc.googlesyndication.com/archive/simgad/3104440278062597240"],
        "regions": [
            {
                "region": "FR",
                "regionName": "France",
                "firstShown": "2025-10-22",
                "lastShown": "2026-09-28",
                "impressions": { "min": 250000, "max": 300000 },
                "impressionsCountedFrom": "2025-10-22",
                "impressionsBySurface": [
                    { "surface": "SHOPPING", "impressions": { "min": 0, "max": 1000 } },
                    { "surface": "SEARCH", "impressions": { "min": 250000, "max": 300000 } },
                    { "surface": "MAPS", "impressions": { "min": 0, "max": 1000 } }
                ]
            },
            {
                "region": "MQ",
                "regionName": "Martinique",
                "firstShown": "2025-10-23",
                "lastShown": "2026-09-27",
                "impressions": { "min": 0, "max": 1000 },
                "impressionsCountedFrom": "2025-10-23",
                "impressionsBySurface": [{ "surface": "SEARCH", "impressions": { "min": 0, "max": 1000 } }]
            }
        ],
        "euImpressions": { "min": 250000, "max": 300000 },
        "euImpressionsCountedFrom": "2025-10-22",
        "euImpressionsBySurface": [
            { "surface": "MAPS", "impressions": { "min": 0, "max": 1000 } },
            { "surface": "SEARCH", "impressions": { "min": 250000, "max": 300000 } },
            { "surface": "SHOPPING", "impressions": { "min": 0, "max": 1000 } }
        ],
        "audience": {
            "demographicInfo": { "included": true, "excluded": false },
            "geographicLocations": { "included": true, "excluded": false },
            "contextualSignals": { "included": true, "excluded": false }
        }
    },
    "content": {
        "headline": "{KeyWord:Nike Vomero}",
        "description": "Découvre {Keyword:chaussures Nike} sur Nike.com. Commande sur le site officiel Nike.",
        "products": [],
        "landingUrl": null,
        "youtubeVideoId": null,
        "youtubeUrl": null
    },
    "scrapedAt": "2026-09-28T13:34:01.034Z"
}
```

A video ad's content, from Nike, Inc.:

```json
{
    "headline": null,
    "description": null,
    "products": [],
    "landingUrl": "https://www.nike.com/retail/",
    "youtubeVideoId": "RZ1MLoOdWcc",
    "youtubeUrl": "https://www.youtube.com/watch?v=RZ1MLoOdWcc"
}
```

The dataset has three views: **Ads** (who, what, when), **Reach and targeting** (details) and
**Ad content**.

### 🗂️ Data fields

| Field | Meaning |
| :--- | :--- |
| `recordType` | What the row is: `ad`. |
| `creativeId`, `adUrl` | The ad's ID and its public page at Google, for the country searched. |
| `advertiserId`, `advertiserName`, `advertiserUrl` | Who ran the ad, and their public page at Google. |
| `advertiserLegalName`, `advertiserCountry`, `advertiserVerified` | The legal name Google verified (only for verified advertisers — Google shows none otherwise), where the advertiser is based, and whether Google has verified its identity. |
| `format` | `TEXT`, `IMAGE` or `VIDEO`. |
| `domain` | On a domain search — or a name search that fell back to a domain — the domain searched for. Advertiser and single-ad searches have none. |
| `firstShown`, `lastShown`, `daysShown` | When the ad first and last ran in the country searched, and on how many days. |
| `previewImageUrl` | A public image of the ad, where Google archives one. In testing, nearly every video and image ad and about one text ad in seven had none: Google renders those live, and their content is in `content`. |
| `region`, `platform` | The country and platform filter the ad was found under. |
| `queryType`, `query` | How the ad was found, and the input that found it. |
| `details` | With details on: `regions` (each country with first and last day, and EU impressions), `euImpressions` and `euImpressionsBySurface`, `audience`, `topicId` (Google's topic label, e.g. 10021 for Apparel), `variationCount` and `variationImageUrls`. |
| `content` | With content on: `headline`, `description`, `products` (title, merchant, image), `landingUrl`, `youtubeVideoId` and `youtubeUrl`. |
| `scrapedAt` | When the row was read. |

### 💳 Pricing

Pay per event: one price per ad stored, and a separate per-ad price for each extra you turn on —
**details** is charged on every ad it was read for, **content** only on ads where something was
found. Filters cost nothing: an ad that doesn't match is never stored or charged, and an ad found
twice in one run for the same country is charged once. If Apify restarts a run, to move it to
another server or because you resurrected it, the ads it had stored are not stored or charged
again.

| Event | Free | Starter | Scale | Business and up |
| :--- | ---: | ---: | ---: | ---: |
| Ad stored, per 1,000 | $1.50 | $1.30 | $1.20 | $1.00 |
| Ad details (extra), per 1,000 ads | $1.00 | $1.00 | $1.00 | $1.00 |
| Ad content (extra), per 1,000 ads where something was found | $2.00 | $2.00 | $2.00 | $2.00 |
| Actor start, per run | $0.00005 | $0.00005 | $0.00005 | $0.00005 |

1,000 ads cost $1.50 on the Free plan, or $2.50 with details on. Apify's free plan includes $5 of
platform credit every month — about 3,300 ads, or 2,000 with details. The Pricing tab always shows
the current prices.

**Maximum ads** is the hard cap on a run's charge; Apify's own spending limit is respected too —
the run stops before an ad whose charges would pass it.

### ⚠️ Limitations

- **Impressions and audience are EU-only.** Google publishes them under the Digital Services Act,
  for ads shown in the European Union, and counts impressions with a delay of about three months.
  Elsewhere the details give the countries an ad ran in and the last day it ran there.
- **Many text ads are pictures.** Google archives a lot of text ads as an image of the ad; those
  have a `previewImageUrl` and no headline. Headlines and descriptions come from text ads Google
  renders live. Location ads and local-store video ads carry no text in this version.
- **Landing pages only where the preview links to one.** In testing that was video ads (a page
  or an app-store listing) and display product ads (the product page); Shopping-card and text
  ads link nowhere in their previews, so they have none.
- **Headlines are kept as Google stores them.** An ad using dynamic keyword insertion shows its
  placeholder, e.g. `{KeyWord:Nike Vomero}` — the text in it is what shows when the search term
  doesn't replace it.
- **YouTube videos are often unlisted.** A video ad usually plays a cut uploaded just for the ad;
  it has a YouTube page, but may not appear on the advertiser's channel.
- **No prices.** Shopping ad previews show "\[Price]" in place of the price, so the data has none.

### ❓ FAQ

**Do I need a Google account or an API key?** No. The data is what anyone sees on Google's public
ad pages, without signing in.

**How many ads does an advertiser have?** The log prints Google's own count for each
search, like "about 4,000–5,000 ads". Set **Maximum ads** to cover it.

**Why does a brand name give me only one advertiser?** A brand often advertises through several
legal entities — Nike through Nike, Inc., Nike Retail BV and others. A name finds the main one;
to get all of them, search the brand's **domain**, which returns every advertiser whose ads lead
there.

**Where does the data come from, and what may I do with it?** From Google's public Ads
Transparency Center. You are responsible for how you use it, including under Google's terms for
that site.

### 🤖 Use with AI agents

This Actor is callable as a tool by any MCP-capable agent — Claude, Cursor, VS Code — or by your
own code, with no wrapper and nothing extra to deploy.

**Connect over MCP**

```
https://mcp.apify.com?tools=autofacts/google-ads-scraper
```

In a client that reads an `mcpServers` configuration block:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=autofacts/google-ads-scraper",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

The agent reads this Actor's parameters and their descriptions straight from the input
schema, and the hosted server infers the result field types from the dataset schema — so a
model knows what to send and what comes back before it ever calls anything.

**Or call the API directly**

```bash
curl -X POST "https://api.apify.com/v2/acts/autofacts~google-ads-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"domains": ["nike.com"], "proxy": {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}}'
```

The response body is the dataset rows described above.

### 🧰 Other Actors by autofacts

Apify only auto-recommends Actors in the same category, so here are the ones that actually pair with this scraper:

| Actor | What it's for |
| :--- | :--- |
| [Shopify Store Leads](https://apify.com/autofacts/shopify-store-leads) | Catalog size, apps and contacts of the stores behind the ads |
| [Shopify Scraper](https://apify.com/autofacts/shopify) | The products an advertiser's Shopify store sells, with prices |
| [WooCommerce Scraper](https://apify.com/autofacts/woocommerce-scraper) | The same for WooCommerce stores |
| [Schema Markup Scraper & SEO Auditor](https://apify.com/autofacts/metadata-scraper) | What a landing page says about itself to search engines |
| [YouTube Subtitle & Transcript Scraper](https://apify.com/autofacts/youtube-subtitle-transcript-scraper) | The spoken words of a video ad, where YouTube has captions |

All of them: [apify.com/autofacts](https://apify.com/autofacts)

# Changelog

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

# Actor input Schema

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

Every ad of these advertisers. The ID is the part of an advertiser's page URL after /advertiser/, such as AR01371945870127267841; the whole page URL works too.

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

Every ad that leads to this website, from every advertiser running one, as Google's own domain search finds them. A bare domain such as nike.com, or any URL on the site.

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

A company or brand name, looked up the way Google's advertiser search box does it. Of the advertisers it suggests, the one whose name contains yours as whole words and has the most ads is searched: "Nike" finds Nike, Inc., not a one-ad advertiser called Nike. Every ad names the advertiser it came from, so the match can be checked; to cover every advertiser of a brand, search its domain.

## `startUrls` (type: `array`):

Pages copied from adstransparency.google.com: an advertiser's page, a single ad's page, or a domain search. Filters named in a URL (region, platform, format, start-date, end-date) apply to that URL in place of the ones below; the ones below apply to the rest.

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

Only ads shown in this country. Google's figures are per country: for a country in the European Union, the ad details include impression ranges and the advertiser's audience selection.

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

Only ads shown on this Google platform.

## `adFormat` (type: `string`):

Only ads of this format.

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

Only ads shown on or after this day. A span such as "30 days" counts back from the day the run starts, so a scheduled run keeps looking at the latest days.

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

Only ads shown on or before this day. Leave empty for today.

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

Stop after storing this many ads, across all searches. This is the hard cap on what the run can charge.

## `maxAdsPerSearch` (type: `integer`):

Stop each advertiser, domain, name or URL after this many ads, so one big advertiser cannot use up the whole run. Leave empty for no limit per search.

## `includeDetails` (type: `boolean`):

Every country the ad ran in, with the first and last day it ran there, and the topic Google labels it with. For ads shown in the European Union also the impression ranges, in total, by country and by platform, and which kinds of audience criteria the advertiser included or excluded. One extra lookup per ad, charged as an extra per ad.

## `includeAdContent` (type: `boolean`):

What the ad says and shows, for ads Google renders live rather than as a picture: a text ad's headline and description, a Shopping ad's products (name, merchant, image), the YouTube video a video ad plays, and the landing page where the preview links to one (video ads and display product ads). One extra lookup per such ad, charged only when something is found.

## `proxy` (type: `object`):

Used for every request the actor makes. Apify residential proxies by default: Google limits how many requests one address may send, and in testing on 2026-09-28 it refused between 7% and 83% of requests from shared datacenter addresses and 6 in 1,217 (0.5%) from residential ones. A refused request is sent again from another address either way.

## Actor input object example

```json
{
  "domains": [
    "nike.com"
  ],
  "region": "anywhere",
  "platform": "ANY",
  "adFormat": "ANY",
  "maxItems": 100,
  "includeDetails": false,
  "includeAdContent": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One row per ad: advertiser, format, domain, first and last shown, preview image, and the extras asked for.

# 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"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("autofacts/google-ads-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 = {
    "domains": ["nike.com"],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("autofacts/google-ads-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 '{
  "domains": [
    "nike.com"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call autofacts/google-ads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,autofacts/google-ads-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/9DESS7DuLH2zaxcIU/builds/7fcADXEgNKNceXytV/openapi.json
