Google Ads Transparency Scraper - Competitor Ads Library avatar

Google Ads Transparency Scraper - Competitor Ads Library

Pricing

from $2.00 / 1,000 job results

Go to Apify Store
Google Ads Transparency Scraper - Competitor Ads Library

Google Ads Transparency Scraper - Competitor Ads Library

See competitor Google Ads: every creative Google's own Ads Transparency Center lists for an advertiser - format, preview, first and last shown dates, days active and the countries it ran in. Only-new-creatives alerts turn a schedule into a competitor-ad feed.

Pricing

from $2.00 / 1,000 job results

Rating

0.0

(0)

Developer

Flash Scrape

Flash Scrape

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

4 days ago

Last modified

Share

Competitor Ads Scraper - Google Ads Transparency Library

See a competitor's Google Ads — every creative Google's own Ads Transparency Center lists for an advertiser, with first-shown and last-shown dates and days active.

See every ad a competitor runs on Google — Search, Display and YouTube — straight from Google's own Ads Transparency Center, the public accountability tool where every advertiser's creatives are listed. Give it a domain (nike.com), a brand term (nike — auto-resolved to its top advertiser domain) or an advertiser ID (AR…), and get one clean row per ad: advertiser, format, preview, first-shown and last-shown dates, and days active.

No API key, no login, no browser — the actor calls the same keyless endpoint the Transparency Center's own page uses, so the data is exactly what Google publishes, at the moment you run it.

Watching competitors on Facebook/Meta instead? The Ad Library niche is well covered on the Store — this actor is for the Google side, where fresh, honest tooling is scarce.

What you get

One row per ad creative. Measured on nike.com (100 rows, 2026-08-15):

columnfillnotes
advertiser_id, advertiser_name100%e.g. AR18378488041124659201, "Nike Retail BV" — ads run by resellers/agencies for the same domain show up under their own advertiser
domain100%the advertiser's verified domain
creative_id100%Google's CR… id — stable, deduplicated
format100%text / image / video
preview_url100%archived screenshot for text ads, render URL for display/video
first_shown, last_shown100%real dates — the sample's last_shown was the run date itself
days_active100%how long the creative has been running — long-running ads are the ones making money
transparency_url100%the ad's own public page on Google's Transparency Center
query100%which of your targets produced the row
regions, region_stats, impressions_min/maxwith includeDetailscountries where the ad ran, plus Google's published EU impression range per country in region_stats (e.g. France: 50,000–60,000). The top-level impressions_min/impressions_max are the SUM of those per-country bounds across every country the ad ran in — not a single published range; max is null unless every listed country publishes an upper bound. null where Google publishes no record

Example row (real):

{
"advertiser_id": "AR18378488041124659201",
"advertiser_name": "Nike Retail BV",
"domain": "nike.com",
"creative_id": "CR16074515335549550593",
"format": "image",
"preview_url": "https://tpc.googlesyndication.com/archive/simgad/3436141099225117854",
"first_shown": "2025-07-01",
"last_shown": "2026-08-15",
"days_active": 410,
"transparency_url": "https://adstransparency.google.com/advertiser/AR18378488041124659201/creative/CR16074515335549550593",
"query": "nike.com",
"source": "google-ads-transparency"
}

Quick start

Press Start with the form as it comes and you get up to 50 of Nike's ads shown in the last 30 days (those are the Console prefills). An API call with a bare {} uses the field defaults instead: no recency filter, up to 100 ads. Then point it at your own market:

{
"queries": ["asics.com", "on-running.com", "hoka.com"],
"activeWithinDays": 30,
"maxItems": 300
}

activeWithinDays: 30 is the filter that matters: it keeps only ads last shown in the past month — a competitor's currently running campaigns instead of a year of history. Long days_active + recent last_shown = the proven winners they keep paying for.

Input reference

fielddefault (Console form starts at)what it does
queries1-domain sampleDomains (exact) or brand terms (auto-resolved via Google's own suggestions). Your list replaces the sample entirely. One exception: a list that byte-equals the built-in sample (["nike.com"]) alongside advertiserIds is read as the injected schema default and dropped — the run says so in its status message. Re-enter it as www.nike.com or add a second entry to keep it.
advertiserIds[]AR… ids — grab them from any run's rows to watch specific advertisers precisely. A full Transparency Center URL works too (the AR… id is extracted). Anything without an AR… id is rejected, named in the status message, and never silently replaced by the sample: if none of your ids is usable the run delivers 0 rows and charges nothing.
formatanyKeep only text, image or video ads. Applied before billing.
activeWithinDays0 = off (Console form: 30)Keep only ads last shown within N days. Undated ads are kept, never guessed away.
onlyNewCreativesfalseCompetitor-ad alert mode — see below.
maxItems100 (Console form: 50)Total cap across all targets, split round-robin. Your cost ceiling.
region"" (worldwide)Two-letter ISO 3166-1 country code — keeps only ads with a published record for that country, from each ad's own detail record. A code that is not a real ISO country stops the run with 0 rows and no charge rather than quietly delivering a worldwide pull. See the region limits below.
includeDetailsfalseFills regions, region_stats, impressions_min/impressions_max — countries where each ad ran + Google's published EU impression ranges per country in region_stats; the top-level impressions_min/impressions_max are cross-country totals of those bounds. One paced request per delivered ad.
proxyApify ProxyGoogle rate-limits this endpoint per IP (measured: ~15 rapid requests → 429). The default automatic proxy rotates to a fresh session on every rate-limit; your own proxy URLs are honored.

Every filter runs before billing — a filtered ad is never charged.

🔔 Competitor-ad alerts: onlyNewCreatives + a schedule

Schedule the actor (daily or weekly) with onlyNewCreatives: true and the same targets:

  • the first run is the baseline — it delivers everything and says so;
  • every later run delivers only creatives not seen before — i.e. the competitor's new ads;
  • a quiet run delivers nothing, bills nothing, and says "Nothing new" plainly.

A competitor launching new creatives is a competitor testing a new offer, product or angle — this is the cheapest way to hear about it first. Watch memory lives in a named key-value store (google-ads-monitor) in your own account. A watch is identified by its targets and its filters (format, activeWithinDays, region), so two schedules on the same domain with different filters keep separate memories. Every creative the watch still sees has its timestamp refreshed, so the automatic prune (90 days unseen / 50k keys) only forgets ads that have actually stopped — a long-running ad never resurfaces as a false "new" alert.

Use it from your code

// JavaScript — apify-client
const { ApifyClient } = require('apify-client');
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('flash_scraper/google-ads-transparency-scraper').call({
queries: ['nike.com'], activeWithinDays: 30, maxItems: 200,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
# Python — apify-client
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("flash_scraper/google-ads-transparency-scraper").call(run_input={
"queries": ["nike.com"], "activeWithinDays": 30, "maxItems": 200})
items = client.dataset(run["defaultDatasetId"]).list_items().items
# curl — run synchronously and get the dataset back
curl -X POST "https://api.apify.com/v2/acts/flash_scraper~google-ads-transparency-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"queries": ["nike.com"], "maxItems": 50}'

n8n / Make / Zapier

All three have a native Apify node/module: pick Run actorflash_scraper/google-ads-transparency-scraper, pass the JSON input above, and read the default dataset when the run finishes. onlyNewCreatives + a schedule trigger pipes every new competitor ad into Slack, a sheet or your CRM — and quiet days cost nothing.

Honest limits

  • Google's data, Google's rules. The Transparency Center keeps roughly the last year of ads and publishes no spend and no exact impression counts — nobody scraping it can get those. What Google does publish is impression ranges for ads shown in the EU (a DSA requirement) — includeDetails ships exactly those, as ranges, and null everywhere Google publishes nothing.
  • Region filtering is record-based. Google's search endpoint has no region parameter, so region: "FR" checks each candidate ad's own per-country record (one paced request per ad) and keeps the ones that ran there — slower than a native filter, but exact and honest. Because every candidate costs one paced request, the region check inspects at most 1000 candidates per run (≈20 minutes at that ceiling) and stops as soon as maxItems ads have matched — so any maxItems the schema accepts is reachable, and the run never pays for candidates it cannot deliver. The status message says exactly how many were checked; lookups that were rate-limited are reported as not checked rather than counted as "Google publishes nothing here".
  • Ad text for text ads ships as an archived screenshot URL (preview_url), not extracted copy — that is how Google serves them.
  • Brand-term resolution is best-effort (it uses Google's own suggestion service); the exact domain is always the reliable input.
  • The endpoint rate-limits per IP; the default proxy handles it. Very large pulls simply take longer — pacing is deliberate.