# Google Ads Transparency Scraper (`tidyharvest/ads-transparency-scraper`) Actor

Built to beat Google Ads Transparency Center Scraper & API: on the same 17 queries (Oct 2026) it matched 8 of 8 brands to the right company (theirs 3 of 8), reads text-ad copy from Google's pictures, and adds landing pages and EU impressions, at under half the cost. From $1 per 1,000 ads.

- **URL**: https://apify.com/tidyharvest/ads-transparency-scraper.md
- **Developed by:** [TidyHarvest](https://apify.com/tidyharvest) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Ads Transparency Scraper: Ads, Copy, Landing Pages and Reach

Get every ad a company runs on Google (Search, YouTube, Shopping, Maps, Play) from the **Google Ads Transparency
Center**. Search by **brand name, website, advertiser ID or a link copied from the site**. Each ad comes back with its
format, first and last shown dates, days live, every variation, the countries it ran in, and, where Google exposes them,
**the headline, description, call to action, landing page and YouTube video**. EU countries add **impressions per
country and per platform** and the audience criteria the advertiser used.

**Type a brand, get the brand.** "Temu", "Shein" and "Ford" advertise under other legal names (ELEMENTARY INNOVATION,
Roadget, Ford Motor Company). This actor returns the company that actually runs the brand's ads, tells you which account it matched,
and lists the runners-up.

No login or API key. **$1.00 per 1,000 ads**, **$1.80 with full details**. Queries with no ads get a free row that
says so.

### Compared with Google Ads Transparency Center Scraper & API

We built this to beat [Google Ads Transparency Center Scraper & API](https://apify.com/scrapesage/google-ads-transparency-scraper)
(`scrapesage/google-ads-transparency-scraper`), the most-used Transparency Center actor on Apify Store. On
**October 16, 2026** we ran both on the **same 17 queries** (8 brand names, 4 company websites, 4 small businesses, 1 ad
link), 10 ads each, all regions, theirs with `includeDetails` on and one advertiser per name.

| | This actor | Google Ads Transparency Center Scraper & API |
|---|---|---|
| Brand names matched to the right company | **8 of 8** | 3 of 8 (Temu, Shein, Natura, Ford and Mercado Libre returned other companies' ads) |
| Headline / landing page / YouTube id, where Google shows them | **yes** | not returned |
| Text-ad headline and description (Google keeps most text ads only as a picture) | **read from the picture: 103 of 103 text ads in this test** | not returned |
| Copy of each ad image you keep | **yes (option)** | not offered |
| Impressions per country and platform (EU) | **yes** | not returned |
| Advertiser country, legal name, verified | **yes** | not returned |
| Queries with no ads | **a free row saying so** | nothing |
| Run time | **104 s** | 266 s |
| What the test cost, details on | **$0.25** (141 ads) | $0.65 (141 ads) |

**Why the cost differs:** their card says $0.002 per ad plus $0.003 per ad for details (no cheaper tiers), which came
to **$4.62 per 1,000 ads** in our test. This actor charges **$1.00 per 1,000 ads plus $0.80 per 1,000 for details**
($1.80 in all), with discounts on higher Apify plans. With details off on both, it is $1.00 against their $2.00.

**Where it still does more:** an "advertisers" mode that returns one row per advertiser (we put the site's ad count on
every row and answer every company with a row instead), up to 5 advertisers per name (we return the
best match and list the runners-up), several platforms per run, URL import from a file, and an optional raw-data dump.

### How well it works

Measured in October 2026 on **88 real queries**, the way people use it: 32 small-business websites from Google Maps in
8 countries, 30 SaaS and retail sites (US, UK, Germany, France, Brazil, India, Japan, Mexico, Canada, Australia),
20 brand names typed plainly ("Nike", "Temu", "Mercado Libre") and 6 ids and links copied from the site.

- **Every brand name matched the brand's own advertiser: 20 of 20.** Our previous version got 9 of 20 right.
- **Every ad label matches the site** (Text / Image / Video), and **every query gets an answer**: the 18 with no
  ads return a free `no_ads` row.
- **All ads up to your cap** (5,645 of 5,645 asked for), each with the **countries it ran in and
  dates**, and the **advertiser's country, legal name and verification**.
- **YouTube video id on 81% of video ads** (836 of 1,028).
- **Text ads' words, even when Google keeps only a picture of the ad.** With *Read text from ad images* on, every text ad
  in our test came back with its headline (103 of 103), and on 43 ads we checked by eye against the picture the headline
  was right on 42 (98%) and the description on 34 of 35 (97%), in English, German, French, Spanish and Portuguese ads.
  Image ads add every word in the picture (`imageText`).
- **Impressions per country and platform on 29% of ads** (Google publishes them for EU countries).
- About **0.4 s per ad with details** (100 ads in under a minute); turn details off for a plain list in seconds.

### What you can do with it

- **Competitor ad research:** what a rival runs, on which platforms and countries, since when, with the copy and
  landing pages of their video and HTML ads.
- **Does this company advertise on Google?** Put a list of company names or websites in, set *Max ads per query* to 1,
  and every company gets a row: an ad carrying the site's total ad count (`siteAdsMin` / `siteAdsMax`), or a free
  `no_ads` / `not_found` row.
- **Brand protection:** search your own domain to see *other* advertisers whose ads point to your site.
- **Monitoring:** schedule a daily run with *Only new ads* on; you get (and pay for) only ads it hasn't seen before.
- **EU reach research:** impressions per country and per platform, from Google's own EU disclosures.

### Input

| Field | What it does |
|---|---|
| `queries` | Brand names (`Nike`), websites (`nike.com`: every ad pointing to it), advertiser ids (`AR...`), or links copied from adstransparency.google.com (advertiser page, single ad, or domain search; the link's region is used). |
| `region` | Only ads shown in one country; all 243 regions the site offers. Empty = anywhere. |
| `format` | `all`, `text`, `image` or `video` (the site's own labels). |
| `platform` | `all`, `search`, `youtube`, `shopping`, `maps` or `play`. |
| `startDate` / `endDate` | `YYYY-MM-DD`; keeps ads that ran at any point in the window. |
| `includeDetails` | On by default: copy, landing page, video, variations, countries, impressions, audience. Off = a faster plain list. |
| `readAdText` | Read the headline, description and display URL of text ads from Google's picture of the ad, and every word in image ads (`imageText`). Tip: run with 4096 MB memory for about 1 s per ad. |
| `saveImages` | Keep a copy of each ad's picture in the run's storage (`imageCopyUrl`), so it survives Google's link expiring. |
| `keywords` | Keep only ads whose advertiser, domain or copy contains one of these. |
| `onlyNewAds` + `monitorName` | Return and bill only ads not returned before under this monitor name. |
| `maxItemsPerQuery` | Ads per query (default 100). Big brands have tens of thousands. |
| `maxItems` | Exact cap for the whole run, and so for your cost. |
| `includeEmptyResults` | Free `no_ads` / `not_found` / `error` row per query that returned no ads (on by default). |

Example:

```json
{ "queries": ["Temu", "allbirds.com"], "region": "DE", "format": "video", "maxItemsPerQuery": 50 }
```

### Output

One row per ad (a real row from the October 2026 test, trimmed):

```json
{
  "status": "ok",
  "query": "https://adstransparency.google.com/advertiser/AR10072600183532683265/creative/CR14397395383158308865?region=US",
  "creativeId": "CR14397395383158308865",
  "advertiserId": "AR10072600183532683265",
  "advertiserName": "Hubspot, Inc.",
  "advertiserCountry": "US", "advertiserLegalName": "Hubspot, Inc.", "advertiserVerified": true,
  "format": "video",
  "lastShown": "2026-10-17",
  "headline": "Follow up faster. Close more deals.",
  "longHeadline": "Everything your team needs to grow. Get started free with HubSpot Smart CRM.",
  "description": "Get the context to close the deal. HubSpot keeps track of all your contact history.",
  "callToAction": "Sign up",
  "displayUrl": "hubspot.com",
  "destinationUrl": "https://www.hubspot.com/crm/e010a", "landingDomain": "hubspot.com",
  "videoId": "O_kS-H6v-s8", "youtubeUrl": "https://www.youtube.com/watch?v=O_kS-H6v-s8",
  "videoDuration": "0:16", "youtubeChannel": "HubSpot Ads",
  "variationCount": 4,
  "regionsShown": ["CA", "US"],
  "siteAdsMin": 1, "siteAdsMax": 1,
  "copyAvailable": "text", "copySource": "preview",
  "url": "https://adstransparency.google.com/advertiser/AR10072600183532683265/creative/CR14397395383158308865"
}
```

In EU countries `regionStats` also carries impressions, e.g. another HubSpot video ad in France:
`{"region": "FR", "firstShown": "2025-09-11", "lastShown": "2026-10-16", "impressionsMin": 250000, "impressionsMax": 300000, "platforms": [{"platform": "youtube", "impressionsMin": 250000, "impressionsMax": 300000}]}`.
A query with no ads adds one free row: `{"query": "theplantguys.com", "status": "no_ads", "message": "..."}`.
The run's `SUMMARY` record lists, per query, how it was read, the matched advertiser and runners-up, and the site's own
ad count (`siteAdsMin` / `siteAdsMax`).

### Pricing

Pay per event, discounted on higher Apify plans:
| Event | Free plan price |
|---|---|
| Ad returned | $1.00 per 1,000 |
| Ad details (copy, landing page, countries, impressions, variations, audience) | $0.80 per 1,000 |
| Text read from an ad picture (only ads where words were read) | $1.00 per 1,000 |
| Ad image saved | $2.00 per 1,000 |
Rows for queries with no ads are free. `maxItems` caps the ads, and so the cost, exactly.

### Notes and limits

- **Where the copy comes from.** Video and HTML ads come with headline, description, landing page and video id where
  Google shows them. Google keeps most text and image ads only as pictures (`imageUrl` / `imageUrls`): turn on
  *Read text from ad images* to get their words (`copySource: "image"`). A few archives are blank layouts with
  placeholders; those say `copyAvailable: "template only"`. Local ads also list their store `locations`, and
  `keywordInsertion: true` marks headlines that change with the searcher's keyword (the default text is shown).
- **Impressions** are published by Google for EU countries (and political ads) only; elsewhere they are empty.
- The platform filter covers ads shown since September 4, 2023 (Google's own limit).
- Domain queries return ads from *every* advertiser pointing to that site (agencies, retailers), exactly as the site does.

### Is it legal?

It reads the public Google Ads Transparency Center: the same ads anyone can open in a browser without logging in. It
needs no account, solves no CAPTCHA and gets around no paywall. The data is advertisers' own published ads, with no
personal profiles. Ad creatives belong to the advertisers, so check your own reuse of them against your local law.

# Actor input Schema

## `queries` (type: `array`):

One per line: a brand or advertiser name (Nike), a website (nike.com: every ad that points to it, from any advertiser), an advertiser id (AR...), or a link copied from adstransparency.google.com (advertiser page, single ad, or domain search; its region is used). A brand name resolves to the brand's own advertiser account, including companies that advertise under another legal name (Temu, Shein); the dataset says which one was matched.

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

Only ads shown in this country (all 243 regions the Transparency Center offers). Empty = anywhere.

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

Same labels as the Transparency Center.

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

Only ads shown on this Google surface. Google applies this filter only to ads shown since Sep 4, 2023.

## `startDate` (type: `string`):

YYYY-MM-DD. Keeps ads that ran on or after this date.

## `endDate` (type: `string`):

YYYY-MM-DD. Keeps ads that ran on or before this date.

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

Adds each ad's headline, description, call to action, landing URL and YouTube video (where Google exposes them), every variation, the countries it ran in with dates, impressions per country and platform (EU countries), and audience targeting. Same price; turn off for a faster list.

## `readAdText` (type: `boolean`):

Google keeps most text ads only as a picture. This reads the headline, description and display URL from the picture when they aren't available otherwise, and returns every word in image ads as imageText.

## `saveImages` (type: `boolean`):

Copy each ad's picture into this run's storage and add its link as imageCopyUrl, so you keep it after Google's link stops working.

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

Keep an ad only if its advertiser, domain or copy contains one of these. Empty = all.

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

Remember every ad returned and skip it next time, so a scheduled run returns and bills only ads it hasn't seen. Memory is kept per 'Monitor name'.

## `monitorName` (type: `string`):

Separate memories for separate monitors (e.g. 'competitors-us'). Default: 'default'.

## `maxItemsPerQuery` (type: `integer`):

Cap per advertiser or domain. Big brands have thousands of ads. With 'Only new ads' this many of the most recent ads are checked.

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

Hard cap for the whole run, and so for your cost. It is exact. 0 or empty = no limit.

## `includeEmptyResults` (type: `boolean`):

Free row with status no_ads / not_found / error for each query that returned no ads, so a list of companies gets an answer for every one. Not charged.

## Actor input object example

```json
{
  "queries": [
    "Nike",
    "hubspot.com"
  ],
  "region": "",
  "format": "all",
  "platform": "all",
  "includeDetails": true,
  "readAdText": false,
  "saveImages": false,
  "onlyNewAds": false,
  "maxItemsPerQuery": 100,
  "includeEmptyResults": true
}
```

# Actor output Schema

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

One item per ad.

# 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 = {
    "queries": [
        "Nike",
        "hubspot.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidyharvest/ads-transparency-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 = { "queries": [
        "Nike",
        "hubspot.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tidyharvest/ads-transparency-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 '{
  "queries": [
    "Nike",
    "hubspot.com"
  ]
}' |
apify call tidyharvest/ads-transparency-scraper --silent --output-dataset

```

## MCP server setup

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