Google Ads Transparency Scraper - no silent empty results
Pricing
from $2.00 / 1,000 results
Google Ads Transparency Scraper - no silent empty results
Scrape every ad an advertiser is running from Google's Ads Transparency Center: creative, format, first and last shown, region. When the result is empty it tells you why - your typo or our bug - instead of billing you for nothing.
Pricing
from $2.00 / 1,000 results
Rating
0.0
(0)
Developer
Marek Hartmann
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
9 days ago
Last modified
Categories
Share
Google Ads Transparency Scraper
Get every ad a company is currently running on Google — search, display, video, shopping — straight from Google's own Ads Transparency Center. Public data, no login, no personal information: just who paid for which ad, where, and when.
What makes this one different: when the result is empty, it tells you why. Most scrapers in this niche return an empty dataset and bill you for it.
Output
One row per ad:
| Field | What it is |
|---|---|
advertiserName, advertiserId | the verified advertiser Google shows |
creativeId, creativeType | ad identity; IMAGE or HOSTED_PREVIEW |
formatCode | Google's raw format code, kept as-is (they don't document it) |
firstShown, lastShown | when the ad started and last ran, as ISO timestamps |
region, regionsSeenIn | the region you asked for, and how many regions ran this ad |
imageUrl, previewUrl | the creative itself |
detailUrl | the ad's page in the Transparency Center |
Input
{"advertisers": ["Nike", "AR16735076323512287233"],"region": "US","maxCreativesPerAdvertiser": 200}
You can pass company names or advertiser IDs. Names are resolved inside the region you chose — and if the closest match turns out to have no ads, the next candidate is tried before the Actor gives up. That matters more than it sounds: searching for "Nike" returns a person called Nike Lee first, who has never run an ad. An advertiser ID is exact, so use one when you know it.
Why "no silent empty results" is the whole point
Google's endpoint has a trap in it. The request carries an opaque block of client context that
looks like decoration. Leave it out and the server answers HTTP 200 with a body of {} —
a perfectly successful request with zero ads in it.
A scraper built on that assumption looks like it works. It returns an empty dataset, charges you per run, and you have no way to tell whether the advertiser really has no ads.
Worse: the same empty {} also comes back when the advertiser genuinely doesn't exist.
The response cannot tell you which of the two happened.
So this Actor asks a second question. When a result is empty, it re-runs the same request against an advertiser it knows exists:
- that control question returns ads → the protocol is fine → your advertiser or region is the issue. The run succeeds with an empty dataset and an explanation. With pay-per-result billing you pay nothing.
- the control question is also empty → Google changed something and the run fails loudly, so you find out from an error and not from a silently empty spreadsheet.
The same rule covers the rest: every returned row is checked to actually belong to the advertiser you asked for. If the filter silently stopped applying, the run fails instead of handing you somebody else's ads.
The run report (RUN_REPORT in the key-value store) says per advertiser which of these happened.
Uses
- see a competitor's entire live ad set, with creatives and how long each has run
- track when a campaign started or stopped
- compare the same advertiser across countries — the ad sets differ
- collect creative examples for a pitch or a swipe file
Notes
- Regions matter. Ads are published per country: an advertiser can have hundreds of ads in the US and none in Slovakia. Pass a two-letter country code; an unknown one stops the run rather than quietly scraping something else.
- Only verified advertisers appear in the Transparency Center. That is Google's rule, not a limitation of this Actor.
- No login, no cookies. Sending credentials actually makes the endpoint reject the request.
- The connection speaks HTTP/1.1 on purpose. Over HTTP/2 the same request is answered with HTTP 429 every single time — measured, not guessed.
Need a scraper for something else?
I build custom Apify Actors and browser automation the same way this one is built: a verified result or an explicit failure, never an empty dataset sold as a success.
- My other Actors: https://apify.com/marekhartmann
- Code and test suites: https://github.com/marekhartmann-creator
Tell me the site and what you need out of it.