# Google Ads Transparency Scraper: Ads, Text, Dates by Advertiser (`cozybit/google-ads-transparency-scraper`) Actor

Collect Google ads by advertiser or website, with screenshots, dates, variations, country details and readable text where available. Monitor unseen ads. Trial: 0.01 USD per 1,000 ads until 30 Nov 2026.

- **URL**: https://apify.com/cozybit/google-ads-transparency-scraper.md
- **Developed by:** [Cozybit](https://apify.com/cozybit) (community)
- **Categories:** Marketing, Business, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.01 / 1,000 ad records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Google Ads Transparency Scraper: ads, text and dates by advertiser

Collect ads from Google's Ads Transparency Center by advertiser name, website or advertiser ID. Get a screenshot or preview link, first and last shown dates, days shown, available ad text, variations and country details. No Google account or browser is needed.

One price per ad covers available text, details, compute and proxies (see [Pricing](#pricing)). Turn on **Return only ads not collected before** to avoid collecting and paying for the same ads again.

**How often you get ad text:** Google stores many ads only as a screenshot or video, so no scraper can read words from those; you still get a screenshot, image or preview link for every ad. Where Google keeps the text, this scraper reads it. On the same 12 Nike ads in the US it returned text for 10, as many as the most-used alternative scraper, while another popular one returns no ad text at all. On 30 Shopping product ads from three US retailers it returned the product title for 22, the most-used alternative scraper for 20 on the same search. Across our test runs, 40% of all ads and 85% of US ads came with readable text (see [Limits](#limits) for other countries).

### What it returns

- Advertiser name and ID, creative ID and a link to the ad on Google.
- Ad format: text, image or video.
- First and last shown dates, plus Google's number of days shown.
- A screenshot/image URL and/or a Google preview URL. Video records also get a YouTube watch link when Google exposes an unambiguous video ID.
- Headline, description and displayed website **where Google provides readable text**, and the product title and store of Shopping product ads. Text availability varies greatly; see [Limits](#limits).
- Available rendering variations, country-level first/last shown dates and impression ranges. Details are on by default; turn them off for speed.
- Or, in [advertiser summary mode](#advertiser-summary-mode), one row per advertiser: who advertises for a website or name, their country, verification, legal name, approximate ad count and latest ad.

### Input

| Field | What it does | Default |
|---|---|---|
| `queries` | Advertiser names, websites such as `nike.com`, or IDs starting with `AR` | example: `nike.com` |
| `resultType` | `ads` (one row per ad) or `advertisers` (one row per advertiser, see [Advertiser summary mode](#advertiser-summary-mode)) | `ads` |
| `maxAdvertisersPerQuery` | Advertiser mode only: advertisers per entry, 1-50 | 5 |
| `region` | Country where ads were shown, such as `US`, `GB`, `DE` or `IN`; empty means anywhere | anywhere |
| `platform` | `SEARCH`, `YOUTUBE`, `SHOPPING`, `MAPS` or `PLAY`; empty includes all | all |
| `format` | `TEXT`, `IMAGE` or `VIDEO`; empty includes all | all |
| `dateFrom`, `dateTo` | Optional shown-between dates, `YYYY-MM-DD`; the end date is inclusive | no date filter |
| `maxAdsPerQuery` | Maximum ads per entry, most recently shown first; in monitoring mode only unseen ads count | 40 |
| `includeAdText` | Read available headlines, descriptions, displayed websites and Shopping product titles | on |
| `includeDetails` | Collect variations and available country dates/impression ranges | on |
| `onlyNewAds` | Remember collected creative IDs and skip them in later runs | off |
| `proxyConfiguration` | Residential proxy for advertiser searches, ads and details; no country is forced. Text previews normally load directly, with one proxy fallback if blocked | residential |

**Websites give the most reliable results.** For a name, the actor picks one advertiser from Google's own name suggestions. It prefers names that start with your words (`Flipkart` finds `Flipkart Internet Private Limited`), then advertisers registered in your selected country, then the advertiser with the most ads. Google's suggestions do not always contain the company you mean: `Canva` can offer only `Canvasso` and similar names. Use `canva.com` or the advertiser ID (`AR...`, shown in the ad's Transparency Center link) instead. The run's `RUN_SUMMARY` record lists the chosen advertiser and the top five candidates for each name. A website can return ads from several advertisers or agencies.

Example: collect up to 100 Nike ads shown in the US:

```json
{
  "queries": ["nike.com"],
  "region": "US",
  "maxAdsPerQuery": 100
}
```

Example: monitor a competitor's new Google Search ads in the US (schedule it daily; each run returns only ads you have not collected before):

```json
{
  "queries": ["shopify.com"],
  "region": "US",
  "platform": "SEARCH",
  "maxAdsPerQuery": 30,
  "onlyNewAds": true
}
```

A run collects roughly 2,500-3,500 ads per hour with text and details on (50 ads take about a minute). The default run timeout is 3 hours; for more, raise **Timeout** in the run options or split the entries over several runs.

### Use it from code

Replace `<YOUR_API_TOKEN>` with your token from Apify Console > Settings > API & Integrations.

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("cozybit/google-ads-transparency-scraper").call(
    run_input={"queries": ["nike.com"], "region": "US", "maxAdsPerQuery": 20}
)
for ad in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(ad["advertiser_name"], ad["format"], ad["last_shown"], ad["image_url"])
```

JavaScript (`npm install apify-client`):

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('cozybit/google-ads-transparency-scraper')
    .call({ queries: ['nike.com'], region: 'US', maxAdsPerQuery: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

HTTP API, one call that waits for the run and returns the ads (for runs under 5 minutes):

```bash
curl -X POST "https://api.apify.com/v2/acts/cozybit~google-ads-transparency-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_API_TOKEN>" -H "Content-Type: application/json" \
  -d '{"queries": ["nike.com"], "region": "US", "maxAdsPerQuery": 20}'
```

Apify can also run this on a schedule and send results to webhooks, Google Sheets, Zapier or Make through its integrations.

### Output (record format v1)

One record per collected ad. You can download the dataset as JSON, CSV or Excel. Example with `includeDetails: false` and `includeAdText: false`:

```json
{
  "query": "nike.com",
  "input_type": "domain",
  "advertiser_id": "AR16735076323512287233",
  "advertiser_name": "Nike, Inc.",
  "creative_id": "CR14091266735822864385",
  "format": "TEXT",
  "first_shown": "2023-04-25T20:05:00.205Z",
  "last_shown": "2026-10-30T16:24:49.587Z",
  "days_shown": 1108,
  "image_url": "https://tpc.googlesyndication.com/archive/simgad/1095568144160320364",
  "preview_url": null,
  "video_url": null,
  "ad_url": "https://adstransparency.google.com/advertiser/AR16735076323512287233/creative/CR14091266735822864385?region=US",
  "headline": null,
  "description": null,
  "display_url": null,
  "ad_text_status": "unavailable",
  "region": "US",
  "status": "ok",
  "error": null,
  "scraped_at": "2026-10-30T17:00:00.000Z"
}
```

With details enabled (the default), records also contain `variations: [{image_url, preview_url}]` and `regions: [{geo_target_id, first_shown, last_shown, impressions_min, impressions_max}]`. Unavailable values are `null`; impression bounds are strings to preserve precision. Country dates use `YYYY-MM-DD`; overall first/last shown timestamps are UTC. `days_shown` is Google's served-day count, not a date subtraction or an impression count.

`ad_text_status` is `ok` when text was extracted, `not_text` for image ads without readable text, or `unavailable` when readable copy was not exposed, extraction was disabled or optional enrichment failed. A valid ad remains `status: "ok"` and is charged even when text/details are unavailable; `error` explains an enrichment failure if there was one. A query that fails before finding an ad produces no fake ad row. Query failures, empty searches, selected advertisers, requests, skipped IDs and missing visuals are recorded in the run's `RUN_SUMMARY` key-value-store record.

### Advertiser summary mode

Set `resultType` to `advertisers` to answer "who advertises for this website, and how much?" without collecting every ad. Each entry returns up to `maxAdvertisersPerQuery` rows:

- **Website:** the distinct advertisers behind the 200 most recently shown ads for that website (with your filters), in order of appearance. A website can be advertised by the brand, its regional companies, agencies and resellers.
- **Name:** the best matches from Google's name suggestions, ranked as described in [Input](#input).
- **Advertiser ID:** that advertiser.

| Field | Meaning |
|---|---|
| `advertiser_name`, `advertiser_id`, `advertiser_url` | Advertiser and its page on Google |
| `advertiser_country`, `legal_name`, `verified` | Country and legal name from Google's advertiser record; `verified` is Google's identity verification (`null` if unknown) |
| `ad_count_min`, `ad_count_max` | Google's approximate number of this advertiser's ads matching your filters; for a website entry, only ads for that website |
| `query_ad_count_min`, `query_ad_count_max` | Website entries: approximate ads for the website from all advertisers |
| `latest_ad_last_shown`, `latest_ad_format`, `latest_ad_url` | The advertiser's most recently shown matching ad |

Example: who advertises for three websites in the US:

```json
{
  "queries": ["nike.com", "booking.com", "shopify.com"],
  "region": "US",
  "resultType": "advertisers"
}
```

This returned 11 advertisers in 60 seconds, the same 11 as the most-used alternative scraper's advertiser mode, including Nike's Dutch and Korean companies and a media agency. An entry without ads returns one row with `status: "no_data"`. A known advertiser without matching ads keeps its row with counts of 0. If Google's advertiser record or count fails, the row stays with those fields empty and an `error`. `onlyNewAds`, `includeAdText`, `includeDetails` and `maxAdsPerQuery` do not apply in this mode. Ad counts are Google's ranges, not exact numbers.

### Pricing

**Trial price until 30 November 2026:** 0.01 USD per 1,000 ads (the lowest price Apify allows). From 1 December 2026 this scraper costs 1.00 USD per 1,000 ads. Apify notifies all users before a price change.

| Event | Price |
|---|---|
| One ad record, including available text, details, dates and links | **1.00 USD per 1,000** (0.001 USD each) |

- 100 collected ads cost **0.10 USD**.
- 1,000 collected ads cost **1.00 USD**.
- Previously seen ads skipped in monitoring mode cost **nothing**. Empty queries also have no ad charge.
- Advertiser summary rows currently have no charge.
- Text or details being unavailable does not make the ad free: the delivered ad, dates and visual links are the charged record.

There is no separate text/details add-on or additional proxy/compute charge. Set a maximum cost per run in the run options to cap spending; no new work starts once it is reached, and ads not delivered are neither charged nor marked collected.

### Free Apify plan

Free-plan runs return at most **50 ads (or 50 advertiser rows) in total**, shared across all entries, even if you request more. At the limit the run finishes successfully with: "Free Apify plan limit reached: up to 50 ads per run. Upgrade to any paid Apify plan to remove it." Everything already collected remains in the dataset. Any paid Apify plan removes this actor-imposed cap.

### Monitoring mode

Turn on `onlyNewAds` and reuse the same entry, country, platform and format on later runs. The first run collects normally. Later runs skip previously collected creative IDs **before** looking up details/text or charging. The maximum per entry counts new ads, not skipped ones.

History is stored in the named key-value store `google-ads-transparency-state`, separately for each normalized entry/country/platform/format combination. It remembers the latest 50,000 collected IDs per combination and drops the oldest beyond that limit. Only successfully emitted records enter history; unpushed records do not. Changing a date filter or the text/details options does not reset history. Use one run at a time for the same combination to avoid concurrent updates overwriting history. Deleting a history record resets that combination, so later collections can include and charge those ads again.

Later searches look at ads shown since three days before the previous run started, unless your `dateFrom` is later. The three days cover Google reporting serving dates late; an ad that Google reports more than three days late can be missed. The first run still obeys your requested maximum.

### Reliability

In 16 test runs on the Apify platform with default settings (7 countries and "anywhere"; names, websites and advertiser IDs; mixed platform and format filters), **every run finished without a failed entry and all 264 ads came back as valid records**, each with a screenshot, image or preview link. Google blocked 4 requests along the way; the actor recovered every time on a new IP. The median run took 30 seconds.

How it stays reliable: Google blocks shared datacenter IPs, so searches and ad details go through residential IPs, each making one slow, paced request at a time. A blocked IP or a failed proxy connection is replaced, up to four times per entry.

### Limits

- **Readable ad text is not guaranteed.** Share of ads with text in our test runs: US 85%, Brazil 63%, Germany 25%, India 16%, UK none in our sample. Image ads usually keep their words inside the picture; Shopping product ads come with their product title.
- Screenshots are returned as `image_url`. **No OCR** or video transcription is used. Video ads get their companion text when Google includes it, plus a YouTube link.
- Keep the default residential proxy. Datacenter proxies are not recommended because Google blocks shared datacenter IPs.
- Dates, variants and impression ranges are returned only where Google exposes them. `region` filters where an ad was shown, not necessarily where its advertiser is registered.
- A name finds a best match from Google's suggestions, not a guaranteed identity (see [Input](#input)). Prefer websites or advertiser IDs.
- A maximum number of ads is not a guarantee that that many exist for your filters. Google can omit, delay or remove ads and can change its undocumented endpoints.
- Preview URLs and media URLs can expire. Save the data or download permitted media promptly if you need long-term access.
- Google's public transparency data is the source; this actor is not affiliated with Google.

### Support

Use the actor's **Issues** tab for problems and feature requests. Include the run link, the entry and filters you used, and the creative ID for a missing field. Do not include proxy passwords or API tokens.

# Actor input Schema

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

Enter names such as Nike, websites such as nike.com, or Google advertiser IDs starting with AR. Name matching prefers whole words at the start, then elsewhere, then substrings. Within each group, your selected country is preferred, then the most ads. Check RUN_SUMMARY.resolved for the choice and candidates. Free Apify plans return up to 50 ads or 50 advertiser rows in total per run; any paid plan removes this limit.

## `resultType` (type: `string`):

Ads returns one row per ad. Advertisers returns one row per advertiser per query with filtered approximate ad counts, billing country, legal name, verification and the latest ad. Website discovery scans at most five pages of 40 recent ads, so it may not find every advertiser. Empty discovery produces one free no-data row. Known advertisers with no matching ads are retained with zero counts; zero or failed counts are free. Advertiser mode ignores onlyNewAds, includeAdText, includeDetails and maxAdsPerQuery.

## `maxAdvertisersPerQuery` (type: `integer`):

Advertiser mode only. Keep this many distinct advertisers in first-appearance order for websites, or the top ranked name matches. An AR ID selects only that advertiser. Free plans return at most 50 rows per run, including no-data rows.

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

ISO country code, for example US, DE, GB or IN. Empty means anywhere; this filters ad delivery, not proxy location.

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

Empty includes every Google platform.

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

Empty includes every ad format.

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

Optional date YYYY-MM-DD. Filters serving dates, not the original creative creation date.

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

Optional inclusive date YYYY-MM-DD.

## `maxAdsPerQuery` (type: `integer`):

Ignored in advertiser mode. Collect up to this many ads for each entry, most recently shown first. In monitoring mode this counts only unseen ads. Free Apify plans return up to 50 ads in total per run, even across several entries.

## `includeAdText` (type: `boolean`):

Ignored in advertiser mode. Fetch text previews, including product titles of Shopping image ads. Text ads without a preview also need a detail lookup. Google sometimes exposes screenshots only, reported as unavailable. No OCR is used.

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

Ignored in advertiser mode. Include available variations, first and last shown dates by country, and impression ranges where Google provides them. Included in the price. Turn off for faster runs; a detail lookup may still be needed to find text.

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

Ignored in advertiser mode; no monitoring state is read or written. Remember collected ads for each entry, country, platform and format. Later runs skip them before text lookup or charging. The first run collects normally. Use one monitoring run at a time for the same settings. Previously collected ads are not returned again until their IDs leave the 50,000-ad history.

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

Keep the default residential proxy for advertiser searches, ads and details. No proxy country is required. Datacenter proxies are not recommended: Google blocks shared datacenter IPs. Text previews normally load directly from the run's server; a blocked preview is retried once through the current proxy session.

## Actor input object example

```json
{
  "queries": [
    "nike.com"
  ],
  "resultType": "ads",
  "maxAdvertisersPerQuery": 5,
  "region": "US",
  "platform": "",
  "format": "",
  "dateFrom": "",
  "dateTo": "",
  "maxAdsPerQuery": 20,
  "includeAdText": true,
  "includeDetails": true,
  "onlyNewAds": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One record per ad, with links, dates and available text.

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

One row per advertiser per query with filtered approximate counts and latest ad; empty queries have one free no-data row.

## `runSummary` (type: `string`):

Selected advertisers, skipped ads, requests and errors.

# 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.com"
    ],
    "region": "US",
    "maxAdsPerQuery": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("cozybit/google-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.com"],
    "region": "US",
    "maxAdsPerQuery": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("cozybit/google-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.com"
  ],
  "region": "US",
  "maxAdsPerQuery": 20
}' |
apify call cozybit/google-ads-transparency-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cozybit/google-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/0Q8MtLfZyA323k7xf/builds/7BJM1I0VX1so5GLzn/openapi.json
