# Google Ads Transparency Scraper with Ad Text (`wellbuilt_zythem/google-ads-text`) Actor

Every ad an advertiser, domain or keyword runs on Google, from the official Ads Transparency Center, with the ad text: headline, description, display URL and the local business address. Text ads Google only shows as an image are read too (OCR). Dates, days shown, region, images, YouTube.

- **URL**: https://apify.com/wellbuilt_zythem/google-ads-text.md
- **Developed by:** [Carlos Escanez](https://apify.com/wellbuilt_zythem) (community)
- **Categories:** SEO tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 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 Transparency Scraper with Ad Text

Every ad an advertiser, a domain or a keyword runs on Google, straight from the official **Google Ads Transparency Center**, **with the words of the ad**: headline, description, display URL, and for local businesses the name, street address, city, rating and phone the ad shows.

Most Transparency Center scrapers stop at a link to the ad preview. This one reads the preview for you, and when Google only keeps a text ad as a picture (it does for most big advertisers), it reads the picture with OCR. You get a spreadsheet of ad copy, not a list of links.

### See it run

https://youtu.be/naqCfi5plWQ

### Who uses it

- **PPC and marketing agencies**: find local businesses that are already paying for Google Ads in a niche and a country, with what they say in their ads. A business that runs ads has a budget.
- **Competitor research**: every headline and description a competitor has run, with first and last shown dates and days on air. Copy that runs for months is copy that converts.
- **Brand protection and compliance**: who is bidding on your brand or sending ads to your domain.
- **Lead generation for local services**: roofers, dentists, plumbers, insurance agents advertising in the US right now, with their website and, on Maps ads, their address and rating.

### What you get, measured

Measured on 8 October 2026 in two runs on the Apify platform (keywords "roofing" and "dentist", domains geico.com and angi.com, United States):

| | Result |
|---|---|
| Text ads with a headline | **93 of 95 (98 %)** |
| Text ads with a description | 85 of 95 (89 %) |
| Text ads with the display URL | 73 of 95 (77 %); Google Maps ads show a business name instead of a URL |
| Text ads read from the HTML preview | 17 |
| Text ads read with OCR (Google keeps them only as an image) | 78, average OCR confidence 85-96 % |
| Speed and platform cost | 108 ads in 94 seconds, about $0.0001 per ad of platform usage |

Said plainly:

- **Image (banner) ads** come with the image URL, not with text: reading a graphic design with OCR gives loose words, not a headline, and the Actor does not pretend otherwise.
- **Video ads** come with the YouTube link.
- **OCR is OCR**: about 4 % of text ads read from an image carry a stray character where a photo touches the text. The field `textSource` says `ocr` or `html_preview`, and `ocrConfidence` gives the score, so you can filter.
- Fields Google does not show in a given ad are absent from that row, never invented.

### Input

- **Keywords**: like the Transparency Center search box ("roofing", "dentist miami"). Each keyword brings the matching advertisers; the run takes those from your country first, then the ones with most ads (`maxAdvertisersPerQuery`, 5 by default).
- **Domains**: every ad that sends traffic to a website, whoever paid for it.
- **Advertiser IDs**: the `AR...` ID from a Transparency Center URL.
- **Country**: two-letter code (US, GB, CA, AU, DE, FR, ES, MX, BR, IN and 25 more) or `anywhere`.
- **Formats**: text, image, video, or all.
- **Only ads shown in the last N days**: 7 or 30 gives what is running now.
- **Max ads per advertiser or domain** and **Max ads in total**.
- **Read the ad text** (on by default): turn off for links only.
- **Only ads new since the last run**: remembers the ads already delivered in a named key-value store in your account. Schedule it weekly to watch competitors and pay only for new ads.

### Output

One row per ad:

```json
{
  "advertiserId": "AR14331040638106075137",
  "advertiserName": "Roofing PD",
  "creativeId": "CR06225560223742951425",
  "format": "text",
  "headline": "Michigan's Roofing Experts",
  "description": "Roofing, Siding, Gutters, & More From A Certified, Licensed Roofing Company! Call Today",
  "displayUrl": "roofingpd.com",
  "domain": "roofingpd.com",
  "businessName": "Roofing PD",
  "addressStreet": "5073 Canterbury Dr",
  "addressCity": "Brighton",
  "addressRegion": "MI",
  "addressPostalCode": "48114",
  "addressCountry": "US",
  "firstShown": "2025-01-17T19:27:41.000Z",
  "lastShown": "2026-10-07T12:01:52.000Z",
  "daysShown": 385,
  "lastShownDaysAgo": 0,
  "textSource": "html_preview",
  "hasText": true,
  "adUrl": "https://adstransparency.google.com/advertiser/AR14331040638106075137/creative/CR06225560223742951425?region=US",
  "regionFilter": "US",
  "query": "roofing",
  "scrapedAt": "2026-10-08T09:12:45.000Z"
}
```

Other fields when the ad has them: `adLayout` (`search` or `maps`), `rating`, `reviewCount`, `category`, `services`, `phone` (E.164), `extraTexts` (sitelinks and other lines), `adTexts` (every text of the ad in order), `ocrConfidence`, `imageUrl`, `previewUrl`, `youtubeVideoId`, `youtubeUrl`.

`RUN_SUMMARY` in the key-value store lists, per advertiser or domain, the ads read and delivered and how many came with text, plus the totals read from previews and by OCR.

### Pricing

Pay per ad delivered (event `ad`), plus a small Actor start fee. Ads filtered out, and ads already delivered when "only new ads" is on, are not charged. The text is included in the price of the ad.

### Reliability and fair use

- The Actor asks the Transparency Center the same questions its own website asks, one request at a time with a pause between them, and backs off when Google says to slow down.
- **It does not try to get past Google's "unusual traffic" check.** If Google answers with its CAPTCHA page, the run stops, delivers what it already has and says so in the log and in `RUN_SUMMARY` (`blockedByGoogle`). Run it again later.
- An advertiser or domain that fails does not stop the others.
- Data: public ad transparency records published by Google about advertisers (companies). No personal data beyond what an advertiser puts in its own ad.

### Release test

`npm test` runs two checks: `test/parse.js` reads real ads saved on 8 October 2026 (a local search ad, a search ad with a URL path, a video ad and a text ad Google keeps as an image), and `test/smoke.js` asks the live Transparency Center (keyword search, two pages of one advertiser, the country filter, a preview and an OCR read). From some home IP addresses Google answers the live test with its CAPTCHA; the test then says so instead of trying to pass it, and has to be run from Apify.

# Actor input Schema

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

Words to search in the Transparency Center, like its own search box: "roofing", "dentist miami", "solar". Each keyword returns the advertisers whose name matches; the run takes the ones with most ads (see "Advertisers per keyword") and lists their ads.

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

Websites whose ads you want, e.g. "angi.com". Every ad that sends traffic to that domain, whoever paid for it.

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

IDs from the Transparency Center URL (they start with AR), e.g. AR14331040638106075137.

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

Two-letter country code: US, GB, CA, AU, DE, FR, ES, MX, BR, IN... or "anywhere".

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

Leave empty for all. Text ads come with their text; image ads with the image; video ads with the YouTube link.

## `lastShownWithinDays` (type: `integer`):

Keep only ads Google showed recently: 7 or 30 gives the ads running now. Leave empty for the whole history.

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

How many of the matching advertisers to take per keyword, those from the chosen country first, then the ones with most ads.

## `maxAdsPerTarget` (type: `integer`):

Newest first.

## `maxResults` (type: `integer`):

Stop after this many ads. Leave empty for no limit.

## `extractText` (type: `boolean`):

Headline, description, display URL and the local business address of every text ad. Ads Google only keeps as an image are read with OCR. Turn off for a faster run with links only.

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

Remembers the ads already delivered in a named key-value store in your account and skips them next time: schedule it weekly to watch competitors and pay only for new ads.

## `stateStoreName` (type: `string`):

One name per schedule or per list of competitors.

## `resetState` (type: `boolean`):

Start "only new ads" from scratch: the next run delivers everything again and remembers it.

## Actor input object example

```json
{
  "queries": [
    "roofing"
  ],
  "region": "US",
  "maxAdvertisersPerQuery": 5,
  "maxAdsPerTarget": 20,
  "extractText": true,
  "onlyNewAds": false,
  "stateStoreName": "google-ads-text-seen",
  "resetState": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

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

No description

# 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": [
        "roofing"
    ],
    "region": "US",
    "maxAdsPerTarget": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("wellbuilt_zythem/google-ads-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 = {
    "queries": ["roofing"],
    "region": "US",
    "maxAdsPerTarget": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("wellbuilt_zythem/google-ads-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 '{
  "queries": [
    "roofing"
  ],
  "region": "US",
  "maxAdsPerTarget": 20
}' |
apify call wellbuilt_zythem/google-ads-text --silent --output-dataset

```

## MCP server setup

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