How to Verify Which Advertisers Run Google Ads on a Domain
One domain often has many advertisers behind it. Use Google's Ads Transparency Center to list every verified entity buying ads that point at your brand.

Thirdwatch's Google Ads Transparency Scraper answers a question most brands cannot answer about themselves: exactly which verified advertiser entities are buying Google ads that point at your domain. A single domain routinely returns subsidiaries, media agencies, affiliates and resellers as separate entities. Pass the domain, get every advertiser with its own ID, creatives and activity dates, and reconcile that list against who you actually authorised.
Why verify which advertisers run ads on your domain
Most brand teams assume the answer is "us". It usually is not. Google verifies the advertiser account, not the destination, so anyone with a verified account can run ads that send traffic to your domain: regional subsidiaries with their own budgets, the media agency the regional team hired, affiliates working your program, resellers who bought inventory, and occasionally somebody you have never heard of.
This is a compliance surface with real money attached. Google made advertiser identity verification mandatory precisely so the entity behind an ad is a known, disclosed party, and it publishes the result in the Ads Transparency Center. The archive is therefore an audit trail of who is spending against your brand, and almost nobody reads it.
We ran a live check while writing this. A pull on hubspot.com returned Hubspot, Inc. alongside seven unrelated entities including Beanstock, Tested4you, JELOU S.A., ClioAssist GmbH and IMPARGO GmbH, all running creatives that resolve to the hubspot.com domain. A pull on nike.com returned Nike Retail BV, Nike, Inc., a Singapore branch entity, a Japanese subsidiary and two media agencies. Neither result is alarming on its own. Both are impossible to see if you never look.
How does this compare to the alternatives?
Brand-protection vendors solve this, expensively and partially. The archive solves it directly.
| Approach | Pricing | Coverage | Setup time | Maintenance |
|---|---|---|---|---|
| Manual Ads Transparency Center lookups | Free | One domain at a time, no export | Zero | 30-60 min per brand per check |
| Brand-protection suite | Enterprise annual contract | Broad, but ad-channel depth varies | 4-8 weeks onboarding | Vendor-managed |
| Thirdwatch actor plus an allowlist diff | Pay per result | Every verified advertiser Google returns for the domain | Under 30 minutes | Rerun on a schedule |
Enterprise brand-protection suites are the right answer if you also need marketplace, domain and social monitoring. If the specific question is "who is buying Google ads to my domain this week", a scheduled pull and a set difference against your approved-partner list gets you a defensible answer for a fraction of the effort, and it produces evidence with Google's own URLs attached.
How to audit advertisers on a domain in 4 steps
Step 1: How do I list every advertiser behind a domain?
Query the domain and group by advertiser_id, which is the stable identifier.
export APIFY_TOKEN="apify_api_xxxxxxxxxxxxxxxx"import os
import collections
import requests
ACTOR = "thirdwatch~google-ads-transparency-scraper"
TOKEN = os.environ["APIFY_TOKEN"]
resp = requests.post(
f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items",
params={"token": TOKEN},
json={
"domainsOrAdvertisers": ["hubspot.com"],
"region": "US",
"maxResultsPerQuery": 500,
"proxyConfiguration": {"useApifyProxy": True},
},
timeout=900,
)
rows = resp.json()
entities = collections.defaultdict(lambda: {"creatives": 0, "name": "", "url": ""})
for row in rows:
bucket = entities[row["advertiser_id"]]
bucket["creatives"] += 1
bucket["name"] = row["advertiser_name"]
bucket["url"] = row["advertiser_url"]
for advertiser_id, info in sorted(
entities.items(), key=lambda kv: -kv[1]["creatives"]
):
print(f"{info['creatives']:4} {info['name'][:44]:46} {advertiser_id}")Group on advertiser_id, never on advertiser_name. Names are localised and occasionally reused; the ID is the thing Google keys the account on.
Step 2: How do I separate approved partners from unknowns?
Maintain an allowlist of advertiser IDs and diff against it.
APPROVED = {
"AR14639398330518470657": "EU reseller, contract 2025-114",
"AR13450050967756079105": "Affiliate program, tier 1",
}
unknown = {
advertiser_id: info
for advertiser_id, info in entities.items()
if advertiser_id not in APPROVED
}
print(f"{len(unknown)} unrecognised advertisers on this domain\n")
for advertiser_id, info in unknown.items():
print(f"{info['name']}")
print(f" creatives: {info['creatives']}")
print(f" evidence : {info['url']}\n")Seed the allowlist by running step 1 once, reviewing the output with whoever owns partner contracts, and pasting in the IDs that check out. After that, every future run only surfaces genuinely new entities.
Step 3: How do I check which unknowns are currently active?
An entity that stopped serving two years ago is history, not an incident. Use last_shown to filter.
from datetime import datetime, timedelta, timezone
recent_cutoff = datetime.now(timezone.utc) - timedelta(days=14)
active_unknown = collections.defaultdict(list)
for row in rows:
if row["advertiser_id"] in APPROVED:
continue
if not row["last_shown"]:
continue
if datetime.fromisoformat(row["last_shown"]) >= recent_cutoff:
active_unknown[row["advertiser_name"]].append(row)
for name, creatives in sorted(
active_unknown.items(), key=lambda kv: -len(kv[1])
):
newest = max(c["first_shown"] or "" for c in creatives)
print(
f"{name[:40]:42} {len(creatives):3} live creatives, "
f"newest launched {newest[:10]}"
)The pairing of "live in the last 14 days" with "launched recently" is the signal worth escalating. A long-dormant entity that suddenly launches new creatives against your domain is a different event from one that has been quietly running the same banner since 2022.
Step 4: How do I run the audit across every market I sell in?
Region filters where creatives were shown, so run the same domain per market and union the results.
MARKETS = ["US", "GB", "DE", "FR", "IN", "AU"]
found = {}
for market in MARKETS:
market_rows = requests.post(
f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items",
params={"token": TOKEN},
json={
"domainsOrAdvertisers": ["hubspot.com"],
"region": market,
"maxResultsPerQuery": 500,
"proxyConfiguration": {"useApifyProxy": True},
},
timeout=900,
).json()
for row in market_rows:
found.setdefault(row["advertiser_id"], set()).add(market)
for advertiser_id, markets in found.items():
print(f"{advertiser_id} seen in: {', '.join(sorted(markets))}")An entity appearing only in one market is usually a legitimate regional partner. An entity appearing across every market you checked is either you, your global agency, or something that deserves a phone call. The actor recognises two-letter ISO country codes; an unrecognised code silently widens the search to all regions, so keep the market list to codes you have verified return results.
Sample output
Two real records from a live hubspot.com pull, showing the brand's own entity and a third party:
[
{
"query": "hubspot.com",
"region": "US",
"advertiser_id": "AR14639398330518470657",
"advertiser_name": "Tested4you",
"advertiser_domain": "hubspot.com",
"creative_id": "CR14472003294416863233",
"format": "text",
"first_shown": "2026-06-02T12:04:04+00:00",
"last_shown": "2026-09-08T12:12:48+00:00",
"image_url": "https://tpc.googlesyndication.com/archive/simgad/9268441189215670650",
"advertiser_url": "https://adstransparency.google.com/advertiser/AR14639398330518470657?region=US",
"url": "https://adstransparency.google.com/advertiser/AR14639398330518470657/creative/CR14472003294416863233?region=US",
"source": "Google Ads Transparency Center"
},
{
"query": "hubspot.com",
"region": "US",
"advertiser_id": "AR13450050967756079105",
"advertiser_name": "Beanstock",
"advertiser_domain": "hubspot.com",
"creative_id": "CR13021818367290376193",
"format": "text",
"first_shown": "2022-11-04T17:16:11+00:00",
"last_shown": "2026-09-08T12:19:25+00:00",
"image_url": "https://tpc.googlesyndication.com/archive/simgad/9663983619547795091",
"advertiser_url": "https://adstransparency.google.com/advertiser/AR13450050967756079105?region=US",
"url": "https://adstransparency.google.com/advertiser/AR13450050967756079105/creative/CR13021818367290376193?region=US",
"source": "Google Ads Transparency Center"
}
]Neither advertiser_name is HubSpot, yet both records carry advertiser_domain of hubspot.com. That is the whole point of the audit. The first entity launched in June 2026 and was serving on the day of the run, which makes it a current, recent arrival. The second has been running since November 2022, which suggests a long-standing relationship rather than anything new. advertiser_url is the evidence link you paste into a ticket; it opens Google's own page for that advertiser.
Common pitfalls
Grouping by advertiser_name. Names are localised, so the same corporate group can appear as Latin script in one market and local script in another. advertiser_id is the only reliable key.
Assuming an unfamiliar name is a violation. Media agencies, demand-side partners and regional subsidiaries all appear as separate advertisers and are usually entirely legitimate. The output is a list to reconcile, not a list of offenders.
Auditing one region and calling it global. region filters where creatives were shown. An entity serving only in Germany will not appear in a US-only pull, so run per market.
Reading the result as a keyword audit. The archive is keyed on the advertiser and the ad's destination domain, not the search terms that triggered it. Competitor brand bidding is a separate investigation that starts from the competitor's own domain.
Thirdwatch's actor returns the advertiser ID, the advertiser page URL and the creative URL on every record, so each finding carries its own citation.
Related use cases
- Google Ads Transparency Scraper
- Scrape Google Ads Transparency Center for competitor ads
- Track competitor Google ad launches and flight dates
- Build a Google display ad creative swipe file
- Cross-channel ad intelligence with Google and Meta
- Scrape Facebook Ad Library for competitive intel
- The complete guide to scraping business data
- All Thirdwatch use-case guides
Frequently asked questions
Why does one domain return several advertiser names?
Because Google verifies the advertiser account, not the domain. Regional subsidiaries, media agencies buying on the brand's behalf, resellers and affiliates can all run ads pointing at the same destination domain, and each appears as a separate advertiser entity.
Is advertiser_name a legal entity name?
It is the name Google holds for the verified advertiser account, which is usually the registered legal entity and is often localised. Expect names in the advertiser's own script and expect subsidiary names rather than the consumer-facing brand.
Can I use this to find unauthorised resellers?
Yes, that is the core workflow. Pull every advertiser running ads to your domain, subtract your approved partner list, and whatever remains is either an unapproved affiliate, a reseller or an entity worth a closer look.
Does this detect brand bidding by competitors?
Not directly. The archive is keyed on the advertiser and the destination domain of the ad, not on the keyword that triggered it. A competitor bidding on your brand term still points at their own domain, so search their domain instead.
How current is the advertiser list?
Each record carries last_shown, which for a live creative sits within hours of the run. Filter to recent last_shown values to get the set of entities actively serving now rather than everyone who ever has.
Related
100 free credits, no credit card.
About 30 real searches. Add the MCP to Claude or Cursor in two minutes.