DEV Community

SteadyScrape
SteadyScrape

Posted on

Monitoring Competitors' Google Ads With the Ads Transparency Center (and Getting Alerts for New Ones)

If you work in marketing, someone eventually asks: "What ads are our competitors running right now?" Since 2023, Google has had a public answer: the Ads Transparency Center (adstransparency.google.com). It lists the ads every verified advertiser runs across Search, YouTube, Shopping, Maps and Play, with first-shown and last-shown dates.

It's a great resource for one-off checks. This post is about turning it into data you can work with: a table of a competitor's creatives, the signals worth pulling out of it, and a daily alert when a competitor launches something new.

What you can see manually

Search an advertiser name or a domain, pick a region, and you get a grid of creatives. Click one and you see:

  • the format (text, image or video) and a rendered preview,
  • first shown and last shown dates,
  • the advertiser's verified name and its location.

The UI is fine for looking at one competitor once. It gets slow when you want to compare five competitors, sort by run time, or notice new creatives the day they appear. There's no export button, and no official API for the ad library.

The signals that matter

Before collecting anything, decide what you'll do with it. Three signals carry most of the value:

  1. Days running. Advertisers stop paying for ads that don't work. A creative that has run for two years is probably a winner, so study its angle and offer.
  2. Active now. "Last shown" within a day or two means it's live. Comparing active ads across competitors tells you who's pushing hardest right now.
  3. New creatives. A batch of new ads usually means a launch, a promotion or a new market.

Getting it as data

Disclosure: we (SteadyScrape) built the tool below and get paid when it's used.

Google Ads Transparency Scraper (steady-scrape/google-ads-transparency) is an Apify Actor that returns Transparency Center data as one row per creative. You can query by domain (hubspot.com), by advertiser ID, or by company name (it finds the advertiser accounts for you). It doesn't use a browser, proxies or a login. The data comes from a licensed ads-data API, so runs don't break when Google changes the page.

Here's a script that pulls a competitor's US ads and summarizes them:

import os, statistics
from collections import Counter
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("steady-scrape/google-ads-transparency").call(run_input={
    "targets": ["hubspot.com"],
    "region": "US",
    "maxAdsPerTarget": 40,
})
ads = [a for a in client.dataset(run["defaultDatasetId"]).iterate_items() if a.get("creativeId")]

print(Counter(a["format"] for a in ads))
print("active now:", sum(a["isActive"] for a in ads))
print("median days running:", statistics.median(a["daysRunning"] for a in ads))
for a in sorted(ads, key=lambda a: -a["daysRunning"])[:5]:
    print(a["daysRunning"], a["format"], a["adUrl"])
Enter fullscreen mode Exit fullscreen mode

In our test run, that returned 40 ads in about 14 seconds. Google estimated around 2,000 ads in total for the domain:

  • 28 text, 11 video, 1 image, all 40 shown in the last 48 hours.
  • Median days running: about 370. Half of the returned creatives had run for more than a year, and the longest for 1,828 days, roughly five years.
  • Not every ad was HubSpot's own. A domain query returns every ad that points to that domain, so a few came from partners. The advertiserName column shows who paid. To see one company's own ads only, query its advertiser ID or use a name search.

Each row has advertiserName, advertiserId, verified, creativeId, format, previewImageUrl or previewUrl, firstShown, lastShown, daysRunning, isActive and adUrl (a link to the creative in the Transparency Center). The longest-running ads are the ones to open first.

Finding the advertiser by name

Don't know a competitor's domain or ID? Search by name and, optionally, pull ads for the best match in the same run:

run = client.actor("steady-scrape/google-ads-transparency").call(run_input={
    "advertiserSearch": ["mailchimp"],
    "fetchAdsForFoundAdvertisers": True,
    "region": "US",
})
Enter fullscreen mode Exit fullscreen mode

In our test, the "mailchimp" search returned one advertiser account with its related domains (mailchimp.com, mailchimpsites.com).

A daily new-ads alert

Monitor mode remembers which creatives you've already received and returns only new ones:

run = client.actor("steady-scrape/google-ads-transparency").call(run_input={
    "targets": ["competitor-one.com", "competitor-two.com"],
    "onlyNewAds": True,
    "lastShownWithinDays": 2,
})
new_ads = list(client.dataset(run["defaultDatasetId"]).iterate_items())
Enter fullscreen mode Exit fullscreen mode

Schedule that daily on Apify (or call it from cron), and send new_ads to Slack, email or a sheet. You can also connect it without code through Make, Zapier or n8n.

Honest limits

  • 120 creatives per query. That's how much the Transparency Center returns, newest first. With format all, the Actor queries text, image and video separately, which lifts this to 360 unique creatives per target. Very large advertisers are covered by their most recently shown ads.
  • No ad copy text yet, and no regional impression ranges. For text and image ads, the preview image shows the rendered copy.
  • Old date ranges can come back empty. Google keeps fewer historical creatives.
  • Counts like totalAdsApprox are Google's rounded estimates.

What it costs

Event Price
Ad row $0.001 ($1 per 1,000)
Advertiser from a name search $0.003
Monitor check, per target after the baseline run $0.008
New ad found in monitor mode $0.003

Errors, "no ads" results and filtered-out ads are free, and Apify's $0.00005 start fee applies per run.

Examples:

  • The 40-ad pull above costs $0.04.
  • A full 360-ad snapshot of one competitor costs $0.36.
  • A daily monitor on 5 competitors costs 5 × $0.008 × 30 = $1.20 a month in checks, plus $0.003 per new creative. Twenty new ads in a month adds $0.06.

You can set a maximum cost per run, and the Actor only fetches what fits in that limit.

The ad library has been public for years, but most teams still check it by hand, if they check it at all. Even a weekly table of "ads running over 180 days" for each competitor tells you a lot about what's working in your market.

Top comments (0)