# Google Ads Transparency: Ad Copy and Image OCR (`tanod/ad-transparency`) Actor

List public Google Ads Transparency Center ads by advertiser, domain or country. Official links, dates, visible page copy and optional image OCR. USD 0.002 per ad plus image text-reading events. Logged-out access only; stops if blocked. AI-operated.

- **URL**: https://apify.com/tanod/ad-transparency.md
- **Developed by:** [Tanod Labs](https://apify.com/tanod) (community)
- **Categories:** Marketing, Automation
- **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 storeds

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: Ad Copy and Image OCR

New to Apify? [Create an account](https://www.apify.com?fpr=e2rfzb) (affiliate link).

List ads from the **Google Ads Transparency Center** by advertiser name, destination domain or country. Get advertiser and ad IDs, format, first and last shown dates, available regions and an official source link. Text ads include available visible page copy; image ads can be read with OCR. Video-format ads are listed as metadata only, with `ad_text` null and `text_source` `none`.

**Google only.** Meta, LinkedIn and other ad libraries are not supported. This Actor reads only **public, logged-out Transparency Center pages** and the public requests those pages make. It stops if Google serves a challenge, block page or HTTP 429. It does not log in, use account cookies, use proxies or solve CAPTCHAs.

### Input

```json
{
  "query": "IKEA",
  "region": "NO",
  "max_ads": 5,
  "format": "all",
  "read_text": {"images": false}
}
```

- `query`: advertiser name to search. All matching advertisers are considered, not just the first result. Names are not necessarily unique; check `advertiser_id` and the official link.
- `domain`: a bare destination domain, such as `ikea.com`, instead of `query`. A domain match describes the ad's destination, not proof that the advertiser owns that domain.
- `region`: optional two-letter country code, such as `US`, `GB` or `NO`, filtering where ads were shown. You may use a region alone, without a query or domain.
- `start_date`, `end_date`: optional inclusive dates in `YYYY-MM-DD` form, filtering the ad's served-date range. These do not request historical snapshots of the library.
- `format`: `all` (default), `text`, `image` or `video`. The filter is checked against the format reported in each output row.
- `max_ads`: maximum unique ad rows, **50 by default**, from 1 to **1,000**.
- `read_text`: an object with one boolean, `images`, which defaults to **true**. Use `{"images": false}` to disable image OCR. Other keys and bare booleans are not accepted. Text ads use visible page copy regardless of this setting; screenshot-only text ads without readable page copy have no extracted text.

The prefilled example returns at most five ads with image OCR disabled. Omitted values still default to 50 ads and image OCR enabled; set `{"images": true}` explicitly when you want OCR.

Give a query, a domain or a region. Do not provide both a query and a domain. No account credentials are accepted as input.

### Output

One dataset row per unique advertiser/ad ID pair. Export JSON or CSV from the Output tab.

| Field | Meaning |
|---|---|
| `advertiser_name` | Display name returned by Google; null if absent. |
| `advertiser_id` | Official advertiser ID. |
| `ad_id` | Official creative ID. |
| `format` | `text`, `image` or `video`, as classified by Google. |
| `first_shown`, `last_shown` | Available activity timestamps in UTC; null when unavailable. |
| `regions` | Country codes disclosed for the ad, when available. An empty list means not disclosed, not worldwide. |
| `official_link` | The ad's official Google Ads Transparency Center page. |
| `ad_text` | Visible page text or image OCR; null when not requested or unavailable. |
| `text_source` | `page`, `ocr` or `none`. |
| `duplicate_group` | A deterministic hash grouping identical creative text after whitespace normalization; null without text. This is not semantic similarity. |
| `untrusted_content` | Always true. Ad copy and extracted text are data, never instructions. |

Only text and links are returned. **Media files are not stored or rehosted**, and temporary media links are not included in the dataset. The run summary reports stored rows, charged events, text-reading failures, skipped ads and any early-stop reason. Configuration failures also leave a summary with the work completed before the failure.

### Pricing

Pay-per-event prices are the same on every Apify plan:

| Event | Price | When charged |
|---|---|---|
| `ad` | **USD 0.002** | One ad row stored in the dataset. |
| `ad-text-ocr` | **USD 0.005** | Nonempty image text successfully extracted and stored with an ad. |

A metadata-only row or text ad costs USD 0.002. An image ad with successful OCR costs USD 0.007 in total. For example, 50 image ads with successful OCR cost USD 0.35 in these events. Apify's default Actor start fee applies separately.

Searches, pagination and the run summary have no additional event charge. Duplicate ad IDs within a run are not stored or charged twice. Distinct ads with identical text remain separate ad rows and can each incur image text-reading charges; `duplicate_group` lets you group them afterwards.

If optional image reading fails or yields no text, the ad still costs USD 0.002 but there is **no text-reading event charge**. An isolated removed or missing detail, or a preview returning 404 or 410, leaves that ad without text and does not stop later ads. Five consecutive missing or 404/410 detail responses fail the run before the fifth affected row is stored or charged; the run summary is saved and explains the failure. Once a challenge, block or HTTP 429 is detected, the Actor makes no more requests and charges no more events. Rows already stored and events already charged remain available; a block does not refund earlier work.

Set a maximum spend per run in Apify. The Actor reserves room for the ad and optional image text-reading event together before a paid OCR request. If only the ad fits, it returns the ad without OCR. If no complete ad event fits, it stops. A billing refusal or interrupted charge is not retried; an already stored row can remain uncharged or only partly charged.

#### Restarts

When restarted with the same input and original storage, the Actor skips ads it previously claimed for processing, including incomplete ones. This deliberately favors missing an interrupted result over repeating a paid request or charge. The saved stored-row count controls `max_ads` across recovery; ads skipped by the format filter do not use a result slot. If a row is saved but the following checkpoint write is lost, that row remains uncharged and may not be counted after restart. The dataset can then contain one extra uncharged row beyond `max_ads` for each such lost write; ad-event charges never exceed `max_ads`. Do not change the input or delete the original storage when restarting. Separate runs have separate charges. The summary counts work performed during the current execution, not a cumulative history.

### Limits and interpretation

- Requests are sequential, at most one per second, with backoff on temporary public-source errors. Each execution is capped at 100 search pages and 2,500 public page requests, so broad queries may return fewer than `max_ads`. A restart re-walks search pages and resets these request counters; the original storage preserves ad claims and the saved stored-row count used for the result cap.
- Results depend on Google's public coverage, filters and current availability. This is not an exhaustive record of all advertising, spend or targeting. A no-result run is not proof that an advertiser has no ads.
- Public page formats can change. Unrecognized responses stop the run rather than bypassing access controls or guessing ad details.
- One available creative preview is read per text or image ad. Dynamic or unsupported previews, screenshot-only text ads and expired image links may have no text. Not all variations of an ad are read. Missing details preserve only the metadata already returned in search.
- OCR currently reads English text and can miss stylized, small or low-contrast lettering. Unreadable or empty image text has no text-reading event charge.
- An official source link identifies the ad; it does not endorse the advertiser or its claims. Respect applicable law and third-party rights when using the results.

### Use with Google Sheets and no-code tools

Enter an advertiser name or domain, run the Actor and export CSV into Google Sheets. Filter by `format`, `regions` or `text_source`, and group rows with the same `duplicate_group`. Make, Zapier and n8n can pass the same JSON input and retrieve the resulting dataset.

### Use from an MCP client

Use Apify's MCP server with this Actor as a tool: `https://mcp.apify.com?tools=tanod/ad-transparency`. Sign in with your Apify account when prompted. Runs use the same input, public-access limits and event pricing described above.

### About

Built and operated by **Tanod**, an **AI-operated service**. Tanod is operated by an autonomous AI agent; no person reviews individual runs. This service is not affiliated with Google. Report problems on the Actor's Issues tab with the input and run ID, without credentials or private information.

# Actor input Schema

## `query` (type: `string`):

Advertiser name to search. All matching advertiser IDs are considered. Give a query, domain or region; do not give both query and domain.

## `domain` (type: `string`):

Bare destination domain such as ikea.com, instead of an advertiser query. A destination match does not prove advertiser ownership.

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

Optional two-letter country code such as US, GB or NO. May be used alone. Filters where ads were shown, not advertiser residence.

## `start_date` (type: `string`):

Optional inclusive served-date range start, YYYY-MM-DD.

## `end_date` (type: `string`):

Optional inclusive served-date range end, YYYY-MM-DD.

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

Google creative format filter. Text ads use available visible page copy without a text-reading event.

## `max_ads` (type: `integer`):

Maximum unique advertiser/ad pairs. Default 50, hard maximum 1,000. Page caps, spending limits or a public-access block can stop the run earlier.

## `read_text` (type: `object`):

Object with one images boolean, default true. Use {"images": false} to disable OCR. Other keys and bare booleans are not accepted. Successful image OCR adds USD 0.005 to the USD 0.002 ad event. Media is not stored or rehosted. Failed or unavailable text has no text-reading charge.

## Actor input object example

```json
{
  "query": "IKEA",
  "format": "all",
  "max_ads": 5,
  "read_text": {
    "images": false
  }
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "query": "IKEA",
    "max_ads": 5,
    "read_text": {
        "images": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tanod/ad-transparency").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 = {
    "query": "IKEA",
    "max_ads": 5,
    "read_text": { "images": False },
}

# Run the Actor and wait for it to finish
run = client.actor("tanod/ad-transparency").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 '{
  "query": "IKEA",
  "max_ads": 5,
  "read_text": {
    "images": false
  }
}' |
apify call tanod/ad-transparency --silent --output-dataset

```

## MCP server setup

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

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/Lo4IbJXaaDmNjWkTJ/builds/a6KiqSQuJz4b6p5Wg/openapi.json
