Since 2023, Google publishes every ad it shows in the Ads Transparency Center: Search ads, YouTube videos, display banners, Shopping, Maps and Play ads, for every advertiser, with the countries and dates they ran.
That's a goldmine for competitive research. But the website only lets you browse ads one by one, there's no export, and most Search ads are shown as screenshots, so you can't even copy their text.
In this post I'll pull every ad of two competitors (Asana and monday.com) into pandas with a few lines of Python, then answer a few questions you'd actually ask about a competitor's advertising.
Disclosure: I built the Google Ads Transparency Scraper used below. It runs on Apify, which has a free plan with monthly credits, so you can follow along without paying anything.
What we'll get
One row per ad, with:
-
advertiserName,format(text, image or video),firstShown,lastShown -
headline,description,displayUrlfor Search ads, read from the screenshot when Google doesn't publish the text -
countrieswhere the ad ran, with per-country dates and impression ranges (published for EU and political ads) -
paidBy: the entity that paid for the ad, which sometimes reveals the agency behind a brand -
imageUrl,youtubeUrlfor image and video ads
Step 1: install the client
Create a free Apify account, copy your API token from Settings → API & Integrations, and install:
pip install apify-client pandas
Step 2: fetch the ads of two competitors
Searching by domain is the most complete option: Google returns the ads of every account that sends traffic to that domain, including agencies and local entities.
import os
import pandas as pd
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("featherscrape/google-ads-transparency-scraper").call(run_input={
"searchTerms": ["asana.com", "monday.com"], # domains, brand names or advertiser IDs
"region": "US", # where the ads were shown; empty = everywhere
"maxAdsPerSearch": 100, # most recently shown first
})
ads = pd.DataFrame(client.dataset(run["defaultDatasetId"]).iterate_items())
ads["firstShown"] = pd.to_datetime(ads["firstShown"])
ads["lastShown"] = pd.to_datetime(ads["lastShown"])
print(len(ads), "ads")
You can also filter by platform (search, youtube, shopping, maps, play), format (text, image, video) and a dateFrom / dateTo range.
Step 3: what are they running?
print(ads["format"].value_counts())
print(ads["advertiserName"].value_counts())
On my run (US, October 2026), 200 ads came back: 158 Search ads, 36 videos and 6 image ads. The advertiser list already holds a surprise:
monday.com LTD 99
Asana, Inc. 94
MightyHive, INC. 6
MightyHive is a media agency: it runs the display banners pointing to asana.com, in up to 23 countries each. That's the kind of thing you won't see by searching Google yourself.
Step 4: read their ad copy
text_ads = ads[ads["headline"].notna()]
for _, ad in text_ads[text_ads["advertiserName"] == "monday.com LTD"].head(10).iterrows():
print(f'{ad["headline"]}\n {ad["description"]}\n {ad["displayUrl"]}\n')
A few of them:
monday CRM is So Much Better
Manage contacts, sales, and pipelines in one place with tools your team will enjoy.
monday.com
What Makes monday.com Better?
Choose monday.com to Build Workflows Perfectly Customized to Your Team's Needs.
monday.com
Project Management Templates
Plan, Track, Manage and Collaborate Using the Top Templates Teams Love. Try for Free.
monday.com
Comparative headlines, a CRM push and templates as a lead magnet: you get monday.com's messaging strategy in one screen.
Step 5: find the ads that work
Advertisers don't keep paying for ads that don't convert. An ad that has been running for years is a strong signal:
ads["daysRunning"] = (ads["lastShown"] - ads["firstShown"]).dt.days
print(ads.sort_values("daysRunning", ascending=False)[["advertiserName", "headline", "daysRunning"]].head())
"What Makes monday.com Better?" has been running since October 2021: five years. If you compete with monday.com, that's the message to answer.
Step 6: where do they advertise?
print(ads.explode("countries")["countries"].value_counts().head(10))
Each ad also has a regions list with first and last shown dates per country, and impression ranges where Google publishes them (ads shown in the EU and political ads).
Step 7: get alerted on new ads
Turn on onlyNewAds and schedule the run (for example every morning in the Apify Console, or from cron with the code above). Each run then returns only the ads that weren't in previous runs, so you can push them to Slack, a Google Sheet or an email digest:
run = client.actor("featherscrape/google-ads-transparency-scraper").call(run_input={
"searchTerms": ["asana.com", "monday.com"],
"onlyNewAds": True,
})
new_ads = list(client.dataset(run["defaultDatasetId"]).iterate_items())
print(f"{len(new_ads)} new ads since the last run")
A few things to know
- Impressions are rare. Google publishes impression ranges only for ads shown in the EU (Digital Services Act) and for political ads. There's no spend or click data for commercial ads.
-
Screenshot text is read automatically (
textSource: "ocr"). It's accurate on standard ads, with the occasional OCR slip (for example "Al" instead of "AI").ocrTextkeeps the raw text. - Domains beat names. A name like "Asana" is matched to the closest advertiser accounts; a domain catches every account and agency advertising for it.
Wrapping up
The Ads Transparency Center is one of the best free sources of competitive intelligence, once you can get the data out of it. With a few lines of Python you can see what your competitors say, where and since when, and get notified when they launch something new.
The scraper costs $1.50 per 1,000 ads. If you need a field or hit a bug, open an issue on the scraper's page: I usually answer within 48 hours. And I'm curious: which competitor would you look at first?
Top comments (0)