# Google Ads Transparency Center Scraper & Google Ads Library (`hiver/google-ads-transparency-scraper`) Actor

Google Ads Transparency Center scraper (Google Ads Library) by domain, advertiser name or ID: run dates, landing domain, image, countries by name; ad copy when Google's preview serves it. Bench 10-09: 8 of 11 hand-checked facts vs 5 of 11; Google refused 1 of 4 runs. $0.75/1k ads. Unofficial.

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

## Pricing

from $0.64 / 1,000 ad returneds

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 Center Scraper & Google Ads Library: ad copy and countries by company

**See the ads any company runs on Google, and where they ran, by country name.** The Google Ads Transparency Center is Google's public ad library. Give this Actor company domains, advertiser names or advertiser IDs and get one row per ad: advertiser, first and last shown dates, landing domain, format, image URL, and the countries it ran in as names (`United States`), not just codes. Headline, body text and YouTube ID are added when Google's preview serves them; that is best effort, and each row's `contentStatus` says whether the creative content is complete or partial.

**$0.75 per 1,000 ads** on the free Apify plan, less on paid plans. No start fee. Failed and empty queries return no ad and are not charged.

Unofficial. Not affiliated with, endorsed by or sponsored by Google. Reads the public Ads Transparency Center without a login. No spend estimates, invented copy, OCR or private targeting data.

### Measured against the most-used alternative

Our board's Lab bench gives this Actor and the most-used Google Ads Transparency Actor on the Apify Store (about 900 monthly users) the same four jobs in the US: nike.com text ads, nike.com video ads, notion.so text ads, and nike.com text ads in metadata-only mode (`includeAdCopy: false`). It checks the answers against 11 facts verified by hand in the Ads Transparency Center (headline, body text, landing domain, country names, image, YouTube ID). Latest bench run 2026-10-09 22:17 UTC, build 0.1.26; the bench verdict was **FAIL**, because this Actor finished fewer runs with results:

| | This Actor | Most-used alternative |
|---|---|---|
| Runs finished with ads | 3 of 4 | **4 of 4** |
| Hand-checked facts matched | **8 of 11** | 5 of 11 |
| nike.com text ads: headline, body text, landing domain, country name | **4 of 4** | 1 of 4 |
| nike.com video ads: YouTube ID | **1 of 1** | 0 of 1 |
| notion.so text ads: images, country name | **3 of 3** | 2 of 3 |
| nike.com text ads, metadata-only mode | 0 of 3 (Google refused the first request) | **2 of 3** |
| Fields filled | **64%** | 59% |
| Average run time per job | 44.5 s | 2.7 s |
| Price per 1,000 ads in these runs (bench account on Apify's Starter plan) | **$0.72** | $2.00 |

**Refusal risk, plainly:** in that bench Google answered one of this Actor's four runs with a redirect to google.com on the first request, and the run stopped with 0 ads and an uncharged error row. This Actor does not retry through other IPs or identities, so expect some runs to stop like that; re-run later. When it does get through, it returned more of the hand-checked ad copy than the alternative. The alternative finishes much faster. Four jobs are a small sample; earlier benches on builds 0.1.16 to 0.1.25 are not repeated here because the latest build did not reproduce them.

Runs use one Apify datacenter proxy session by default; if Google refuses a request, the run stops there (see "What happens on a block?").

### Input

- `queries`: domains (`nike.com`), names (`Nike`) or exact advertiser IDs (`AR16735076323512287233`). A name chooses the first matching advertiser unless `maxAdvertisersPerName` is increased.
- `maxAdsPerQuery`: 1–10,000, default 100.
- `regions`: optional ISO two-letter countries, such as `US`, `GB`, `DE`. These are converted to Google's geo-target IDs, not ISO numeric codes.
- `format`: `any`, `text`, `image`, `video`.
- `startDate`, `endDate`: optional YYYY-MM-DD filters. Invalid dates are ignored individually; reversed ranges are ignored. Unknown region codes are skipped while valid codes remain. The status message explains each correction and what to try. These input-correction runs finish successfully and all returned rows are uncharged. A truly empty `queries` input still fails.
- `includeRegions`: default true. Turning this off omits region data, not creative extraction.
- `includeAdCopy: true` (default, full mode): best-effort headline, body and video extraction with paced preview GETs; on the 2026-10-09 22:17 UTC bench (build 0.1.26) the three full-mode jobs took 12.6 s to 97.5 s.
- `includeAdCopy: false` (metadata-only mode): no preview GETs or copy extraction; keeps list images, landing domains and country names from the detail RPC, with `contentStatus: skipped_by_input`. In the same bench Google refused this mode's run on its first request (0 ads), so no speed or completeness figure is claimed for it.
- `requestDelaySecs`: optional; omitted means a 2-second post-response pause in full mode or serialized RPC starts at least 1 second apart in fast mode (response time counts toward that interval). Explicit values retain a post-response pause in either mode, minimum 1 second. Full-mode preview GETs wait at least 5 seconds before contact; successful exact-URL previews are reused within the run (up to 16 cached). Requests are serialized. Uncharged error rows include `sourceRefusal` with request stage, HTTP status and redirect host only (no redirect query).
- `proxyConfiguration`: defaults to Apify datacenter (auto group), one fixed session for Google RPCs only; previews stay direct, unavailable proxy falls back to direct before requests, and a Google refusal stops the run without retry or routing changes.

```json
{"queries": ["nike.com"], "regions": ["US"], "format": "text", "maxAdsPerQuery": 10}
```

### Output

Identifiers and metadata: `advertiserId`, `advertiserName`, `adId`, `format`, `firstShown`, `lastShown`, `landingDomain`, `variationCount`, `adUrl`, `previewUrl`.

Creative content:

- `headline`, `text`: ad copy from native creative HTML or Google's preview content.js (text-ad data-p records and semantic headline/description anchors). The advertiser name or landing domain is never substituted for a missing headline.
- `imageUrl`: an image exposed in creative HTML or the preview source, not a logo or tracking pixel.
- `videoUrl`, `youtubeId`: filled only when Google's preview exposes a YouTube ID. Video files are not downloaded.
- `contentSources`: per-field source (`creative_rpc_html`, `preview_content_js`, etc.).
- `contentUnavailableReasons`: why a content field is null: source does not expose it, format is not video, fetch/parse failure, or unsupported preview URL.
- `contentStatus`: `skipped_by_input` in metadata-only mode (`includeAdCopy: false`); otherwise `complete` when text ads have headline and body, image ads have an image, or video ads have a video URL plus headline and body; otherwise `partial`. Text ads that Google serves only as a picture keep their image and null copy with a reason. This is a per-row extraction indicator, not a bench score or a claim that all variations were retrieved.

Regions:

- `regions`: ISO country codes, such as `GB`, `US`.
- `regionNames`: human-readable country names, such as `United Kingdom`, `United States`.
- `regionIds`: the original Google geo-target IDs, such as `2826`, `2840`.
- `regionDetails`: original ID, code, name and per-region dates. Unknown IDs stay in `regionIds` and have a null name with a reason. Missing dates stay null.

Country names use Google's [geo-target country CSV](https://developers.google.com/google-ads/api/data/geotargets), version 2026-08-12.

Empty queries and failures produce an uncharged `success: false` row with an `error`. A metadata row with unavailable creative content is still a charged ad; check `contentStatus` and the null reasons before relying on ad copy.

### Uses

- Competitor ad research: which ads a rival runs on Google, in which countries and since when, with the ad copy when Google's preview serves it.
- Agency pitches and audits: pull a prospect's live ads and landing domains before the call.
- Monitoring: run it per competitor on a schedule and compare `firstShown` dates to spot new ads.

### Creative limitations

Google sometimes serves only a flattened screenshot or a preview with no machine-readable copy. This Actor does not transcribe text from images. It downloads public `content.js` with a plain HTTP GET and decodes the HTML passed to Google's preview insertion function without executing JavaScript or starting a browser. It parses text-ad data-p records and headline/description anchors. It does not visit advertiser landing pages or stream videos. Up to three available preview representations are tried per ad; results are the first available fields, not an exhaustive export of every variation. Source changes, expired previews and rate limits can leave fields null.

A domain query includes ads pointing to that domain from any advertiser, including agencies or resellers. Use an advertiser ID for one legal entity. Dates and counts are Google's public values on the day of the run.

### Pricing

Pay per event, one `ad-result` event per ad returned:

| Ads returned, per 1,000 | Free | Bronze | Silver | Gold |
|---|---:|---:|---:|---:|
| Price | $0.75 | $0.72 | $0.68 | $0.64 |

There is no run fee, start event or separate detail charge. A ten-ad billable run on the free plan costs $0.00750. Empty results and input-correction runs cost $0. Set `maxAdsPerQuery` to cap spending. Apify platform usage is included in these prices.

### FAQ

#### Is every ad or variation returned?

Google determines the public catalog. Search filters and public previews can vary over time. The cap applies per query; full variation export is not supported.

#### Is a preview link the same as ad text?

No. `previewUrl` is only the source preview. `headline` and `text` contain extracted ad copy; when extraction fails they are null with reasons.

#### Does it get video ad content?

Best effort. Video rows carry the YouTube ID and link, headline and body text only when Google serves the preview. If Google rate-limits the preview, the run stops cleanly and the rows are marked `contentStatus: partial`, with a reason in `contentUnavailableReasons`. Fetched video rows keep advertiser, dates and landing domain. Video files are not downloaded.

#### What happens on a block?

The run stops with an uncharged error row and no further source requests. Already fetched ads are preserved, including the remaining search-page metadata when an optional preview or detail request refuses access; these valid ad rows are charged even when content is partial, unless input corrections made the run uncharged. `enrichmentUnavailableReason` explains the refusal. It does not follow refused redirects, solve CAPTCHAs, rotate IPs or change identity to bypass the block. Re-run later with a lower request rate.

#### How do I report missing content?

Open an issue on this Actor's Issues tab with the input, run link and `adId`. Include `contentUnavailableReasons` if present.

# Actor input Schema

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

One per line. A domain (`nike.com`) returns ads that point to that domain. A name (`Nike`) uses the best-matching advertiser. An advertiser ID (`AR16735076323512287233`) returns that exact advertiser.

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

Stop after this many ads per query (1 to 10,000). You pay per ad returned, so this is your cost cap.

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

Only ads shown in these countries. Two-letter ISO codes such as `US`, `GB`, `DE`. Empty means everywhere.

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

Filter by ad format.

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

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

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

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

## `includeRegions` (type: `boolean`):

Include country codes, human-readable country names and region-level dates. Creative extraction always uses the same detail request even when regions are off.

## `includeAdCopy` (type: `boolean`):

True (default): full mode, best-effort ad copy/video with paced preview GETs; measured 41.2 s for 5 nike.com US text ads. False: metadata-only fast mode, no preview GETs; keeps list images, landing domains and detail-RPC country names; contentStatus is skipped_by_input; measured 7.1 s for the same 5-ad job, all 3 metadata checks matched. Bench 2026-10-09 21:34 UTC, build 0.1.25; single-job measurements, not speed guarantees.

## `maxAdvertisersPerName` (type: `integer`):

When a query is a company name, how many matching advertisers to include (1 to 10). Large brands have several legal entities.

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

Default Apify datacenter proxy with one fixed session for Google RPCs; previews stay direct. If proxy is unavailable before requests, runs direct. A refusal stops the run without retries or route changes.

## `requestDelaySecs` (type: `number`):

Optional RPC pause: omitted means a 2-second post-response pause in full mode, or serialized starts at least 1 second apart when includeAdCopy is false (response time counts toward the interval). Explicit values retain post-response pauses in either mode (minimum 1). Full-mode preview GETs wait at least 5 seconds; exact-URL successes are reused. Refusals stop without retry or alternate routing.

## Actor input object example

```json
{
  "queries": [
    "nike.com",
    "notion.so",
    "AR16735076323512287233"
  ],
  "maxAdsPerQuery": 3,
  "regions": [],
  "format": "any",
  "includeRegions": true,
  "includeAdCopy": true,
  "maxAdvertisersPerName": 1,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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": [
        "nike.com",
        "notion.so",
        "AR16735076323512287233"
    ],
    "maxAdsPerQuery": 3,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("hiver/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",
        "notion.so",
        "AR16735076323512287233",
    ],
    "maxAdsPerQuery": 3,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("hiver/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",
    "notion.so",
    "AR16735076323512287233"
  ],
  "maxAdsPerQuery": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call hiver/google-ads-transparency-scraper --silent --output-dataset

```

## MCP server setup

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