Google Ads Transparency Center Scraper avatar

Google Ads Transparency Center Scraper

Pricing

Pay per event

Go to Apify Store
Google Ads Transparency Center Scraper

Google Ads Transparency Center Scraper

Get the ads an advertiser runs on Google, up to 5,000 per input line. Search by name, ID or domain and filter by region, date, format and platform. Each ad has format, first/last shown, days shown, image or preview URL, text where shown, landing domain. Optional per-region dates and impressions.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Michael Lip

Michael Lip

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 hours ago

Last modified

Security

Limited permissions

Share

Get the ads any advertiser runs on Google as clean JSON rows, from $0.0012 per ad, by name, advertiser ID or website domain.

The data comes from the public Google Ads Transparency Center. The Actor reads the same JSON endpoints that page uses, with no browser and no login. In a test run on 2026-11-05 it saved 50 Nike ads with 2 requests to Google.

Use it to see what a competitor is running and for how long, to collect creatives from a whole category, to watch an advertiser on a schedule, or to find resellers and affiliates that send ad traffic to your domain.

What you get

One dataset row per ad. The first group of fields is always there. The second group is filled only when you turn on Include ad details.

FieldTypeMeaning
advertiserIdstringGoogle's advertiser ID (AR...)
advertiserNamestring or nullThe advertiser's legal name as Google lists it
creativeIdstringGoogle's ID of the ad (CR...)
formatstring or nulltext, image or video
firstShown, lastShownISO timestamp (UTC) or nullWhen Google first and last showed the ad. For a running ad, lastShown is recent
daysShownnumber or nullDays the ad was shown, as Google reports it
landingDomainstring or nullDomain the ad sends traffic to, when Google lists it
imageUrl, width, heightstring, number or nullImage of the ad. Google shows many text ads as an image, so text ads often have one too
previewUrlstring or nullGoogle's preview script (content.js) for ads without a direct image, such as most video ads
headline, description, displayUrlstring or nullText ad copy, only when Google puts it as plain fields in the preview URL. On real ads this is rare, so expect null most of the time
adUrlstringThe ad's page in the Ads Transparency Center
regionFilter, platformFilterstringThe region and platform you searched with
query, queryTypestringThe input line that found the ad, and how it was read (name, id, creative or domain)
scrapedAtISO timestampWhen the row was saved
regionsarray or nullDetails only. One entry per region where the ad ran, with region (2-letter code, null for areas smaller than a country), regionId, firstShown, lastShown, impressionsMin, impressionsMax. Google adds first dates and impression ranges mainly for EU regions
platformsarray or nullDetails only. Platforms in Google's reach breakdown, such as SEARCH, SHOPPING, YOUTUBE. Often empty for ads shown only outside the EU
impressionsMin, impressionsMaxnumber or nullDetails only. Google's total impression range. impressionsMin is null when Google gives only an upper bound such as "up to 1,000"
variationCount, imageUrlsnumber, array or nullDetails only. Number of creative variations and their image URLs
detailErrorstring or nullDetails only. Why the detail lookup failed. The ad is still saved and the lookup isn't charged

The dataset has two views in Console, Ads (with image previews) and Regions and reach.

Each run also writes a report to the key-value store record OUTPUT. It has one entry per input line with the advertiser it resolved to, ads saved, Google's estimate of matching ads (totalEstimate, for example 50000 to 60000 for nike.com) and the error if the line failed.

Example output

This row is real. It comes from run c5A1NgmRygXZXu9Z8 on 2026-11-05 with input nike.com, region anywhere and details off.

{
"advertiserId": "AR16735076323512287233",
"advertiserName": "Nike, Inc.",
"creativeId": "CR06692121344286392321",
"format": "text",
"firstShown": "2023-11-16T21:49:06.000Z",
"lastShown": "2026-11-05T11:22:35.000Z",
"daysShown": 885,
"landingDomain": "nike.com",
"imageUrl": "https://tpc.googlesyndication.com/archive/simgad/11274417169112350865",
"previewUrl": null,
"width": 348,
"height": 305,
"headline": null,
"description": null,
"displayUrl": null,
"adUrl": "https://adstransparency.google.com/advertiser/AR16735076323512287233/creative/CR06692121344286392321",
"regionFilter": "anywhere",
"platformFilter": "all",
"query": "nike.com",
"queryType": "domain",
"scrapedAt": "2026-11-05T11:40:37.956Z"
}

With Include ad details on, each row gets these extra fields. They're from a test run on 2026-11-05 with input nike.com, region DE, for ad CR10056063563010670593 of Nike Retail BV.

{
"regions": [
{ "region": "NL", "regionId": 2528, "firstShown": "2023-03-12", "lastShown": "2024-02-23", "impressionsMin": null, "impressionsMax": 1000 },
{ "region": "PL", "regionId": 2616, "firstShown": "2023-03-01", "lastShown": "2026-11-05", "impressionsMin": 5000000, "impressionsMax": 6000000 }
],
"platforms": ["SEARCH", "SHOPPING", "YOUTUBE"],
"impressionsMin": 5000000,
"impressionsMax": 6000000,
"variationCount": 3,
"imageUrls": [],
"detailError": null
}

Pricing

You pay per result. There's no subscription and no charge for compute.

EventPriceWhen it's charged
ad$0.0012Each ad row saved, after filters and duplicate removal
ad-detail$0.002Each successful detail lookup, only with Include ad details on

Some worked examples.

JobMathCost
The prefilled first run (20 ads)20 × $0.0012$0.024
1,000 ads1,000 × $0.0012$1.20
1,000 ads with details1,000 × ($0.0012 + $0.002)$3.20
10 competitors, 200 ads each2,000 × $0.0012$2.40

You aren't charged for advertisers that aren't found or fail, for the same ad found by two input lines, for ads your filters drop, or for failed detail lookups. Set a maximum cost per run (maxTotalChargeUsd in the API) and the Actor stops before a row it can't pay for in full, then says so in the run report.

Quick start in Console

  1. Open the Actor and click Start. The input is prefilled with nike.com, region US and 20 ads, so the first run costs about $0.02 and takes a few seconds.
  2. Open the Ads view of the dataset, or export it as JSON, CSV or Excel.
  3. Swap in your own advertisers, one per line, and raise "Max ads per advertiser".

Use it from the API

This call runs the Actor and returns the rows in one response. Sync calls wait up to 300 seconds, so for big jobs start an async run and read the dataset when it finishes.

curl -X POST "https://api.apify.com/v2/acts/zovo-one~google-ads-transparency-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"advertisers": ["nike.com", "AR16735076323512287233"],
"region": "US",
"dateFrom": "90 days",
"format": "video",
"maxAdsPerAdvertiser": 100,
"includeDetails": false
}'

Use it from Python

$pip install apify-client
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("zovo-one/google-ads-transparency-scraper").call(run_input={
"advertisers": ["nike.com", "Adidas AG"],
"region": "US",
"maxAdsPerAdvertiser": 100,
})
for ad in client.dataset(run["defaultDatasetId"]).iterate_items():
print(ad["advertiserName"], ad["format"], ad["daysShown"], ad["adUrl"])

Use it from Node.js

$npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('zovo-one/google-ads-transparency-scraper').call({
advertisers: ['nike.com', 'Adidas AG'],
region: 'US',
maxAdsPerAdvertiser: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);

Use it from AI agents (MCP)

AI agents can call this Actor as a tool through the Apify MCP server. Each client adds servers in its own way. VS Code and Claude Desktop, for example, don't take the JSON below as written. For your client, open the MCP configurator, pick this Actor and copy the setup it gives you. A client that reads an mcpServers block with a url key, such as Cursor, can use this config.

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=zovo-one/google-ads-transparency-scraper"
}
}
}

On the first connection your browser opens so you can sign in to Apify. To skip that, add the header "Authorization": "Bearer <APIFY_TOKEN>" to the server config.

Without the tools parameter, an agent can still find the Actor with search-actors, run it with call-actor, and read the rows with get-dataset-items.

Here's a prompt that works well.

Use the Google Ads Transparency scraper to get up to 50 video ads that nike.com
ran in the US in the last 90 days. Sort them by daysShown and tell me which
5 ran the longest, with their adUrl.

Input

FieldWhat it doesExample
advertisersRequired. One per line, up to 100 advertisers: a name, an advertiser ID (AR...), a domain, or an Ads Transparency Center URL["nike.com", "AR16735076323512287233", "Adidas AG"]
region2-letter country code, or anywhere. Default anywhere"US"
dateFromOnly ads shown on or after this date. YYYY-MM-DD or relative (30 days, 3 months)"90 days"
dateToOnly ads shown on or before this date. Needs dateFrom. Empty means today"2026-06-30"
formatall, text, image or video. Default all"video"
platformall, SEARCH, YOUTUBE, SHOPPING, MAPS or PLAY. Default all"YOUTUBE"
maxAdsPerAdvertiserAds to save per input line, 1 to 5,000. Default 50200
includeDetailsAdds regions, per-region dates, impression ranges, platforms and creative variations. One extra request per ad, charged as ad-detail. Default offtrue
proxyConfigurationApify Proxy is on by default. Pick the RESIDENTIAL group if Google blocks the run{"useApifyProxy": true}
maxConcurrencyAdvertisers read in parallel, 1 to 3. Default 22

How each kind of input line is read.

  • An advertiser ID or a Transparency Center advertiser URL (https://adstransparency.google.com/advertiser/AR...) reads that advertiser. It's the most precise choice.
  • A domain (nike.com, https://www.nike.com/us) returns ads from each advertiser that sends traffic to it. For nike.com that's "Nike, Inc." plus Nike's regional companies, and it can include resellers. Unicode domains are sent in their ASCII form.
  • A name must match an advertiser name exactly, ignoring case, accents, punctuation, & versus and, a leading "The" and up to two company suffixes such as Inc or GmbH. So nike inc matches "Nike, Inc.", but Coca doesn't match "Coca-Cola". When several advertisers share the name, the one with the most ads is read and the rest are listed in OUTPUT. When nothing matches, the run report lists the closest names with their IDs.
  • An ad URL (.../advertiser/AR.../creative/CR...) saves only that one ad, if it's among the first 400 ads Google lists for that advertiser with your filters.

A line that can't be read is reported in OUTPUT and the other lines still run.

Limits

  • Up to 100 advertisers and 1,000 input lines per run. Split bigger lists into several runs.
  • Up to 5,000 ads per input line. A domain or advertiser with more matching ads returns the same first 5,000 each time, so narrow the region, format, platform or dates to reach others.
  • Google's date filter covers about the last 12 months. A range that ends before that is rejected with an explanation. For older ads, leave the dates empty and use firstShown and lastShown.

The Actor reads 40 ads per request, spaced at least 0.3 seconds apart with Apify Proxy and 1 second without. Details add one request per ad. A blocked request is retried up to 5 times with a new proxy session.

Two breakers keep a bad run short. After 3 failed detail lookups in a row, the rest of the ads are saved without details. Those rows still cost the ad price, but not ad-detail. After 3 advertisers in a row fail, the run stops and marks the remaining lines as skipped.

Near the run timeout the Actor stops, keeps what it saved and still writes OUTPUT. If the platform restarts a run, ads already saved are skipped and not charged again. Ads aren't remembered between runs, though. For monitoring, drop creativeId values you've already seen on your side.

There's no OCR and no video download. When Google shows a text ad only as an image, the text is in imageUrl. Video ads come with previewUrl.

FAQ

Why did a domain return ads from several advertisers

A domain search returns each advertiser that sends traffic to it. That can be the brand, its regional companies, resellers or affiliates. Filter on advertiserId, or use the advertiser ID as input.

A name wasn't found, but the advertiser exists

Names have to match exactly, apart from case, accents, punctuation and company suffixes. Open the OUTPUT record. It lists the closest names with their IDs, so you can rerun with the ID or the domain.

How do the date filters work

An ad is kept when it was shown at any time in your range. So it ended on or after "Shown since" and started on or before "Shown until". All dates are UTC.

How do I watch a competitor every day

Schedule the Actor in Console with "Shown since" set to 2 days. Each run returns the ads that are new or still running, and you pay only for those rows.

What if Google blocks the run

If no ad was saved yet, the run fails with a clear message and nothing is charged. If the block comes partway through, the ads saved before it are kept and charged, and OUTPUT lists the advertisers that failed. Open the Proxy setting and pick the RESIDENTIAL group, then run it again.

The Actor reads data Google publishes for public transparency, with no login. It collects advertiser names and ads, not data about people. You're responsible for how you use the data and for following Google's terms in your use case. This Actor isn't affiliated with, endorsed by or sponsored by Google.

Support

Open an issue in the Actor's Issues tab with the run ID and your input. The Transparency Center endpoints aren't an official API and Google can change them. When Google answers in a shape the Actor doesn't know, that advertiser gets an error in the run report instead of an empty result, so please report it and it'll get fixed.