Google Ads Transparency Center Scraper & API
Every Google ad an advertiser is running: creatives, formats, first/last shown, impression ranges, per-country reach. Search by domain, brand or advertiser ID. Monitor changes in Google's result window. Batch 'is this domain advertising?' checks for lead enrichment. No browser.
Categories: MARKETING, LEAD_GENERATION, AGENTS. Pay per result, no subscription; Apify's free plan includes $5 of monthly usage.
Run it from the command line
curl -sS -X POST "https://api.apify.com/v2/acts/brenton8907~google-ads-transparency-center/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"domains": ["nike.com"], "mode": "ads", "region": "ANYWHERE", "format": "ALL", "surface": "ALL", "maxAdsPerQuery": 200}'
Run it from Python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("brenton8907/google-ads-transparency-center").call(run_input={
"domains": [
"nike.com"
],
"mode": "ads",
"region": "ANYWHERE",
"format": "ALL",
"surface": "ALL",
"maxAdsPerQuery": 200
})
for item in client.dataset(run.default_dataset_id).iterate_items():
print(item)
Use it as an MCP tool (Claude, Cursor, any MCP client)
The actor is listed in the official MCP registry as io.github.brenton-keller/google-ads-transparency-center and served by Apify's hosted MCP server:
{
"mcpServers": {
"google-ads-transparency-center": {
"url": "https://mcp.apify.com/?tools=brenton8907/google-ads-transparency-center",
"headers": {
"Authorization": "Bearer YOUR_APIFY_TOKEN"
}
}
}
}
Pricing (pay per event)
| event | what | price | details |
|---|---|---|---|
ad-record | Ad creative | $0.0010 | One ad creative row: advertiser, format, preview URL, first/last shown, days shown, impression and spend ranges, matched domain. |
enriched-ad-record | Enriched ad creative | $0.0040 | One ad creative row with full details attached: per-country impression ranges, surfaces, targeting, category. One extra request per ad. |
advertiser-record | Advertiser check | $0.0020 | One advertiser summary row (advertiser-check mode): advertiser IDs matched for a domain or brand, creative count range, verification status. One request per query. |
Documentation
Pull every ad an advertiser is running on Google (Search, YouTube, Shopping, Maps, Play) from the Ads Transparency Center, as clean JSON/CSV. Search by domain, brand name, advertiser ID or a pasted Transparency Center URL. Filter by country, format, platform and date. Run it on a schedule with "only newly observed ads" to track changes in Google's result window. Use advertiser-check mode to answer "is this company running Google Ads, and how many?" for thousands of domains at a fraction of a cent each.
No browser, no login. Talks to the Transparency Center's own data API directly with a browser-grade TLS fingerprint, paces itself under Google's rate limits and rotates proxies automatically, so runs finish fast and don't fail halfway.
Try it free: every Apify account includes free monthly usage, and 1,000 ads cost $1, so checking a competitor is free.
Competitor ads monitor: schedule it, track Google's result window
This is the highest-value way to use the actor: a daily run that returns only the ads you have not seen before, delivered to Slack or email. Three steps, no code:
- Create a task from this actor with the input below (one line per competitor domain or advertiser ID). Run it once; the first run seeds the list of known ads.
- Schedule it: in the task, open Schedule (or Console → Schedules) and pick daily or hourly. Each run costs only creative IDs not previously observed in the capped result window; a quiet day costs a few requests and no results.
- Alerts: in the task's Integrations tab add Slack or Gmail on "run succeeded". Use the run's status message as the notification text; in monitor mode it reports newly observed result-window ads or that none were observed. Slack message template (Handlebars):
*Google Ads monitor:* {{resource.statusMessage}}
<https://console.apify.com/storage/datasets/{{resource.defaultDatasetId}}|Open the newly observed ads>
For "only notify when there is something new", route the run through n8n / Make / Zapier (Apify triggers exist for all three) and filter on the dataset item count.
{
"domains": ["nike.com", "adidas.com", "onrunning.com"],
"onlyNewAds": true,
"monitorStoreName": "running-shoes-competitors",
"includeDetails": true,
"maxAdsPerQuery": 200
}
Each newly observed row carries is_new: true, first_seen_by_monitor, the creative preview URL, format, first/last shown and (with includeDetails) served countries and surfaces. This means newly observed, not necessarily newly launched. Google's capped top-N result window can reorder between runs, so a creative first shown months or years ago can enter the window and appear for the first time. Use first_shown to identify likely launches; narrow by advertiser ID, region, format, surface or date to make the window more stable. Use one monitorStoreName per watchlist.
What it costs at scale: 50 newly observed ads/day with details = 50 × $0.004 = $0.20/day, ~$6/month; without details, $1.50/month. Result-window reordering can increase this count on broad or heavily capped queries.
What it does
| Mode | One row per | Use it for |
|---|---|---|
ads (default) |
ad creative | competitor creative research, ad libraries, longevity analysis (which ads run for months), creative monitoring |
advertisers |
matched advertiser | lead enrichment / qualification: is this domain advertising on Google right now, under which account(s), roughly how many ads |
Who it's for
- Performance marketers and agencies tracking competitors' Google ads: result-window change alerts, formats, and how long each creative survives (an ad-spy tool for Google, YouTube and Shopping ads at pay-per-result prices).
- Sales and lead-gen teams enriching prospect lists (Clay, n8n, Make, Zapier, Google Sheets) with "runs Google Ads: yes/no + volume" as a buying signal.
- Brand and compliance teams watching for affiliates, resellers or impersonators advertising on their domain.
- Researchers and journalists studying advertising activity by region and platform.
- AI agents: every field is stable and typed, and the actor is exposed as an MCP tool via Apify.
Input examples
All ads for a domain, US, last 90 days, YouTube only
{
"domains": ["allbirds.com"],
"region": "US",
"surface": "YOUTUBE",
"dateFrom": "2026-06-01",
"maxAdsPerQuery": 500
}
A brand by name, with full per-country details
{
"queries": ["Nike"],
"maxAdvertisersPerQuery": 1,
"includeDetails": true
}
Paste Transparency Center URLs
{
"startUrls": [{ "url": "https://adstransparency.google.com/advertiser/AR18378488041124659201?region=GB" }]
}
Monitor by advertiser ID instead of domain (see the monitor section above for scheduling and alerts)
{
"advertiserIds": ["AR18378488041124659201", "AR16735076323512287233"],
"onlyNewAds": true,
"monitorStoreName": "footwear-competitors"
}
Lead enrichment: is each domain advertising?
{
"domains": ["allbirds.com", "warbyparker.com", "some-local-plumber.com"],
"mode": "advertisers",
"region": "US"
}
Output
ads mode
{
"creative_id": "CR04143963882324492289",
"advertiser_id": "AR18378488041124659201",
"advertiser_name": "Nike Retail BV",
"format": "TEXT",
"matched_domain": "nike.com",
"first_shown": "2026-07-22T04:31:55+00:00",
"last_shown": "2026-09-01T21:04:44+00:00",
"days_shown": 43,
"impressions": null,
"impressions_low": null,
"impressions_high": null,
"preview_url": "https://tpc.googlesyndication.com/archive/simgad/12774179880874022668",
"preview": { "script_url": "…", "iframe_url": null, "html_snippet": null, "image_url": "…" },
"ad_url": "https://adstransparency.google.com/advertiser/AR18378488041124659201/creative/CR04143963882324492289?region=anywhere",
"advertiser_url": "https://adstransparency.google.com/advertiser/AR18378488041124659201?region=anywhere",
"query": "nike.com",
"query_kind": "domain",
"region": "US",
"matched_total": { "low": 100000, "high": 200000 },
"is_new": true
}
With includeDetails: true each row also gets:
"details": {
"served_countries": ["US", "GB", "DE", "FR", "CA"],
"country_reach": [{ "country": "US", "first_served": "2026-07-22", "last_served": "2026-09-01", "impressions": null, "surfaces": [] }],
"eu_reach": { "impressions": { "low": 10000, "high": 50000 }, "first_served": "2026-07-21", "last_served": "2026-09-01" },
"variations": [ { "script_url": "…", "image_url": "…" } ],
"targeting": { "categories": { "demographics": {"included": true}, "geography": {"included": true} } },
"category": { "gcb_category_id": 10021, "vertical": 1 },
"customer_name": "Nike Retail BV",
"contains_synthetic_media": false
},
"countries": ["US", "GB", "DE", "FR", "CA"],
"surfaces": ["SEARCH", "YOUTUBE"]
Impression and spend ranges are only published by Google for EU-served ads and political ads; elsewhere they are null. Where they exist they are lifted to the top level as impressions_low / impressions_high (global) and eu_impressions_low / eu_impressions_high (EU reach from the details block), so CSV exports and the Overview table show them without digging into details.
matched_total is Google's estimate of how many ads match the whole query (useful even when you cap maxAdsPerQuery).
advertisers mode
{
"query": "allbirds.com",
"query_kind": "domain",
"region": "US",
"is_advertising": true,
"advertiser_id": "AR12345678901234567890",
"advertiser_name": "Allbirds Inc",
"creatives_count_low": 1000,
"creatives_count_high": 2000,
"advertisers_on_domain": 1,
"formats_seen": ["IMAGE", "TEXT", "VIDEO"],
"last_shown": "2026-09-02T01:46:39+00:00",
"advertiser_url": "https://adstransparency.google.com/advertiser/AR12345678901234567890?region=US"
}
Domains with no ads return one row with is_advertising: false, so every input gets exactly one answer.
Advertiser-check rows for a domain
Each advertiser row for a domain carries its own ad count for that domain (creatives_count_low / creatives_count_high), the advertiser's billing country, legal_name and unverified flag from its Transparency Center profile, and the domain-wide total in domain_creatives_count_low / domain_creatives_count_high. This distinguishes the domain owner (Nike, Inc.: 7,000–8,000 ads on nike.com) from resellers and affiliates that also link to the domain (an agency with 500–600). Costs 2 extra requests per advertiser row; maxAdvertisersPerQuery caps it.
Pricing
Pay per result, no subscription:
| Event | Price |
|---|---|
Ad creative (ads mode) |
$1.00 per 1,000 |
Ad creative with full details (includeDetails) |
$4.00 per 1,000 |
Advertiser check row (advertisers mode) |
$2.00 per 1,000 |
Plus normal Apify platform usage (a few cents per thousand ads; the actor runs in 256 MB and makes one request per 40 ads). Failed targets are not charged.
Limits and notes
- Google paginates to roughly 1,000 ads per query. Split large advertisers by format, surface or date range to get more.
- Google keeps about 12 months of ad history.
- A domain search returns every advertiser whose ads link to that domain, not only the domain owner. That can include resellers, affiliates and demo/test ads (
example.comyields real ads from unrelated advertisers). Useadvertisersmode to see who they are, oradvertiserIdsto pin one account. - Brand-name queries are resolved through Google's own suggestion index; the largest exact/prefix match is used first. Use
advertiserIdsfor precision. - Ad-text (keyword) search is only available for political ads in Google's data and is not exposed here.
- Google rate-limits by IP. Keep the default Apify proxy on for anything beyond a few hundred ads; the actor rotates sessions on every rate-limit response and backs off, so runs complete instead of failing.
- If Google blocks every request (bot-check page or HTTP 429 after retries), the run fails with a clear error and nothing is charged. Enable Apify Proxy (residential) or raise
requestDelaySeconds. If only some targets are blocked, the run succeeds and those targets get a row with anerrorfield. - Rows whose detail lookup failed (
details_error) are charged at the base ad rate, not the enriched rate. - Monitor state is per
monitorStoreName(a named key-value store in your account). Do not run two monitor runs against the same store at the same time; use one store per watchlist or schedule.
Use as an MCP tool / from code
The actor is callable from any MCP client through Apify's MCP server, and from code with the Apify API. Agent pattern: keep a watchlist, call with onlyNewAds: true on a schedule, and act only on the rows that come back.
curl -X POST "https://api.apify.com/v2/acts/<username>~google-ads-transparency-center/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"domains": ["allbirds.com"], "mode": "advertisers", "region": "US"}'
Legal
This actor only reads data that Google publishes for public inspection in the Ads Transparency Center. Use it in line with Google's terms and applicable law; you are responsible for how you use the output.
Support
Open an issue on the actor page with the run ID and input. If Google changes the Transparency Center's data format, affected fields go null rather than failing the run; report which field and we will patch it. If the actor saved you time, a review helps other people find it.