# Google Ads Transparency Scraper - Ad Text & Copy (`ferncrawl/google-ads-transparency-ad-text`) Actor

Google Ads scraper for the Ads Transparency Center with ad text included at no extra charge: headlines, descriptions, display URLs & YouTube titles. Track competitor ads by domain or advertiser, OCR for screenshot ads, only-new-ads mode.

- **URL**: https://apify.com/ferncrawl/google-ads-transparency-ad-text.md
- **Developed by:** [Fern Crawl](https://apify.com/ferncrawl) (community)
- **Categories:** SEO tools, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 ad scrapeds

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

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

## What's an Apify Actor?

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 - Ad Text & Copy

**Scrape any advertiser's ads from the [Google Ads Transparency Center](https://adstransparency.google.com/) by domain, advertiser name or advertiser ID, with the ad text included: headline, description and display URL (plus the YouTube title for video ads). Ad text costs nothing extra.**

💲 **About $2 per 1,000 ads, ad text included.** Some other tools charge an extra fee for ad text. Here the text is part of the $0.002 per-ad price. Only optional extras cost more: OCR for screenshot-only text ads (+$0.002 per image only when OCR returns text) and per-ad country details (+$0.001).

🆕 **Only new ads mode.** Schedule a daily run and get only the ads you haven't received yet. Ads already returned are skipped and **not charged**.

#### Use cases

- 🕵️ **Competitor ads research**: see every Google search, display and YouTube ad a competitor runs, with the actual copy
- ✍️ **Ad copy inspiration & swipe files**: export headlines and descriptions to CSV/Excel for copywriters and AI prompts
- 🔔 **New-ad alerts**: a daily schedule with *Only new ads* sends you just the ads a brand launched since the last run
- 📊 **Market & agency reporting**: count ads by format, region and date and track how long ads keep running
- 🌍 **Multi-country checks**: compare what one advertiser shows in the US, UK, Taiwan, Japan… in one run

*Keywords: google ads scraper, google ads transparency center scraper, ads transparency center API, competitor ads, google ad library, ad copy scraper, search ads, YouTube ads.*

### Why this scraper?

- ✍️ **Real ad copy** — headline, long headline, description, display URL, call-to-action and business name, decoded from Google's live ad preview (`textSource: preview-structured / preview-heuristic`)
- 🎬 **YouTube video titles & channels** for video ads — the best text signal a video ad has
- 👁️ **Smart OCR (Auto)** — Google stores many search text ads only as PNG screenshots. In the default **Auto** mode the Actor OCRs exactly those ads (and nothing else), so you get their headline, description and display URL without paying for OCR on banners (PP-OCRv5: English, Traditional & Simplified Chinese, Japanese)
- 🌍 **Multi-region in one run** — US, GB, TW, JP… or *anywhere*; results are merged per ad with the list of regions
- 🆕 **Only new ads**: outputs only ads you have not received before (memory kept per query in your own Apify account); skipped ads are free. Optional **first-shown-since date** filter
- 🔔 **Monitoring (diff) mode** — schedule it daily and every ad is labelled `new`, `existing` or `removed` compared with the previous run of the same query
- 📅 **First shown / last shown dates**, days running, image and video URLs, video duration
- 🧯 **Fails loudly when Google changes its format** — schema-drift checks stop the run with `SCHEMA_DRIFT` instead of returning silent garbage
- 🛡️ **Proxy on by default** — Apify Proxy (datacenter) is pre-selected; the Actor rotates sessions and backs off on rate limits

### What data can I extract?

| Field | Example |
|---|---|
| advertiserName / advertiserId | Nike, Inc. / AR16735076323512287233 |
| creativeId, format | CR08040681151846678529, TEXT / IMAGE / VIDEO |
| headline, description, displayUrl | Shop Clothing Made For You - Free Shipping On Orders $75+ … |
| longHeadline, callToAction, businessName | responsive display / video ads |
| adText | all visible ad copy in one string |
| textSource | `preview-structured`, `preview-heuristic`, `youtube-title`, `ocr` |
| youtubeTitle, youtubeChannel, youtubeVideoIds, videoUrls, videoDuration | video ads |
| imageUrls | creative images |
| firstShown, lastShown, daysShown | 2023-11-17T02:49:19Z, 2026-10-30T…Z, 876 |
| regions / allRegions | \["US"] (allRegions with *Fetch per-ad details*) |
| ocrMeanConfidence | 0–1, only for OCR text |
| ocrError | Set only when OCR was skipped or failed for that ad (the ad is still returned with its other fields, and no OCR fee is charged) |
| previewUrl | link to the ad in the Transparency Center |
| monitorStatus | new / existing / removed (monitoring mode) |

### How to use it

1. Enter one or more **domains** (`nike.com`), **advertiser names** or **advertiser IDs** (`AR…`).
2. Pick **regions** (or leave *anywhere*), optional **formats** and **date range**.
3. Set **Max ads per advertiser**.
4. Leave **OCR** on *Auto* (default) — or choose *Off* for the fastest run, or *All image ads* to also try banners. Keep the default **1024 MB** memory whenever OCR can run.
5. Optional: turn on **Only new ads** (or **Monitoring mode**) and add a schedule.
6. Click **Start** and download JSON, CSV or Excel, or use the API.

Keep **Proxy configuration** enabled (the default). Google blocks requests from cloud data-center IPs without a proxy, often on the very first request.

#### Input example

```json
{
  "domains": ["nike.com", "momoshop.com.tw"],
  "regions": ["US", "TW"],
  "maxAdsPerAdvertiser": 100,
  "lastNDays": 30,
  "ocr": "auto",
  "monitorMode": true,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

#### Output example

```json
{
  "advertiserId": "AR16735076323512287233",
  "advertiserName": "Nike, Inc.",
  "creativeId": "CR08040681151846678529",
  "format": "TEXT",
  "headline": "Shop Clothing Made For You - Free Shipping On Orders $75+",
  "description": "Get geared up & take on any activity in style. Available now at Nike®…",
  "displayUrl": "www.nike.com/shop/sweatshirts",
  "textSource": "ocr",
  "firstShown": "2023-11-17T02:49:19Z",
  "lastShown": "2026-10-30T…Z",
  "regions": ["US"],
  "imageUrls": ["https://tpc.googlesyndication.com/archive/simgad/10342465081222581191"],
  "previewUrl": "https://adstransparency.google.com/advertiser/AR16735076323512287233/creative/CR08040681151846678529?region=US",
  "monitorStatus": "new"
}
```

### Monitor competitor ads (new / removed)

Turn on **Monitoring mode** and [schedule](https://docs.apify.com/platform/schedules) the Actor (e.g. daily). Each run compares with the previous run of the *same* query (stored in a named key-value store) and labels every ad `new` or `existing`. Ads that disappeared are output with `monitorStatus: "removed"` — but only when the run covered the advertiser's complete ad list, so a *Max ads* cut-off never creates false "removed" alerts. Connect a Slack or email integration to get alerts.

### Only new ads (incremental runs)

Turn on **Only new ads** (`onlyNewAds: true`) and schedule the Actor. The Actor remembers the creative IDs each query has already returned and skips them next time:

- **First run:** returns ads as usual and remembers them.
- **Later runs:** return only ads you have not received yet. If nothing new appeared, the run returns 0 ads and charges only the tiny start fee.
- **Not charged:** skipped ads are dropped before any charge, text decoding, OCR or detail request, so they cost nothing. They also don't count toward *Max ads per advertiser*.
- **Scope:** memory is kept per domain/advertiser + regions + formats, in a named key-value store `google-ads-transparency-seen` in **your own** Apify account. Changing regions or formats starts a fresh memory. To reset, delete that store or the matching `SEEN_…` record.
- **Scan limit:** results come ordered by most recently shown, so freshly launched ads appear near the top. To keep runs fast, each query looks through at most 1,000 already-seen or older ads beyond *Max ads per advertiser*.
- **Since date:** add **Only ads first shown on/after** (`sinceDate`, `YYYY-MM-DD`) to keep only ads launched after a date. It works with or without *Only new ads*, and older ads are skipped without charge.
- **Compared with Monitoring mode:** *Monitoring mode* outputs all ads labelled new/existing/removed (all charged). *Only new ads* outputs only the new ones. If both are on, *Only new ads* is used.

The run's `OUTPUT` record shows `skippedAlreadySeen` and `skippedFirstShownBeforeSince`.

```json
{
  "domains": ["nike.com"],
  "regions": ["US"],
  "maxAdsPerAdvertiser": 50,
  "onlyNewAds": true,
  "sinceDate": "2026-10-01"
}
```

### OCR modes

| | **Auto** (default) | **Off** | **All image ads** |
|---|---|---|---|
| Text from live ad previews | ✅ | ✅ | ✅ |
| YouTube video titles | ✅ | ✅ | ✅ |
| Text ads stored only as screenshots | ✅ OCR | ❌ empty | ✅ OCR |
| Image / banner ads | not OCR'd | not OCR'd | OCR'd |
| Speed | fast; +~9 s per screenshot ad | fastest (~1–20 s per 30 ads) | slowest |
| OCR charge | only ads actually OCR'd | none | every image ad |

Whether a text ad comes as a live preview or a screenshot depends on the advertiser and region. In our tests a large US brand's search text ads were almost all screenshots (0/30 had text with OCR off, 29/30 with OCR), while Taiwanese advertisers' ads were mostly live previews and needed little or no OCR. *Auto* handles both: it only OCRs a text ad when its copy is missing, and loads the OCR model only when the first such ad appears.

**Memory:** the default is 1024 MB. OCR at 512 MB is about 1.5× more expensive per image and peaked at 431 MB of 512 MB in our test, so lower memory is only safe with **OCR: Off** (peak ~150 MB).

### Limitations

- **Image ads:** most banner and display ads carry their message inside the artwork and are not OCR'd in *Auto*. Even with *All image ads*, only about **29%** of image ads in our test returned usable text. Text-ad coverage is much higher.
- **OCR quality:** OCR can make small character errors (e.g. `®` read as `?`, stray characters at line ends). Use `ocrMeanConfidence` to filter.
- **OCR speed:** about 9 seconds per image at 1024 MB, slower at lower memory. An advertiser with 100 screenshot text ads ≈ 15 minutes in *Auto*. Advertisers whose ads have live previews finish in seconds.
- **Video ads:** in-stream video ads have no copy outside the video itself; we return the YouTube title and channel when the video is public.
- **Advertiser names:** Latin-script names resolve best. For local-language brands use the **domain** or the **AR… ID** from the Transparency Center URL (e.g. Shopee Taiwan is registered as 「新加坡商蝦皮娛樂電商有限公司」).
- **Rate limits:** Google throttles heavy traffic. Keep the proxy on; the Actor backs off automatically and saves partial results if it is blocked.
- **Coverage is Google's:** the Transparency Center only shows what Google publishes. Some ads have no preview or are removed by Google.

### Tips

- Regions use ISO codes; `UK` is accepted as `GB`.
- Enable **Fetch per-ad details** to get `allRegions` with the last-shown date per country.
- Use **formats: TEXT** with **lastNDays** to focus on current search ads.

### Is it legal to scrape the Google Ads Transparency Center?

The Transparency Center is a public library Google publishes so anyone can see who is advertising. This Actor only reads publicly available ad data and collects no personal data. Check your use case against applicable laws and Google's terms, and consult a lawyer if unsure.

### FAQ

**I searched an advertiser name and got 0 ads in my country?** Advertisers are registered under their legal name, often in the local language. Search the local name or use the domain. The run log warns you when the matched advertiser is registered in another country.

**Why do some ads have no headline?** Either the ad is a screenshot-only text ad (use OCR *Auto* or *All*), a pure image/HTML5 banner (text is inside the artwork; OCR *All image ads* helps sometimes), or an in-stream video (see `youtubeTitle`).

**I started a run with an empty input — what happens?** If no domain, advertiser name or ID is given, the Actor runs the example query (nike.com, US, 10 ads) so it never fails, and logs a warning.

**Something broke?** If Google changes its internal format the run fails with `SCHEMA_DRIFT` in the OUTPUT record instead of returning bad data. Please open an issue in the Issues tab — we fix these fast.

# Actor input Schema

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

Websites whose ads you want, e.g. nike.com. Matches every advertiser account that advertises this domain.

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

Legal / verified advertiser names, e.g. 'Nike, Inc.'. Resolved through the Transparency Center search box (Latin-script names work best).

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

Exact advertiser IDs from an adstransparency.google.com/advertiser/AR… URL. Most precise option.

## `nameMatch` (type: `string`):

For advertiser names: take the single best match, or all advertisers Google suggests.

## `regions` (type: `array`):

ISO country codes (US, TW, GB, JP…) or 'anywhere'. Several regions are scraped in one run and merged per ad.

## `formats` (type: `array`):

Leave empty for all formats.

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

Only ads shown on/after this date.

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

Only ads shown on/before this date.

## `lastNDays` (type: `integer`):

Overrides the dates above.

## `maxAdsPerAdvertiser` (type: `integer`):

Stop after this many ads per advertiser/domain (across all regions and formats). Higher = more cost.

## `extractAdText` (type: `boolean`):

Decode each ad's rendered preview to get headline, description and display URL. Included in the per-ad price.

## `ocr` (type: `string`):

Auto (default): OCR runs only on text-format ads that Google stores as a screenshot, so their headline/description/display URL would otherwise be empty (common for US search ads). Image and video ads are not OCR'd. Off: no OCR at all (fastest). All: OCR every image ad including banners — slow, and banners often contain little readable copy. Text from live previews and YouTube video titles is extracted in every mode. OCR takes ~9 s per image at 1024 MB and is charged per image actually OCR'd. Use at least 1024 MB memory when OCR can run.

## `fetchDetails` (type: `boolean`):

One extra request per ad: every country the ad ran in, last date per country, number of variations. Charged per ad.

## `monitorMode` (type: `boolean`):

Compares with the previous run of the same query and labels each ad new/existing; ads that disappeared are output as 'removed'. Schedule the actor daily for competitor alerts.

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

Remembers the ads this query already returned (per domain/advertiser + regions + formats, in a key-value store in your own Apify account) and outputs only ads you have not received before. Skipped ads are not charged. The first run returns ads normally and builds the memory. Ideal with a daily schedule.

## `sinceDate` (type: `string`):

Optional. Keeps only ads whose first-shown date is on or after this date (newly launched ads). Older ads are skipped and not charged. Works with or without 'Only new ads'.

## `minRequestIntervalSecs` (type: `number`):

Politeness delay. Lower = faster but Google may show its anti-bot page.

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

Required in practice: Google blocks cloud data-center IPs without a proxy. Apify Proxy (datacenter) is on by default; sessions rotate automatically if Google rate-limits.

## `proxyAssets` (type: `boolean`):

Off (recommended): only the Transparency Center search queries use the proxy. Preview scripts and ad images come from other Google hosts that don't rate-limit, so this keeps proxy traffic ~50x smaller.

## Actor input object example

```json
{
  "domains": [
    "nike.com"
  ],
  "nameMatch": "best",
  "regions": [
    "US"
  ],
  "maxAdsPerAdvertiser": 10,
  "extractAdText": true,
  "ocr": "auto",
  "fetchDetails": false,
  "monitorMode": false,
  "onlyNewAds": false,
  "minRequestIntervalSecs": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "proxyAssets": false
}
```

# Actor output Schema

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

One item per ad: advertiser, creative ID, format, headline, description, display URL, adText, textSource, YouTube title, image/video URLs, first/last shown dates, regions, monitorStatus.

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

JSON summary: resolved advertisers, ad counts, OCR images and seconds, monitoring new/removed counts, errors such as SCHEMA_DRIFT.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "nike.com"
    ],
    "regions": [
        "US"
    ],
    "maxAdsPerAdvertiser": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("ferncrawl/google-ads-transparency-ad-text").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "domains": ["nike.com"],
    "regions": ["US"],
    "maxAdsPerAdvertiser": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("ferncrawl/google-ads-transparency-ad-text").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "nike.com"
  ],
  "regions": [
    "US"
  ],
  "maxAdsPerAdvertiser": 10
}' |
apify call ferncrawl/google-ads-transparency-ad-text --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ferncrawl/google-ads-transparency-ad-text"
        }
    }
}
```

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/cUSY4G7jh2LYVdb17/builds/IoGHFKPs6xL4EePCI/openapi.json
