# Google Ads Scraper - Competitor Ads (`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:** [Proooxy](https://apify.com/autofacts) (community)
- **Categories:** Marketing, Lead generation, MCP servers
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.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 — Competitor Ads 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. An independent tool, not affiliated with
Google.

### 📖 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; for ads Google renders live, the
  ad account and ad group they run from (for more of them with details or content on).
- ✅ **Or one row per search**: with **Advertiser check**, whether an advertiser, domain or brand
  runs Google ads under your filters, about how many, when it last ran one and who pays for them —
  the advertiser, with the legal name Google verified where it has one, or up to ten advertisers
  behind a domain — without collecting the ads.
- 🌍 **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, description, display URL and the
  default text of its keyword insertions, a Shopping ad's products, the YouTube video a video ad
  plays, and the landing page where the preview links to one. **Only ads with text** keeps the ads
  whose text the run reads.
- 📈 **Built for monitoring**: with a memory and **Only new ads**, a scheduled run delivers only the
  ads you have not had yet, and you pay for the pages it reads to find them, not for the same ads again. A date
  filter such as "30 days" moves with the schedule, 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.

To check a list of advertisers, domains or brands instead — does each advertise, how many ads,
who pays — set **What to scrape** to *Advertiser check*: one row per search.

### 💡 What people use it for

- **Competitor ad monitoring** — every ad a rival runs, with the day it started and stopped, on
  a schedule; with **Only new ads**, each run delivers just the ads that appeared since the last.
- **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; **Advertiser
  check** answers whether they do for a whole list, one row each.

### 📋 Input

| Field | What it does |
| :--- | :--- |
| What to scrape | *Ads* (the default): every ad each search finds, one row per ad. *Advertiser check*: one row per search, saying whether it advertises; see **Advertiser check** below. Settings that belong to the other mode are ignored, and the log names each one set. |
| 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: of the advertisers holding that name as whole words, the one with the most ads — "Nike" finds Nike Retail BV (about 90,000 ads), not a one-ad advertiser called Nike. With a country, platform, format or date set, the top few are ranked again by their ads under those filters ("Nike" in the US finds Nike, Inc.), and one with none there is passed over; with **Advertiser check**, those ranked first are checked whether they have ads there or not. |
| Maximum advertisers per name | How many of those advertisers to search, 1 to 10. Default 1; "Nike" at 2 also searches Nike, Inc. Each advertiser gets its own **Maximum ads per search**; without one, the first can use up **Maximum results** and the rest are never searched. With **Advertiser check**, each is checked, one row each. |
| 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 results | The hard cap on rows stored — ads, or with **Advertiser check** searches checked — and so on what they can charge. Default 100. With **Only new ads**, the pages read are charged besides. |
| Maximum ads per search | Stops each advertiser, domain or URL after this many (for a name, each of its advertisers), so one big advertiser can't use up the run. |
| Memory name | Remembers which ads this task delivered, in a key-value store of that name in your Apify storage. One name per scheduled task, such as `nike-us-daily`: lower-case letters, digits and hyphens, and a name no store in your account has yet, since the Actor creates the store and can use only the stores it created. |
| Only new ads | Delivers only ads no earlier run with this memory delivered; see **Scheduled monitoring** below. Each page of up to 100 ads read is charged; ads you already had are not. |
| Remember ads for | Days an ad stays remembered after the last run that saw it: at least 14, and at least the time between two runs. The default 30 covers any schedule with up to 30 days between runs; for a monthly one, use 31. |
| Start from now | With **Only new ads**: monitoring of each search begins the day it is first run, which reads and delivers nothing; later runs deliver only ads first shown after that day. |
| Include ad details | Countries, EU impressions and audience selection. Included in the price of an ad. |
| Include ad content | Headlines, descriptions, products, videos and landing pages. Charged as an extra per ad, only when something is found. An ad listed as a picture is first looked up for a variation Google renders live, at no charge. |
| Only ads with text | Keeps only ads whose headline or description the run reads; leaves out video and Shopping ads, ads Google keeps only as a picture, and ads whose text could not be loaded (counted apart in the run summary, since running again may read them). Needs ad content on. Ads left out cost nothing. An ad you give by its own page URL is always kept. With **Only new ads**, an ad found without text is not read again by later runs. A search reads about 20 ads for every ad it may store, then moves on; after about 20 times **Maximum results** read over all searches, no further search starts. In testing it read about 2.6 ads a second, so with a large **Maximum results** and few ads with text, give the run a longer timeout. |
| 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
}
```

#### Scheduled monitoring

Save your input as a task, give it a **Memory name** and turn on **Only new ads**, then schedule
the task, daily for example. Each run delivers only the ads no earlier run of that memory
delivered; the ads it passes over are neither stored nor charged. Instead, each page of up to 100
ads the run reads to find the new ones is charged (see **Pricing** below).

- **The first run** of a search delivers every ad it finds, like a run without a memory, and its
  pages are charged. With **Start from now**, monitoring of each search begins the day it is first
  run: that day's runs read and deliver nothing, and later runs deliver only ads first shown after
  it — never one first shown before, even if it runs again later, nor one whose first day Google
  does not give. Without it, later runs also deliver older ads that come back into the days read:
  in testing, nearly nine in ten ads delivered after the first run had first been shown more than a
  month before.
- **Later runs are cheaper.** Once a search has been read to its end, the next runs read only the
  ads shown from two days before that read reached, and the whole search again about once a week,
  for ads Google lists some days after they were first shown; a search asking for fewer than 14
  days is always read whole. An ad that runs every day is shown every day, so for a big advertiser
  most of its running ads are read each time, and the weekly full read reads every ad in the dates
  asked for: a span such as "30 days" keeps it small. The run summary says how each search was read.
- **Read each search to its end once.** Until a search has been read to its end, each run reads it
  from the start again, so set the run's maximum cost to cover it: about $0.002 for every 100 ads
  Google counts for the search. The log warns when the maximum cost cut a search short.
- **One memory per task.** Every search in a task shares its memory: an ad delivered for a domain
  is not delivered again for its advertiser. Two runs of one memory at the same time can deliver an
  ad twice.
- **Forgetting.** An ad not seen for **Remember ads for** days (30 by default, at least 14) is
  forgotten, and delivered again if it comes back. Keep it at least as long as the time between runs.
- **A full memory.** A memory holds about 250,000 ads. Once it is full, a run delivers no new ad it
  cannot keep, since it would deliver, and charge, that ad again on every run; the log and the run
  summary say so. Give some of the searches a memory of their own.
- **An ad asked for by its own page URL** is also delivered only once; **Start from now** does not
  apply to it.

A **Memory name** without **Only new ads** remembers what each run delivers while still delivering
every ad, so that turning **Only new ads** on later starts from there.

#### Advertiser check

Set **What to scrape** to *Advertiser check* to find out, for each advertiser, domain or name,
whether it runs Google ads, without collecting them. Each search is one row. Its first page of up
to 100 ads is read — once more when it comes back empty — and the advertiser's own page, so a
check takes about two requests; a domain's up to eleven, one per advertiser listed; and a name's one
more to look it up, and with a filter set up to three more to rank its advertisers under it.

- **What a row says**: whether it `advertises` under your filters (country, platform, format,
  dates); Google's count of its ads (`adCountMin` to `adCountMax`); its latest ad (`lastShown`,
  `latestAdUrl`); and who pays — for an advertiser or a name, its name, verified legal name, country
  and whether Google verified it; for a domain, up to ten advertisers whose ads lead to it
  (`advertisers`), and whether that is all of them (`advertisersComplete`).
- **Names**: a name is looked up as Google's search box does and checked as the advertisers holding
  it as whole words: the one with the most ads, or with **Maximum advertisers per name** several, one
  row each, whether they advertise or not. A name no advertiser holds is not answered for by another
  advertiser or a domain: it gets a row with `status` `noMatch`, and the log names what Google
  suggested instead, an advertiser's ID or a domain you can check. So does an advertiser ID Google
  has no record of, with no ads.
- **A check that fails costs nothing**: a search Google would not answer, an advertiser whose
  identity could not be read, or a page Google counts ads for but lists none of stores no row, and
  the run summary names it, with its error. Running again later usually answers it.
- **Not used here**: ad details and content, **Only ads with text**, **Maximum ads per search**, the
  memory settings, and ad page URLs. The log names each one set.

Example: do these three advertise on YouTube in the US?

```json
{
    "scrapeMode": "advertiserCheck",
    "domains": ["allbirds.com", "gymshark.com"],
    "advertiserNames": ["Oatly"],
    "region": "US",
    "platform": "YOUTUBE"
}
```

### 📤 Output

One row per ad. A text ad Nike Retail BV ran in the EU, as a run on 2026-09-29 stored it with
details and content on (its list of 11 countries is cut to two):

```json
{
    "recordType": "ad",
    "creativeId": "CR01462880687244705793",
    "adUrl": "https://adstransparency.google.com/advertiser/AR18378488041124659201/creative/CR01462880687244705793?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",
    "adAccountId": "3259543060",
    "adGroupId": "184891867817",
    "format": "TEXT",
    "domain": null,
    "firstShown": "2025-10-22T15:08:44.618Z",
    "lastShown": "2026-09-29T09:02:15.269Z",
    "daysShown": 343,
    "previewImageUrl": "https://tpc.googlesyndication.com/archive/simgad/7736038186578344450",
    "region": "NL",
    "platform": null,
    "queryType": "advertiserId",
    "query": "AR18378488041124659201",
    "details": {
        "topicId": 10021,
        "variationCount": 2,
        "variationImageUrls": ["https://tpc.googlesyndication.com/archive/simgad/7736038186578344450"],
        "regions": [
            {
                "region": "PT",
                "regionName": "Portugal",
                "firstShown": "2025-10-23",
                "lastShown": "2026-09-28",
                "impressions": { "min": 0, "max": 1000 },
                "impressionsCountedFrom": "2025-10-23",
                "impressionsBySurface": [
                    { "surface": "SEARCH", "impressions": { "min": 0, "max": 1000 } },
                    { "surface": "MAPS", "impressions": { "min": 0, "max": 1000 } }
                ]
            },
            {
                "region": "IT",
                "regionName": "Italy",
                "firstShown": "2025-10-22",
                "lastShown": "2026-09-28",
                "impressions": { "min": 0, "max": 1000 },
                "impressionsCountedFrom": "2025-10-22",
                "impressionsBySurface": [
                    { "surface": "SEARCH", "impressions": { "min": 0, "max": 1000 } },
                    { "surface": "MAPS", "impressions": { "min": 0, "max": 1000 } }
                ]
            }
        ],
        "euImpressions": { "min": 600000, "max": 700000 },
        "euImpressionsCountedFrom": "2025-10-22",
        "euImpressionsBySurface": [
            { "surface": "MAPS", "impressions": { "min": 0, "max": 1000 } },
            { "surface": "SEARCH", "impressions": { "min": 600000, "max": 700000 } },
            { "surface": "SHOPPING", "impressions": { "min": 0, "max": 1000 } }
        ],
        "audience": {
            "demographicInfo": { "included": true, "excluded": false },
            "geographicLocations": { "included": true, "excluded": false },
            "contextualSignals": { "included": true, "excluded": false }
        }
    },
    "content": {
        "textStatus": "live",
        "headline": "{KeyWord:Nike Vomero}",
        "description": "Descubre {Keyword:zapatillas Nike} en Nike.com y compra en la página oficial de Nike.",
        "displayUrl": "nike.com",
        "keywordInsertions": ["Nike Vomero", "zapatillas Nike"],
        "products": [],
        "landingUrl": null,
        "youtubeVideoId": null,
        "youtubeUrl": null
    },
    "scrapedAt": "2026-09-29T09:33:23.709Z"
}
```

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

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

With **Advertiser check**, one row per search. An advertiser checked in the US on 2026-09-29:

```json
{
    "recordType": "advertiserCheck",
    "status": "checked",
    "queryType": "advertiserId",
    "query": "AR01371945870127267841",
    "advertiserId": "AR01371945870127267841",
    "domain": null,
    "region": "US",
    "platform": null,
    "adFormat": null,
    "dateFrom": null,
    "dateTo": null,
    "advertises": true,
    "adCountMin": 800,
    "adCountMax": 900,
    "lastShown": "2026-09-29T15:37:22.364Z",
    "latestAdUrl": "https://adstransparency.google.com/advertiser/AR01371945870127267841/creative/CR17750316037196218369?region=US",
    "advertiserName": "Allbirds Inc",
    "advertiserLegalName": "Allbirds Inc",
    "advertiserCountry": "US",
    "advertiserVerified": true,
    "advertiserUrl": "https://adstransparency.google.com/advertiser/AR01371945870127267841?region=US",
    "advertisers": null,
    "advertisersComplete": null,
    "searchUrl": "https://adstransparency.google.com/advertiser/AR01371945870127267841?region=US",
    "scrapedAt": "2026-09-29T16:45:41.607Z"
}
```

A domain's check has the domain instead of the advertiser's fields, and lists the advertisers
behind it in `advertisers`, each with the same fields.

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

Each run that finishes its searches also writes a **SUMMARY** record to its key-value store,
linked from the run's output: how each search went, which tells an empty answer from a failure.
See *Run summary* under *Use with AI agents*.

### 🗂️ Data fields

| Field | Meaning |
| :--- | :--- |
| `recordType` | What the row is: `ad`, or with **Advertiser check** `advertiserCheck`. |
| `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. |
| `adAccountId`, `adGroupId` | The ad account the ad runs from and its ad group, as Google identifies them. One advertiser can run several accounts; ads sharing an ad group were set up together. Read from the ad's rendered version, so none for ads Google keeps only as a picture. Without details or content, only the ad's search listing is read: an ad listed as a picture has none even when a variation of it is rendered. `adGroupId` is also none where the rendering names no ad group, as on app-install video ads. |
| `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: `textStatus` (`live` read from the rendered ad, `none` no headline or description to read, `imageOnly` kept only as a picture, `failed` the ad's rendering could not be loaded: the ad is stored, its content is not charged, and running again may load it), `headline`, `description`, `displayUrl`, `keywordInsertions` (the default text of each `{KeyWord:…}` placeholder), `products` (title, merchant, image), `landingUrl`, `youtubeVideoId` and `youtubeUrl`. |
| `scrapedAt` | When the row was read. |

With **Advertiser check**, a row has none of an ad's own fields, and these instead:

| Field | Meaning |
| :--- | :--- |
| `status` | `checked`: Google answered, yes or no. `noMatch`: Google knows no advertiser by the name, or, with no ads either, by the ID. |
| `queryType`, `query` | What was checked: an advertiser ID, a domain or a name, as in the input. |
| `region`, `platform`, `adFormat`, `dateFrom`, `dateTo` | The filters it was checked under: `region` is `anywhere` for every country, the rest null where none is set, and a date range with one end set has the other filled in (the archive's start, or today). |
| `advertises` | Whether it has any ads under them. |
| `adCountMin`, `adCountMax` | Google's count of its ads, a range of round numbers, exact when both are the same. Where Google gives none, the ads on its first page: exact when that is the only page, else at least that many, with no upper end. |
| `lastShown`, `latestAdUrl` | When its latest ad was last shown, and that ad's page at Google. |
| `advertiserId`, `advertiserName`, `advertiserLegalName`, `advertiserCountry`, `advertiserVerified`, `advertiserUrl` | For an advertiser or a name: who it is. Null on a domain's check. |
| `domain`, `advertisers`, `advertisersComplete` | For a domain: the domain, up to ten advertisers whose ads lead to it, each with the fields above, and whether that is all of them: its ads fit on one page and come from ten advertisers at most. |
| `searchUrl` | The search's page at Google, filters included. |
| `scrapedAt` | When the row was read. |

### 💳 Pricing

Pay per event: one price per ad stored, details included, and a separate per-ad price for
**content**, charged only on ads where something was found. Filters cost nothing: an ad that
doesn't match is never stored or charged, nor is one that **Only ads with text** leaves out, 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, nor are the pages it had read.

With **Only new ads**, the ads you already had cost nothing, and each page of up to 100 ads the
run reads to find the new ones is charged, once its ads are handled; a page the run's time limit
cut is not. A run reads about one page for every 100 ads Google counts as shown in the days it
reads, which for a big advertiser is far more than it runs on one day. In testing (2026-09-30 to
10-01, two days read each run), a small advertiser read 1–5 pages a run ($0.002–0.01), adidas.com
in the US 20 pages ($0.04) and zalando.de in Germany 74–80 pages ($0.15–0.16), plus the new ads;
about once a week the run reads every ad in the dates asked for instead — with no dates, every ad
the advertiser ever ran — so a span such as "30 days" keeps that read small. The first day of
**Start from now** reads nothing and costs nothing.

With **Advertiser check**, each row — a search checked, a name's advertisers one each — is charged
as an advertiser check. A search that cannot be checked stores no row and costs nothing, and
nothing else is charged but the Actor start.

| Event | Free | Starter | Scale | Business and up |
| :--- | ---: | ---: | ---: | ---: |
| Ad stored, details included, per 1,000 | $1.50 | $1.30 | $1.20 | $1.00 |
| Ad content (extra), per 1,000 ads where something was found | $1.00 | $1.00 | $1.00 | $1.00 |
| Page read with **Only new ads**, up to 100 ads | $0.002 | $0.002 | $0.002 | $0.002 |
| Advertiser check, per 1,000 rows | $3.00 | $3.00 | $3.00 | $3.00 |
| Actor start, per run | $0.00005 | $0.00005 | $0.00005 | $0.00005 |

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

Without **Only new ads**, **Maximum results** is the hard cap on a run's charge; with it, the pages
read are charged besides. Apify's own spending limit is respected either way: the run stops before
an ad or a page whose charge 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`, no headline, and `content.textStatus` `imageOnly`. Headlines and
  descriptions come from text ads Google renders live. How many varies by advertiser: in testing on
  2026-09-29, adidas.com's text ads were all live and nearly all of zalando.de's were pictures.
  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, and `content.keywordInsertions` lists those words. A dynamic search ad, which
  Google writes from the landing page, has `<dynamically generated based on landing page content>`
  as its headline and display URL; its description is the advertiser's.
- **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.
- **A check reads a search's first page.** Its `lastShown` is the latest of up to 100 ads, and a
  domain's `advertisers` are those on that page, ten at most; `advertisersComplete` says whether that
  is all of them.

### ❓ 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", and the SUMMARY record keeps it. Set **Maximum results** to
cover it.

**How do I check whether a list of companies advertises on Google?** Set **What to scrape** to
*Advertiser check* and put them under **Domains** or **Advertiser names**, with a country if you
like: one row each, saying whether it advertises, about how many ads, when it last ran one and who
pays for them.

**Why does a brand name give me only one advertiser?** A brand often advertises through several
legal entities — Nike through Nike Retail BV, Nike, Inc. and others. A name searches the one with
the most ads; raise **Maximum advertisers per name** to search more of them, and set **Maximum ads
per search** so the first does not use up the run. The log lists the biggest ones left out, with
their IDs, which you can also give under **Advertiser IDs**. To get all of them, search the
brand's **domain**, which returns every advertiser whose ads lead there.

**How do I get only the new ads every day?** Save the input as a task with a **Memory name** and
**Only new ads** on, and schedule it; see **Scheduled monitoring** above. Turn on **Start from now**
if you want only the ads first shown after the day of its first run.

**Why did a run with Only new ads charge pages but store no ads?** No ad had appeared since the
last run. Reading the pages is how the run finds out; the `SUMMARY` record counts, for each search,
the ads passed over because an earlier run delivered them (`alreadyDelivered`) and, with **Start
from now**, because they were first shown before monitoring began (`startedBefore`).

**Can two tasks share a memory?** Give each task its own. Tasks sharing one memory share what was
delivered: an ad one of them delivered is passed over by the other, even for another search. And
two runs of one memory at the same time can each deliver the same new ad.

**Where does the data come from, and what may I do with it?** From Google's public Ads
Transparency Center. This Actor is an independent tool, not affiliated with Google. You are
responsible for how you use the data, 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.

**Pay per call, without an Apify account**

An agent can also run this Actor and pay for each run itself, with no Apify account, through
Apify's agentic payments: [x402](https://docs.apify.com/platform/integrations/x402) and
[Skyfire](https://docs.apify.com/platform/integrations/skyfire). Apify lists it among the Actors
agents can pay for.

**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, and nothing else: to tell an empty
answer from a failure, start the run through `/runs` instead and read its `SUMMARY` record.

#### Run summary — `SUMMARY`

Every run that finishes its searches writes a `SUMMARY` record to its default key-value store,
also when a search fails. A run that nears its time limit stops itself a little before it and
writes the record too, with `stoppedForTime`. A run stopped by its input writes none, nor does one
that is aborted or being restarted by Apify.

```
GET https://api.apify.com/v2/key-value-stores/{defaultKeyValueStoreId}/records/SUMMARY?token=$APIFY_TOKEN
```

A run with an advertiser, a domain and one ad's page URL, 11 ads at most and 5 per search:

```json
{
  "status": "SUCCESS",
  "note": "No search failed. A search that stored no ads found none that match its filters, only ads the run had already (`repeats`), or, with \"Only ads with text\", only ads without text (`leftOutWithoutText`). `stopped` and `notReached` mean Maximum results, the charge limit, the run's time limit (`stoppedForTime`) or the read limit of \"Only ads with text\" ended the run first; `readLimit` means \"Only ads with text\" read as many ads as it may for that search.",
  "mode": "ads",
  "adsStored": 11,
  "checksStored": 0,
  "pagesCharged": 0,
  "searches": [
    {
      "search": "advertiser AR18378488041124659201 (anywhere)",
      "searched": "advertiser AR18378488041124659201 (anywhere)",
      "googleAdsFrom": 80000,
      "googleAdsTo": 90000,
      "stored": 5,
      "repeats": 0,
      "alreadyDelivered": 0,
      "leftOutWithoutText": 0,
      "leftOutNotLoaded": 0,
      "pagesRead": 1,
      "scan": null,
      "startedBefore": 0,
      "complete": null,
      "incompleteBecause": null,
      "outcome": "finished",
      "error": null
    },
    {
      "search": "domain nike.com (anywhere)",
      "searched": "domain nike.com (anywhere)",
      "googleAdsFrom": 100000,
      "googleAdsTo": 200000,
      "stored": 5,
      "repeats": 5,
      "alreadyDelivered": 0,
      "leftOutWithoutText": 0,
      "leftOutNotLoaded": 0,
      "pagesRead": 3,
      "scan": null,
      "startedBefore": 0,
      "complete": null,
      "incompleteBecause": null,
      "outcome": "finished",
      "error": null
    },
    {
      "search": "ad CR04380650295827365889 (NL)",
      "searched": null,
      "googleAdsFrom": null,
      "googleAdsTo": null,
      "stored": 1,
      "repeats": 0,
      "alreadyDelivered": 0,
      "leftOutWithoutText": 0,
      "leftOutNotLoaded": 0,
      "pagesRead": 0,
      "scan": null,
      "startedBefore": 0,
      "complete": null,
      "incompleteBecause": null,
      "outcome": "finished",
      "error": null
    }
  ],
  "withoutDetails": 0,
  "withoutContent": 0,
  "leftOutWithoutText": 0,
  "leftOutNotLoaded": 0,
  "stoppedForTime": false,
  "restarted": false,
  "memory": null,
  "startedAt": "2026-09-29T14:37:43.217Z",
  "finishedAt": "2026-09-29T14:38:03.605Z"
}
```

| `status` | Meaning | What to do |
| :--- | :--- | :--- |
| `SUCCESS` | No search failed, and no ad was left out for text that could not be loaded | Use the dataset; a search with no ads found none that match its filters |
| `PARTIAL_RESULTS` | Some searches could not be finished, or **Only ads with text** left out ads whose text could not be loaded | Use the dataset; run the rest again later |
| `NO_RESULTS` | The same, and nothing was stored | Run again in a few minutes |
| `RUN_FAILED` | The run stopped with an error | The rows already in the dataset are valid |

`note` says the same in words. Next to it: `mode`, `ads` or, with **Advertiser check**,
`advertiserCheck`; `adsStored`, the ads in the dataset; `checksStored`, with **Advertiser check**,
the searches checked, one row each; `pagesCharged`,
with **Only new ads**, the pages charged, before a restart included; `withoutDetails`
and `withoutContent`, stored ads whose details, or whose content, Google did not give;
`leftOutWithoutText` and `leftOutNotLoaded`, with **Only ads with text**, the ads left out for
having no text and for text that could not be loaded; `stoppedForTime`, whether the run stopped
itself near its time limit, when a longer timeout would read more; `restarted`, whether the run picked up after
Apify restarted it; `memory`, with a **Memory name**, its `name`, whether `onlyNewAds` and
`startFromNow` were on, the ads it holds (`remembered`), those this run added (`added`), whether
it was saved in full at the end (`saved`), and whether it was full (`full`), with the new ads not
delivered because it could not keep them (`leftOutFull`), or null;
`startedAt` and `finishedAt`; `datasetId` on the platform; and `error`, only
with `RUN_FAILED`.

Each entry of `searches` names the input (`search`) and what was searched for it (`searched`:
a name leads to one or more advertisers, joined by "; ", or to a domain; none for an ad's own page
URL, nor for a search that was not reached, not found, failed before its first page or finished
before a restart), Google's own count of its ads (`googleAdsFrom` to `googleAdsTo`, added up over a
name's advertisers), the ads it `stored` (with **Advertiser check**, its rows), the ads it met that the run had stored already or an
earlier search had left out (`repeats`), the ads an earlier run of the memory had delivered,
passed over (`alreadyDelivered`), the ads **Only ads with text** left out (`leftOutWithoutText`,
with **Only new ads** also those an earlier run found without text, not read again;
`leftOutNotLoaded`), and the pages of ads it read (`pagesRead`). With a **Memory name**, it also
says how the search was read (`scan`: `full`; `window`, only its latest days, when Google's count
is of those days; `started`, not read, on the day **Start from now** began its monitoring; or
`mixed`, for a name whose advertisers were read differently), the ads **Start from now** passed
over for being first shown before that day (`startedBefore`), and whether it was read to its end
(`complete`), which lets the next run read only the latest days, or why not (`incompleteBecause`);
without one, `scan`, `complete` and `incompleteBecause` are null. Last comes its `outcome`:

| `outcome` | Meaning |
| :--- | :--- |
| `finished` | It went through its ads, or stored **Maximum ads per search**; with **Advertiser check**, its rows are stored. |
| `finishedBeforeRestart` | It had finished before Apify restarted the run. |
| `notFound` | Google knows no advertiser by that name, or no such ad; with **Advertiser check**, its row says `noMatch`. |
| `stopped` | **Maximum results**, the charge limit or the run's time limit ended the run during it. |
| `readLimit` | **Only ads with text** read as many ads as it may for it. |
| `notReached` | The run stopped before it: **Maximum results**, the charge limit, the run's time limit, or the read limit of **Only ads with text**. |
| `failed` | It could not be finished; `error` says why. |

| `incompleteBecause` | Meaning |
| :--- | :--- |
| `maximum` | **Maximum ads per search**, or **Maximum results**, left no room for the rest of its ads. |
| `chargeLimit` | The run's maximum cost ended the run during it. |
| `timeLimit` | The run's time limit ended the run during it. |
| `readLimit` | **Only ads with text** read as many ads as it may for it. |
| `failed` | It could not be finished. |
| `repeating` | Google's pages of it started repeating. |
| `belowGoogleCount` | Its pages listed fewer ads than Google counts for it. |
| `textNotLoaded` | **Only ads with text** left out ads whose text could not be loaded. |

### 🧰 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 |
| [Google Trends API & Scraper](https://apify.com/autofacts/google-trends) | How much people search for an advertiser's brand or product, and the searches rising around it |
| [YouTube Transcript Scraper API](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

## `scrapeMode` (type: `string`):

Ads: every ad each search finds, one row per ad. Advertiser check: one row per search, saying whether it runs Google ads under your filters, about how many, when it last ran one and who pays for them, without collecting the ads themselves. Settings that belong to the other mode are ignored, and the log names anything skipped.

## `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 (more with Maximum advertisers per name); if none does, the first it suggests. "Nike" finds Nike Retail BV (about 90,000 ads) rather than a one-ad advertiser called Nike. Advertisers are ranked by all their ads; with a country, platform, format or date set, the top few are ranked again by their ads under those filters, and one with none there is passed over for the next. Every ad names the advertiser it came from, so the match can be checked; to cover every advertiser of a brand, search its domain.

## `maxAdvertisersPerName` (type: `integer`):

How many of the advertisers holding each name to search, most ads first. "Nike" is held by Nike Retail BV (about 90,000 ads), Nike, Inc. (about 10,000), nine more Nike-named companies of up to 6,000 ads, and a long tail of people and small firms with that name. Each advertiser gets its own Maximum ads per search; without one, the first can use up Maximum results and the rest are never searched. With Advertiser check, each is checked, one row each. The log lists the biggest ones left out, with their IDs.

## `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 rows, across all searches: ads or, with Advertiser check, searches checked. This caps what the run's rows can cost; with Only new ads on, the pages read to find new ads are charged besides, up to the maximum cost you set for the run.

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

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

## `storeName` (type: `string`):

Name a memory to keep which ads this task delivers, so that its next runs can skip them with Only new ads. Use one name per scheduled task, such as "nike-us-daily": lower-case letters, digits and hyphens, and a name no store in your account has yet, since the Actor creates the store and can use only the stores it created. Leave empty for a one-off run.

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

Deliver only ads that no earlier run with this memory delivered. Needs a memory name. Ads skipped are not charged; each page of up to 100 ads the run reads to find the new ones is charged as a watched page. The first run delivers every ad it finds, unless Start from now is on; until a search has been read to its end once, each run reads it from the start, so let the run's maximum cost cover it.

## `seenRetentionDays` (type: `integer`):

How many days an ad stays remembered after the last run that saw it: at least 14, since each search is read in full about once a week, and at least the time between two runs; the default 30 covers any schedule with up to 30 days between runs, and 31 a monthly one. An ad forgotten while it is still listed is delivered, and charged, again; one that stops and comes back after this many days is delivered as new.

## `startFromNow` (type: `boolean`):

Monitoring of each search begins the day it is first run with this on: that day reads nothing and delivers nothing, and later runs deliver only ads first shown after it — never one first shown before, even if it runs again, nor one whose first day Google does not give. Needs Only new ads. A search this memory had already read to its end keeps going as it was.

## `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, included in the price of an 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. An ad listed as a picture is first looked up for a variation Google renders live, at no charge.

## `onlyAdsWithText` (type: `boolean`):

Keep only ads whose headline or description the run reads, and leave out the rest: video and Shopping ads, ads Google keeps only as a picture, and ads whose text could not be loaded. Ads left out are not stored or charged; the run's SUMMARY record counts them, those whose text could not be loaded apart, since running again may read them. Needs 'Include ad content' on. A search reads about 20 ads for every ad it may store and then moves on, and after about 20 times Maximum results over all searches the run makes no further searches. An ad asked for by its own page URL is always kept. In testing it read about 2.6 ads a second, so with a large Maximum results and few ads with text, give the run a longer timeout.

## `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
{
  "scrapeMode": "ads",
  "domains": [
    "nike.com"
  ],
  "maxAdvertisersPerName": 1,
  "region": "anywhere",
  "platform": "ANY",
  "adFormat": "ANY",
  "maxItems": 100,
  "onlyNewAds": false,
  "seenRetentionDays": 30,
  "startFromNow": false,
  "includeDetails": false,
  "includeAdContent": false,
  "onlyAdsWithText": 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; or, with Advertiser check, one row per search: whether it advertises, how many ads, its latest ad and who pays.

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

How each search went: Google's own count of its ads, how many were stored, and how it ended: finished, stopped by a maximum, not reached, at the read limit of Only ads with text, not found, or failed. Tells an empty answer from a failure.

# 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 = {
    "scrapeMode": "ads",
    "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 = {
    "scrapeMode": "ads",
    "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 '{
  "scrapeMode": "ads",
  "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/SN9q4oJ1gh6Q0kVjK/openapi.json
