Google Ads Transparency scraper: export a competitor’s ads with ad copy and regions
Google publishes the ads of every verified advertiser in its Ads Transparency Center (adstransparency.google.com). The site shows them as a grid of previews, one advertiser at a time, with no export, so tracking what a competitor runs means scrolling and screenshotting.
arcadium/google-ads-transparency takes brand names, landing domains or advertiser ids and returns one JSON row per ad: the verified advertiser name and country, format (text, image or video), first and last day shown, days shown, regions and the archived image of the ad. With details on, text ads come with headline, description, display URL and sitelinks where Google still renders them, video ads with their YouTube video, and EU/EEA ads with impression ranges per country and surface.
Cost of this example on the Apify Free plan: 40 × $0.0045 = $0.18 ($0.1352 on the Apify Gold plan and higher), plus $0.001 per GB of memory when the run starts.
What you need
- An Apify account and API token.
- The competitors to look at, as landing domains (
hellofresh.com), brand names or advertiser ids (AR...).
Step 1: Run it in the Apify Store (no code)
- Open arcadium/google-ads-transparency on the Apify Store and sign in (a free Apify account works).
- Enter the competitor under Brands, domains or advertiser links. A landing domain such as
hellofresh.comreturns every advertiser whose ads point to it; a brand name uses the best-matching verified advertisers. - Add the countries under Regions (two-letter codes) and set Shown on or after to
-90dfor the last 90 days. - Turn on Ad details and decoded content for the ad text and set Max ads per advertiser or domain to 40.
- Optional: set a maximum cost per run in the run options as a hard cap.
- Click Start. Rows appear in the Output tab; export them as JSON, CSV or Excel.
The same input as JSON (paste it in the JSON tab of the input form):
{
"searchTerms": [
"hellofresh.com"
],
"regions": [
"US"
],
"startDate": "-90d",
"maxAdsPerAdvertiser": 40,
"includeDetails": true
}
Step 2: Call it over the Apify API
One HTTP call runs the actor and returns its rows. It suits runs up to about five minutes; for longer runs start the run asynchronously and read the dataset when it finishes.
curl -X POST "https://api.apify.com/v2/acts/arcadium~google-ads-transparency/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchTerms":["hellofresh.com"],"regions":["US"],"startDate":"-90d","maxAdsPerAdvertiser":40,"includeDetails":true}'
Step 3: Run it from Python
With apify-client 3.x, call() returns a Run object: read the dataset id as run.default_dataset_id (older examples use run["defaultDatasetId"], which no longer works).
# pip install "apify-client>=3"
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/google-ads-transparency").call(run_input={
"searchTerms": ["hellofresh.com"],
"regions": ["US"],
"startDate": "-90d",
"maxAdsPerAdvertiser": 40,
"includeDetails": True,
})
rows = list(client.dataset(run.default_dataset_id).iterate_items()) # apify-client 3.x
for ad in (r for r in rows if r.get("rowType") == "ad"):
copy = ad.get("headline") or ad.get("youtubeUrl") or ad.get("imageUrl")
print(ad["advertiserName"], ad["format"], ad["firstShown"], "->", ad["lastShown"],
ad.get("daysShown"), "days |", copy)
for e in (r for r in rows if r.get("rowType") == "error"):
print("could not read:", e.get("input"), e.get("code"), e.get("message"))
Step 4: Run it from JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/google-ads-transparency').call({
"searchTerms": [
"hellofresh.com"
],
"regions": [
"US"
],
"startDate": "-90d",
"maxAdsPerAdvertiser": 40,
"includeDetails": true
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
Step 5: Ask an AI assistant (MCP)
Apify’s hosted MCP server turns the actor into one tool. Add it to Claude, Cursor, VS Code or any MCP client:
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/google-ads-transparency",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}Then ask, for example:
Use arcadium/google-ads-transparency with details on to list the Google ads pointing to hellofresh.com that were shown in the US in the last 90 days. Summarise the headlines and offers of the text ads and list the YouTube videos the video ads use.
More clients and the local setup on the MCP page.
Only new and stopped ads, every day
Monitor mode remembers what it saw. The first run records a baseline; each later run returns one summary row per advertiser and one ad-change row per ad that started, came back or stopped (changeType new, resumed or stopped). Save it as a Task with a daily Schedule. Each change row is charged as an Ad, each advertiser summary as Advertiser checked.
{
"mode": "monitor",
"searchTerms": [
"hellofresh.com",
"factor75.com"
],
"regions": [
"US"
],
"monitorName": "meal-kits-us"
}
Find the advertiser first
Big brands verify one advertiser per country entity. Run "mode": "advertisers" with the brand name to list the matching verified advertisers with their advertiserId, country and approximate number of ads, then pass the ids you want in advertiserIds.
Output fields
Every row has a rowType; filter on it first. Failed inputs come back as free rows with a code and message. The fields this tutorial uses, from the actor’s dataset schema:
| Field | What it holds |
|---|---|
rowType | Kind of row. Filter on this first. 'ad': one ad creative (charged as 'ad', or 'ad-detail' when detailed is true). 'ad-change': monitor mode, an ad that is new, resumed or stopped since the last run (charged like an ad row). 'advertiser': one matching advertiser (charged as 'advertiser'). 'summary': monitor mode, one row per advertiser and run (charged as 'advertiser-check'). 'error': an input could not be read; see code and message (free). |
advertiserId | Google's advertiser id (AR...). Use it in 'advertiserIds' to fetch the same advertiser again. |
advertiserName | Verified legal name of the advertiser as Google shows it. null when the name may belong to a private person (see advertiserNameWithheld). |
advertiserCountry | Country the advertiser is based in (verified by Google), two-letter code. |
creativeId | Google's id of the ad creative (CR...). |
adUrl | Page of the ad on adstransparency.google.com (with the region when one region was filtered). |
format | 'text', 'image' or 'video'. Text ads are often archived as an image of the ad (see imageUrl). |
firstShown | Day the ad was first shown, YYYY-MM-DD. |
lastShown | Day the ad was last shown, YYYY-MM-DD. |
daysShown | Number of days Google showed the ad. |
regionsShown | Countries where the ad was shown (two-letter codes); with details, every region in regionStats. |
imageUrl | Archived image of the ad (text and image ads), tpc.googlesyndication.com. |
headline | Decoded headline of a text ad, or product title of a Shopping ad. |
description | Decoded description line of a text ad. |
displayUrl | Decoded display URL of a text ad, e.g. www.example.com/shoes. |
sitelinks | Sitelink texts of a text ad. |
youtubeUrl | YouTube watch link of a video ad. |
regionStats | With details: one entry per region shown: region, firstShown, lastShown, impressionsMin/Max and per-surface impressions (search, youtube, shopping, maps, play) where Google publishes them (EU/EEA, political). |
changeType | Monitor change rows: 'new' (first shown since the last check), 'resumed' (older ad active again) or 'stopped' (not shown in the last activeWithinDays days). |
All fields: Google Ads Transparency Scraper guide.
Cost per run
Pay per event: you pay for the rows delivered, at the actor’s live Apify price on 2026-11-03. Error, coverage and summary rows are free.
| What | Rows | Price per row (Free plan) | Subtotal |
|---|---|---|---|
| Ad with details: 1 domain, up to 40 ads with details | 40 | $0.0045 | $0.18 |
Cost of this example on the Apify Free plan: 40 × $0.0045 = $0.18 ($0.1352 on the Apify Gold plan and higher), plus $0.001 per GB of memory when the run starts. Higher Apify plans pay less per row. The Store page always has the current price.
Limitations
- The cost example assumes the domain has at least 40 ads in the period; fewer ads cost less. Without details each ad is charged as Ad instead of Ad with details.
- Only what the Transparency Center shows anonymously. Google keeps ads for about a year after they were last shown (political ads longer).
- Google archives most text ads as an image of the ad; for those
headlinestays empty andimageUrlholds the ad. Text ads that Google still renders (and Shopping ads) are decoded. - Impression ranges exist only for EU/EEA regions and political ads; ad counts are Google's approximations ("~9,000").
- Region filters with a brand name pick the advertisers first: when a brand runs local entities, raise
maxAdvertisersPerTermor use the local advertiser id. - The actor reads Google over Apify's shared datacenter proxy. When Google rate-limits that pool, the run retries with short pauses and, if the limit persists, stops and reports the remaining inputs as free
blockederror rows instead of burning time. Run again later.
Links
- Google Ads Transparency Scraper: fields, price table and sample output
- How to use Google Ads Transparency Scraper: every input field, n8n, LangChain
- arcadium/google-ads-transparency on the Apify Store
- All tutorials