Google Ads Transparency Center & Competitor Ad Scraper avatar

Google Ads Transparency Center & Competitor Ad Scraper

Pricing

$0.0015 / ad creative

Go to Apify Store
Google Ads Transparency Center & Competitor Ad Scraper

Google Ads Transparency Center & Competitor Ad Scraper

Every ad an advertiser is running, from Google's Ads Transparency Center — with first and last shown dates, the creative image and the rich-media preview URL.

Pricing

$0.0015 / ad creative

Rating

0.0

(0)

Developer

ScrapeBench

ScrapeBench

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

Categories

Share

Pain points

  • The Ads Transparency Center publishes no API. Its own front end talks to an internal RPC whose fields are numbered rather than named, so every field has to be identified by hand before any of it can be read.
  • Google throttles that endpoint per IP and answers a spent one with a redirect to its /sorry/ page rather than an error — so an unproxied scraper returns an ordinary-looking empty result instead of saying it was refused.
  • An unrecognised region code is not rejected either: it comes back as an empty creative list, which reads exactly like 'this advertiser runs no ads in that country'.
  • Google states an advertiser's library size as a RANGE — 8,000 to 9,000 — never an exact number, so any tool that reports a single total is reporting something Google did not say.
  • Google states an advertiser's library size only for the region you asked about, so without carrying that figure on the row there is no way to tell whether a run holds all of an advertiser's ads or two per cent of them.
  • Google publishes no impression or spend figures anywhere in this service, so any tool offering them for a Google ad is not getting them from here.

What we solve

  • The RPC field map was read off the live service and is documented in the source, so the numbering is not folklore.
  • A refusal is caught by checking where the response landed, not just what it says, and returns a free marker row saying the run was blocked — never zero rows on a successful-looking run.
  • The proxy is on by default, because a per-IP throttle is invisible to every check that looks at the response body.
  • Region codes are validated before the request, against 48 countries verified one by one.
  • Both bounds of Google's stated library size ride on every row, so a capped run can be read against the source's own figure.
  • Creative image URL, rich-media preview URL, first shown and last shown are all returned.

Summary

Give it an advertiser domain (nike.com), a Google advertiser id (AR16735076323512287233) or a company name, and it returns one row per ad creative: advertiser id, verified advertiser name, domain, creative id, format, the creative's own hosted image URL or its rich-media preview URL, and the dates Google states the ad was first and last shown — which together give you how long a creative has been running, the usual proxy for whether it is working. Every row also carries Google's own stated size for that advertiser's library in that country, so you can always see what fraction of it a run covered. Reads any of 48 country libraries, all verified against the live service. Built for performance marketers auditing competitors' creative, agencies preparing pitches, and ad-intelligence tools that need the underlying data rather than a dashboard.

Who it's for

  • Performance marketers auditing what competitors are running and for how long
  • Agencies building competitive creative decks for pitches
  • Ad-intelligence and market-research tools needing the underlying rows
  • Brand and compliance teams checking who is advertising against their name

How to use

Set the input, run the actor, and collect results from the run's dataset (export to JSON/CSV/Excel, or pull via the Apify API). Example input:

{
"queries": [
"nike.com",
"geico.com"
],
"region": "US",
"maxResults": 200
}

See Inputs below for every available field.

What you get

One row per record:

FieldDescription
advertiser_idGoogle's own advertiser identifier (AR…) — stable across runs and regions
advertiser_nameThe verified advertiser name as Google publishes it
advertiser_domainThe advertiser's domain as stated on the creative
creative_idGoogle's identifier for this specific ad (CR…)
format_codeGoogle's own numeric format code, passed through unlabelled
creative_typeimage or rich_media — derived from what the payload contains, not from the format code
creative_image_urlGoogle's own hosted image for the ad, taken verbatim from the response
creative_preview_urlFor rich-media ads, Google's renderable preview URL
display_width / display_heightThe size the ad is displayed at, which is not the asset's own resolution
first_shown / last_shownWhen Google says the ad first and last ran, as ISO 8601 UTC. The gap between them is the creative's run length
days_runningWhole days between first and last shown, worked out for you — the usual proxy for whether a creative is performing
regionWhich country library the row came from
advertiser_creatives_low / _highGoogle's own stated range for the advertiser's library size in that region — it never publishes an exact count
matched_byHow the advertiser was resolved: domain, advertiser_id or name_search
advertiser_urlThe advertiser's Transparency Center page
source_urlThis creative's own page on the Transparency Center

Sample:

{
"source_url": "https://adstransparency.google.com/advertiser/AR16735076323512287233/creative/CR10365997061744099329?region=US",
"advertiser_id": "AR16735076323512287233",
"advertiser_name": "Nike, Inc.",
"advertiser_domain": "nike.com",
"creative_id": "CR10365997061744099329",
"format_code": 1,
"creative_type": "image",
"creative_image_url": "https://tpc.googlesyndication.com/archive/simgad/2588413232821661199",
"creative_preview_url": null,
"display_width": 348,
"display_height": 180,
"first_shown": "2023-11-16T22:59:37Z",
"last_shown": "2026-09-07T05:25:23Z",
"days_running": 1025,
"region": "US",
"advertiser_creatives_low": 8000,
"advertiser_creatives_high": 9000,
"matched_by": "domain",
"advertiser_url": "https://adstransparency.google.com/advertiser/AR16735076323512287233?region=US"
}

Inputs

FieldRequiredTypeDefaultDescription
querynostring"nike.com"An advertiser domain (nike.com), a Google advertiser id (AR16735076323512287233), or a company name to search for. Which of the three you gave is recorded on every row in 'matched_by'. Kept forever alongside 'Advertisers' — saved tasks and API callers send it.
queriesnoarray["nike.com", "geico.com"]Look up many advertisers in ONE run instead of starting a run per advertiser. Takes the same values as 'Advertiser'. Combined with it if you fill both, and duplicates are removed. The result cap applies PER advertiser, so one large advertiser cannot use up the run and leave the others looking like they run no ads.
regionnostring"US"The country whose ad library to read, as an ISO country code — US, GB, DE, FR, JP, BR and so on. This is not cosmetic: Google keeps a separate library per country and the same advertiser has very different volumes in each. An unrecognised code is refused up front, because Google answers one with an empty list rather than an error, which would read as 'this advertiser runs no ads here'.
maxResultsnointeger200Cap on creatives returned PER advertiser, not per run. Large advertisers run enormous libraries — Google stated 8,000-9,000 creatives for one of them in the US alone on 2026-09-07 — so this is your spend control. Every row carries Google's own stated range for that advertiser and region, so a cap can never read as completeness.
proxyConfigurationnoobject{"useApifyProxy": true}ON by default, and it should stay on. Google throttles the Transparency Center per IP and answers a spent one with a redirect to its /sorry/ page rather than an error — measured from an ordinary connection during development. A shared cloud IP is often already spent, so running without a proxy is the most likely reason for a run that returns nothing.

Pricing (Pay Per Event)

You pay per result (creative) — no charge for empty runs. Example: 1000 ad creatives at $0.0015/result$1.50.

The default input returns up to 200 creatives per advertiser, so a two-advertiser default run is 400 creatives and costs $0.60. You are charged per creative returned, not per advertiser submitted. Apify platform usage (compute) is billed separately per your plan.

Use cases

  • Competitor creative audit — pull every ad a rival is running in your market with the dates each has been live.
  • Creative longevity — sort by first shown against last shown to see which creatives a competitor keeps running and which they dropped in a week.
  • Cross-market comparison — run the same advertiser across several country libraries to see where their volume actually is.
  • New-creative alerts — run on a schedule and watch for creative ids that were not in the previous run.

Why this actor

  • Returns the creative preview URL, the creative image URL, and both the first-shown and last-shown dates, with days_running worked out for you.
  • Carries Google's own stated library range for the advertiser and region on every row, so 'is this all of them?' is answerable rather than assumed.
  • Catches Google's per-IP throttle by checking where the response landed, and says so in a free unbilled row instead of returning an ordinary-looking empty result.
  • Validates the region before asking, because Google answers an unknown one with an empty list that reads as 'no ads here'.
  • Says on every row how the advertiser was resolved — domain, advertiser id, or name search — so a wrong-looking advertiser can be traced.
  • $1.50 per 1,000 creatives, against $0.12 to $200 across this category on the Store (2026-09-07).

Limitations & updates

Reads what Google's Ads Transparency Center publishes and nothing beyond it. There are no impressions, no spend, no click and no audience figures in this source, for any advertiser — if you need those for Google ads they do not exist here. Advertiser library sizes are stated by Google only as bucketed ranges, never as exact counts. Political ads carry extra disclosures on Google's own site that this Actor does not currently return. 48 country libraries are supported and were each verified on 2026-09-07; other regions may work with a numeric Google region code but are not verified. Creative image URLs are Google's own hosted assets and serve to browser-style clients only. One undocumented integer on each creative row is deliberately not returned rather than guessed at. Google throttles this endpoint per IP, so the proxy is on by default and a throttled run returns a free marker row rather than an empty result. Data is whatever the Transparency Center shows at the moment of the run.

FAQ

Does this give me impressions, spend or reach?

No, and neither can anything else reading this source: Google publishes no impression count, no spend figure and no audience reach anywhere in the Ads Transparency Center for a normal commercial ad. What it does publish is which creatives ran, in which country, and the dates each was first and last shown — and the gap between those two dates is the most useful signal available here, because an advertiser who keeps a creative live for months is telling you it works. The creative row does carry one undocumented integer that this Actor deliberately does not return; it is between 5 and 1,737 across the ads sampled, it matches neither the image area nor its dimensions, and shipping it labelled 'impressions' would be inventing a number.

Why is the proxy on by default when other Actors here have it off?

Because Google throttles this endpoint per IP, and it does so invisibly. A spent IP does not get an error or a 429 — the request is redirected to google.com/sorry/, so a scraper that only checks the response body sees a perfectly valid page and reports an empty result. That happened during development from an ordinary connection and persisted for several minutes. Shared cloud IPs are frequently already spent by someone else, so an unproxied run is the likeliest reason for a run that comes back with nothing. This Actor also checks where each response landed rather than only what it contains, so when it is throttled it says so in a free row instead of pretending the advertiser has no ads.

Why does a row say 8,000 to 9,000 instead of a number?

Because that is what Google says. It publishes an advertiser's library size as a bucketed range and never as an exact count, so both bounds are carried on every row rather than one of them being presented as a total. It is the only honest way to answer 'did I get all of them?': compare your row count against the range. It also varies enormously by country — on 2026-09-07 the same advertiser was stated at 8,000-9,000 creatives in the US, 20,000-30,000 in Germany and 400-500 in Japan.

Which countries can I search?

48 were verified one by one against the live service on 2026-09-07 and every one returned creatives: the US, Canada, Mexico, Brazil, Argentina, Chile, Colombia, the UK, Ireland, Germany, France, Italy, Spain, Portugal, the Netherlands, Belgium, Switzerland, Austria, Sweden, Norway, Denmark, Finland, Poland, Czechia, Greece, Hungary, Romania, Ukraine, Turkey, Israel, the UAE, Saudi Arabia, Egypt, South Africa, Nigeria, India, Japan, South Korea, Singapore, Malaysia, Thailand, Indonesia, the Philippines, Vietnam, Taiwan, Hong Kong, Australia and New Zealand. Give a standard two-letter country code. Anything unrecognised is refused with a free row naming the valid ones, rather than being sent to Google and coming back as a misleading empty list.

Can I search by company name instead of by domain?

Yes. A value that is not a domain and not an advertiser id is put through Google's own advertiser search, and the best match is used — the row then says matched_by: name_search so you know a resolution step happened. Names are ambiguous, though: searching 'Nike' returns NIKE SRL in Italy and several unrelated advertisers alongside Nike, Inc., and the run log lists how many candidates were considered. Where you know the domain or the advertiser id, use it — those are exact.

Why can't I fetch the creative image with curl?

Google serves those images to browsers and refuses plain command-line clients — a request with default headers hangs rather than returning an error. The URL is real and is taken verbatim from Google's own response, not assembled by this Actor; one checked on 2026-09-07 rendered as a 696×458 image in a browser. Use browser-style headers if you are downloading them programmatically, or just open one in a tab. Note also that display_width and display_height are the size the ad is shown at, not the asset's own resolution — that same creative is served as 348×489 in the ad slot.

What happens if Google changes the RPC?

You get a free, unbilled row with notice_type 'source_changed'. This matters more here than for a normal scraper: the endpoint identifies its fields by NUMBER rather than by name, so if Google renumbers them the response is still valid JSON of the right shape and every value silently lands in the wrong column. The Actor checks that the rows it got still carry a creative id where one belongs, and stops rather than emitting a dataset that looks fine and is wrong.

How many ads can one run return?

As many as the advertiser has, in pages of 40, with the cap applied per advertiser rather than per run — so a batch of ten cannot be used up by the first large one. Libraries get big: Google stated 8,000-9,000 creatives for a single advertiser in the US alone. Set the cap to control spend, and read it against the advertiser_creatives range on the rows to see what fraction you covered.

Is this the same data as the Transparency Center website?

Yes — it is the same service, read through the same internal endpoint the website's own front end calls, so what you get is what the site would show for that advertiser and country. Nothing here is inferred, modelled or enriched from anywhere else, and the source page for every single row is on the row so you can open it and check.

Do I need a Google account or an Ads account?

No. The Transparency Center is public and this Actor sends no credentials of any kind. It reads only what Google publishes about ads that ran; there is no account data, no audience data and no personal information involved.

Which actor to choose

Other Actors in the portfolio that pair with this one:

  • Shopify Store Intel — The advertisers you are tracking run Shopify storefronts and you want the store side too.
  • Business Email & Phone Contact Details Scraper — You have the advertiser domains and now need contact details for outreach.

Guides & use cases

Written up on scrapebench.dev — the bench that runs and verifies this actor against the live source every night:

More actors, coverage and nightly verification results: scrapebench.dev

Works with AI assistants (MCP)

Callable as an MCP tool, so Claude, Cursor, VS Code Copilot and other MCP clients can run it directly. Grab the config from the MCP tab on this page — Apify hosts the server and keeps that snippet current, and OAuth signs you in on first connect, so no API token goes in your config file.

Then just ask:

"Show me every ad nike.com is running in the US right now, with the dates each was first and last shown."

An advertiser can be a domain, a Google advertiser id (AR…), or a company name — a name goes through Google's own advertiser search first, and the row says so in matched_by. Region is a two-letter country code; each country is a separate ad library with very different volumes. Runs started this way bill exactly like any other run.