Competitor Ad Monitor: Google Ads Transparency & Domain Check
Pricing
from $0.80 / 1,000 ad scrapeds
Competitor Ad Monitor: Google Ads Transparency & Domain Check
Competitor ad monitor for Google Ads: schedule it daily or weekly and get only the new ads, with an alert through Apify integrations. Bulk check domains running Google ads: one row per domain, yes or no, with its advertisers. By brand, domain or advertiser ID: dates, days shown, EU reach.
Pricing
from $0.80 / 1,000 ad scrapeds
Rating
0.0
(0)
Developer
Solid Fetch
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 hours ago
Last modified
Categories
Share
Competitor Ad Monitor for Google Ads: Transparency Scraper and Domain Check
Scrape the Google Ads Transparency Center (Google's public ad library) and get every ad a competitor runs, one row per ad. Search by brand name, domain or advertiser ID, then filter by country, date, format and platform (Google Search, YouTube, Shopping, Maps, Play). Each row has the advertiser, the format, the first and last shown dates, the days shown and a link to the ad. Turn on adDetails for ads shown in the EU and each row also gets the impression range and the audience targeting the advertiser chose. No login and no browser. Use it as a scraper from the Console, as an API, or from an AI agent through the Apify MCP server.
What you get for your money
- The brand you asked for, not its namesakes. A name search resolves to the brand's own advertiser account (for Allegro, Allegro sp. z o. o.), and switches to the brand's domain when that has many more ads, as with Temu or Shein. Unrelated companies that share the name stay out of your results and your bill.
maxAdscounts per brand you type. Ask for 50 Allegro ads and you get at most 50, even if Google lists several Allegro-like accounts.- Reach and targeting at the same price per ad. With
adDetailson, EU ads get impression ranges, targeting and per-country dates. It costs no extra per ad; the run takes a few seconds longer. - Days served as Google reports them, so the number matches what you see in the Transparency Center.
What people use it for
- Competitor research. Put in the brands you compete with and see which ads they ran, in which format and for how long.
- Video ads. With the Video format, most video ads come with their YouTube link and thumbnail.
- One platform only. Set
platformtoYOUTUBE,SEARCH,SHOPPING,MAPSorPLAYto get only the ads Google showed there, for example a competitor's YouTube ads or their Shopping ads. - Shopping prices. For shopping ads that carry product data, the actor returns the product title, price, currency and seller. Turn on
onlyWithProductsto keep only those ads. - Who advertises for a domain. Search by a domain to see the advertisers whose ads send traffic to it.
- Market scans by country. Filter by country and dates, for example the last 30 days in Poland.
- New competitor ads every day. Turn on
onlyNewSinceLastRunand schedule the run: each run returns only the ads that appeared since the run before. - Reach and targeting of ads shown in the EU. Turn on
adDetailsto get the impression range of each ad, the audience targeting categories the advertiser used, and first and last shown dates per country.
How to use it
- Type one or more brand names, domains or advertiser IDs. Advertiser page URLs from the Transparency Center work as IDs.
- Optionally pick a country, a date range or a number of recent days, a format (text, image, video) and a platform (Google Search, YouTube, Shopping, Maps, Play).
- Run it and download the results as JSON, CSV or Excel, or read them through the API.
{"advertiserNames": ["Nike", "Allegro", "Temu"],"domains": ["zalando.pl"],"region": "Poland","shownInLastDays": 30,"maxAds": 200}
Each name, each domain and the list of advertiser IDs is a separate search. maxAds applies to each search, and one run takes up to 500 searches. An ad found by two searches is saved and charged once, and its searchQuery field names the search that found it first.
If a name matches no advertiser, the run goes on with the other searches and lists the name in its status message. If no name in the run matches, the run still finishes successfully with no ads and suggests close advertiser names that Google knows, for example No advertiser matched "Adiddas". Did you mean: adidas?
How brand names are matched
Google suggests advertisers for a name. The actor picks the advertiser whose name starts with your word and has the most ads, and it prefers an advertiser from the country you selected. The log lists the other matching advertisers, and you can pass their IDs to get them too.
Some brands run their ads under another company name. Temu has no advertiser called Temu: in a sample of 100 ads that lead to temu.com, 91 came from ELEMENTARY INNOVATION PTE. LTD. and 9 from Whaleco Inc. When no advertiser carries the name, or when the best one is a small namesake with fewer than 1,000 ads and more ads lead to the brand's domain, the actor searches by that domain instead. A domain search returns every advertiser whose ads lead to the domain, so it can include resellers and app publishers. The fields resolvedBy and matchedAdvertiser show which way each search went. For one exact advertiser, use its ID.
Monitor new ads
Turn on onlyNewSinceLastRun and each run returns only the ads that the same search has not returned before and that started running since the last run. Schedule it and it works as a daily alert for new competitor ads.
{"advertiserNames": ["Nike", "Adidas"],"region": "Poland","maxAds": 300,"onlyNewSinceLastRun": true}
- The first run is the baseline. No earlier state exists, so it returns every matching ad, and the log and the run's status message say it is the baseline. From the second run on, only new ads are saved.
- You pay only for new ads. Ads seen by earlier runs are skipped before saving and are not charged. A run with nothing new costs only the start fee.
- What counts as new. An ad is new when it is not in the list of seen ads and its first-shown date is not earlier than 2 days before the start of the previous run. An ad that is not in the list but started long before is an old ad that this run happened to read for the first time, because Google returned a different slice of a large advertiser. It is added to the list, not saved and not charged. The log and the status message say how many were skipped this way. An ad without a first-shown date counts as new. The first run has no earlier run to compare with and returns every ad, and so does the run after a first run that stopped at the maximum cost. A later run that stops at the maximum cost, or that searched nothing because no input value could be used, leaves the comparison point where it was, so the ads it did not get to stay new.
- How deep each run looks, and what it can miss. Google lists the ads by last-shown date, newest first, not by first-shown date, and every ad that runs today has today as its last-shown date.
maxAdsis how many ads each search reads. For a brand that runs more ads at once thanmaxAds, which ones you get among the running ads is up to Google, so a new ad can fall outside the ads read that day and show up in a later run, as long as it started within the last 2 days before that run, or not at all. SetmaxAdsabove the number of ads the brand runs at the same time when you can; for brands with hundreds of thousands of ads, use a country, format or platform filter to narrow the search. - Older lists keep working. A list saved before this rule existed has no start time of its own, so the first run after the update takes the day the list was last saved as the previous run.
- One list per search. The list of seen ads belongs to the names, domains, IDs, country, format and platform. Changing
maxAds, dates oradDetailskeeps the same list, changing the country or a name starts a new baseline. GivemonitorNamea name to choose the list yourself, for example to share it between two tasks. - Still running ads. With
includeLastShownChanges, the run also saves ads seen before whose last-shown date moved since the last run. - Each new row has
changeType(neworlastShownChanged) andpreviousLastShown. - If a run stops at the maximum cost per run, the ads it did not save stay new for the next run.
- The list is kept in the key-value store
google-ads-transparency-stateon your account and holds up to 50,000 ads per name, somaxAdstimes the number of searches may be at most 50,000. When the list is full, the ads seen longest ago are forgotten and count as new if they come back. Two runs with the same name at the same time do not lose each other's ads.
To schedule it in Apify Console:
- Fill in the input above and click Save as a new task.
- Go to Schedules in the left menu, create a schedule with a time, for example every day at 8:00, and add the task to it.
- Run the task once by hand to save the baseline.
- To get the new ads by email, Slack or a webhook, add an integration on the task for "Run succeeded".
Check which domains advertise
Set mode to domainCheck to answer one question for a list of domains: does this domain advertise on Google? Each domain gets one row from one small request to Google, with no preview or detail requests.
{"mode": "domainCheck","domains": ["nike.com", "https://www.zalando.de/damen/", "example-shop.pl"],"region": "Poland"}
A row for nike.com, built from a recorded Google response:
{"domain": "nike.com","input": "https://www.nike.com/pl/","hasAds": true,"advertisers": [{ "id": "AR16735076323512287233", "name": "Nike, Inc." },{ "id": "AR18378488041124659201", "name": "Nike Retail BV" }],"adCountEstimate": { "min": 100000, "max": 200000 },"latestShown": "2026-10-03T08:58:03.000Z","formats": ["TEXT"],"sampleFormatCounts": { "text": 5, "image": 0, "video": 0 },"sampleSize": 5,"likelyOwner": { "id": "AR16735076323512287233", "name": "Nike, Inc.", "ids": ["AR16735076323512287233"] },"ownerMatch": "name-match","otherAdvertisers": [{ "id": "AR18378488041124659201", "ids": ["AR18378488041124659201"], "name": "Nike Retail BV", "nameMatch": true }],"activeLast30Days": true,"daysSinceLastAd": 10,"adsLast30Days": 5,"adsLast30DaysIsSample": true,"firstSeen": "2023-11-16T22:23:39.000Z","checkedAt": "2026-10-13T12:00:00.000Z","region": null,"error": null}
inputis the value exactly as you sent it (https://www.nike.com/pl/), so you can map a row back to your list.domainis the cleaned host.hasAdsistruewhen the Transparency Center lists at least one ad that leads to the domain, in the chosen country if you setregion. It is not limited to ads running today: look atlatestShownto see how recent they are.advertiserslists up to 5 advertisers from the first 20 ads Google shows for the domain, with their IDs, so you can pass an ID to the ads mode to get that advertiser's ads.adCountEstimateis the rounded total Google reports for the domain, as a range. It is{ "min": 0, "max": 0 }for a domain without ads.formatsare the formats seen among those first 20 ads (TEXT,IMAGE,VIDEO), not across every ad of the domain.likelyOwnerandownerMatchsay who probably owns the domain, from the same first 20 ads.name-match: the advertiser's name starts with the domain's brand or has it as a word (for nike.com, Nike Retail BV; for booking.com, Booking.com B.V.), and with several such companies the one with the most ads on the page wins, except that a branch (a name with Podruznica, Branch, Filiale, Sucursal and the like, as in "Samsung Electronics Austria GmbH Podruznica Zagreb") loses to any other company of the brand. Advertiser IDs that carry the same name (booking.com, mcdonalds.com and hm.com each have one company under two IDs) count as one company, andlikelyOwner.idslists all of its IDs, the one with the most ads first (id).largest-advertiser: no name fits, so it is the advertiser with the most ads on the page, which is a guess. Both arenullfor a domain without ads. It is a guess from names and a 20-ad sample, not a verified owner, and a brand whose company has a different name (Temu, run by Whaleco) getslargest-advertiser.otherAdvertiserslists up to 5 more companies from the page, one entry per name with all its advertiser IDs inids(idis the one with the most ads), each withnameMatch.falsemeans the name does not fit the brand, which is how agencies and resellers (for nike.com, WPP and M Media Group) show up;trueis another company of the brand.activeLast30DaysistruewhenlatestShownis at most 30 days beforecheckedAt, andfalsefor a domain with no ads.daysSinceLastAdis the whole UTC days fromlatestShowntocheckedAt, ornullwithout ads. Both rest on the latest last-shown date among the first 20 ads, which Google lists newest first.sampleFormatCountscounts the ads on the fetched page astext,imageandvideo, andsampleSizeis how many ads that is (at most 20); ads of an unknown format are in none. It is a sample, not the domain's totals: Google's answer carries no per-format totals, and getting them would take extra requests per domain. Do not read it as a distribution of the domain's 80,000 ads. (This field was calledformatCountsuntil 21 October 2026; it was one day old, and the old name suggested totals, so it was renamed without an alias. Update a saved view or Clay column that used it.)adsLast30Daysis how many of the fetched ads were last shown within 30 days ofcheckedAt.adsLast30DaysIsSampleistruewhen Google has more ads than the page held, so the number is a lower bound;falsemeans the page held every ad.firstSeenis the earliest first-shown date among the fetched ads, not necessarily the domain's first ad. All three come from the page already fetched, with no extra request.- A domain that could not be checked has
nullin all the sample and activity fields,likelyOwner,ownerMatch,activeLast30DaysanddaysSinceLastAd. - Up to 1,000 domains per run. Full URLs work: the scheme, path and
www.are removed, international names are saved as punycode (żółć.plbecomesxn--kda4b0koi.pl, however you write it), and a domain listed twice is checked once, with theinputof its first mention. - Not a domain, not checked, not charged. IP addresses, e-mail addresses (
mailto:foo@bar.com), empty strings and names without a public suffix (localhost,printer.local) each get a row withhasAds: nulland the reason inerror. The check looks at the last label (a plain top-level domain such as.com,.plor a punycode one), it does not use the full public suffix list. - You pay for each answer, including
hasAds: false. A domain that could not be checked (Google kept failing after the retries, or the value is not a domain) is saved withhasAds: nulland anerror, and is not charged. One failing domain does not stop the run; after 10 failed domains in a row the run stops, saves the rest as not checked and fails, since Google is then blocking it. - Two domains are checked at the same time. The run goes back to one request at a time after any failed domain or any CAPTCHA/429 from Google, and rows are always saved in input order.
- Only
domainsandregionare used in this mode. The ads-mode filters,maxAdsandonlyNewSinceLastRunare ignored, and the log names the ones you set.
Look up advertisers
Set mode to advertisers to find the advertiser accounts behind brand names and domains, with their IDs, before you scrape their ads or to map a list of brands to Transparency Center accounts. Each advertiser is one row.
{"mode": "advertisers","advertiserNames": ["Nike", "Allegro"],"domains": ["zalando.de"],"region": "Poland"}
The first row for Nike, built from a recorded Google response:
{"query": "Nike","queryType": "name","advertiserId": "AR18378488041124659201","name": "Nike Retail BV","country": "NL","adCountEstimate": { "min": 80000, "max": 90000 },"isBestMatch": true,"transparencyUrl": "https://adstransparency.google.com/advertiser/AR18378488041124659201","region": null,"checkedAt": "2026-10-13T12:00:00.000Z","error": null}
- A name returns every advertiser Google suggests whose name contains that name as a whole word: Nike finds Nike Retail BV, Nike, Inc. and NIKE SRL, but not Nikesh. The best match comes first, then the others by ad count.
isBestMatchmarks the account the ads mode searches for that name (see How brand names are matched). When the ads mode would search the brand's domain instead, as for Shein or Temu, no row of that name is marked. Domains never have a best match.- A domain returns the advertisers on the first 40 ads that lead to it, in the chosen country if you set
region. Theircountryisnull, since Google lists it only with name suggestions. countryis the country of the advertiser account as Google lists it.adCountEstimateis Google's rounded count of the advertiser's ads in all countries, as a range.- An advertiser found by two names or domains is saved and charged once, under the first one that found it.
- Up to 500 names and domains per run. Duplicates are looked up once, and full URLs work for domains.
- Advertiser lookups are free for now; a per-advertiser price will be announced on the Pricing tab two weeks ahead. A name or domain without advertisers is saved once with
advertiserId: nulland the reason inerror, and is not charged. A lookup that could not be done (Google kept failing after the retries, or the value is not a name or a domain) is saved the same way, free. After 10 failed lookups in a row the run stops, saves the rest as not looked up and fails, since Google is then blocking it. - Only
advertiserNames,domainsandregionare used in this mode. The other filters are ignored, and the log names the ones you set.
Input
| Field | Description |
|---|---|
mode | ads (default) returns ads. domainCheck returns one row per domain in domains saying whether it advertises. See Check which domains advertise. advertisers returns one row per advertiser account matching advertiserNames or running ads for domains. See Look up advertisers. |
advertiserNames | Brand or advertiser names, one search each. Names that differ only in case or punctuation (Nike, nike, NIKE ) are searched once, and the log says so. |
advertiserNamesText | The same names as one text, one per line (or separated by commas when there is no line break). For tools that send a column as one value, see Use it from Clay, Google Sheets or Make. |
advertiserIds | Advertiser IDs (AR followed by 20 digits) or advertiser page URLs. All IDs are searched together. An ID that is not valid does not stop the run: it is saved as a free row with searchQuery (your value) and error, and the valid IDs are searched. |
startUrls | Links copied from adstransparency.google.com: an advertiser page or a creative page (that advertiser is searched) and a domain search page (that domain is searched). A region=PL in the links sets region when it is empty. Other links are saved as free rows with an error. |
domains | Domains the ads point to, one search each. In domain check mode, the domains to check (up to 1,000). Full URLs work: the actor keeps the host and drops www.. |
domainsText | The same domains as one text, separated by line breaks, spaces, commas or semicolons. |
region | Country as a two-letter code (US, PL, DE, UK) or an English name (Poland, United Kingdom). Empty means all countries. |
dateFrom, dateTo | Ads shown at some point inside this range (YYYY-MM-DD). Google allows this filter only for the last 364 days. If you set one date, the actor fills the other. |
shownInLastDays | Ads shown at some point in the last N days, today included (1 to 364). An ad that started months ago and still runs is included; for only new ads use onlyNewSinceLastRun. Use it instead of fixed dates in saved tasks and schedules, so the range moves with every run. |
format | ALL, TEXT, IMAGE or VIDEO. |
platform | ALL, SEARCH, YOUTUBE, SHOPPING, MAPS or PLAY: only ads Google showed on that platform. |
videoLinks | Adds videoUrl and thumbnailUrl to video ads. On by default for the Video format, off for the others. |
previewImages | On by default. For image ads that Google only shows through a preview, reads that preview file (about 0.2 MB, 4 at a time) and fills imageUrl. It adds roughly 0.25 s per such ad to the run and does not change the price. Turn it off for the fastest run. |
maxAds | Ads per search. Maximum 100,000. When you do not send it, each search stops at 100 ads and the log opens with a warning that names the cap and the most a run can charge. |
adDetails | Off by default. Reads each ad's detail page and adds impressionsMin, impressionsMax, impressionsAvailableFrom, targeting and regionStats. This is one extra request per ad, so runs take longer. The price per ad stays the same. |
downloadCreatives | Off by default. Saves each ad's image as a file in the run's key-value store and adds creativeFileUrl. Stored files are free for now (a per-file price will be announced on the Pricing tab with the usual two weeks' notice). See Download the ad images. |
onlyNewSinceLastRun | Off by default. Saves only ads this search has not returned in earlier runs. See Monitor new ads. |
monitorName | Optional name of the list of seen ads, for onlyNewSinceLastRun. Empty means one list per search. The old name stateKey still works. |
includeLastShownChanges | With onlyNewSinceLastRun, also saves ads seen before whose last-shown date moved. |
onlyWithProducts | Keep only shopping ads that come with product data. The other ads are skipped before saving, so you are not charged for them, and maxAds counts the ads kept. |
proxyConfiguration | Apify Proxy is on by default. When Google answers with a CAPTCHA page, the actor retries on a new IP. With the default Apify Proxy, the first CAPTCHA moves the run to Apify residential proxy when the run has access to it. |
Output
A real video ad row:
{"searchQuery": "Nike, Inc.","resolvedBy": "advertiser","matchedAdvertiser": "Nike, Inc.","advertiserId": "AR16735076323512287233","advertiserName": "Nike, Inc.","domain": null,"creativeId": "CR07320027051652546561","format": "VIDEO","firstShown": "2026-07-07T14:21:15.000Z","lastShown": "2026-10-03T03:04:46.000Z","daysShown": 89,"imageUrl": null,"videoUrl": "https://www.youtube.com/watch?v=grj7FyOMWh8","thumbnailUrl": "https://i.ytimg.com/vi/grj7FyOMWh8/hqdefault.jpg","landingUrl": "https://www.nike.com/ca/retail","previewScriptUrl": "https://displayads-formats.googleusercontent.com/ads/preview/content.js?client=ads-integrity-transparency&...","transparencyUrl": "https://adstransparency.google.com/advertiser/AR16735076323512287233/creative/CR07320027051652546561","headline": null,"description": null,"displayUrl": null,"businessName": null,"sitelinks": [],"products": []}
| Field | Description |
|---|---|
searchQuery | The name, domain or ID list that found the ad. |
resolvedBy | How the search found its target: advertiser (a name matched an advertiser), domain (a name was searched as the brand's domain), id or domain-input (you gave IDs or a domain). |
matchedAdvertiser | The advertiser name, domain or ID list the search ran on. |
advertiserId, advertiserName | Advertiser as Google shows it. |
domain | Filled when the search ran on a domain. |
creativeId, transparencyUrl | The ad's ID and its page in the Transparency Center. |
format | TEXT, IMAGE, VIDEO, or UNKNOWN for a format code the actor does not know. |
firstShown, lastShown, daysShown | First and last time Google served the ad, in UTC, and the number of days Google served the ad, as the Transparency Center reports it (when Google leaves it out, the calendar days from the first to the last, both included). |
imageUrl | An image of the ad, when Google has one. Most text ads have it with Google's own image link. Image ads without one get Google's thumbnail of the ad image or product photo (encrypted-tbn…gstatic.com) from their preview, unless previewImages is off or the preview could not be read. Text ads that carry only a preview, and video ads, keep it empty. |
creativeFileUrl | With downloadCreatives on, the link to the ad's image file in the run's key-value store. Null when downloadCreatives is off, the ad has no image or the download failed. |
videoUrl, thumbnailUrl | YouTube link and thumbnail of a video ad. |
landingUrl | The landing page URL of the ad, when Google's preview includes it. Filled for some video and image ads only (from the preview file the actor already reads for videoUrl and imageUrl); null for text ads and for every ad whose preview has no advertiser link. No extra requests and no extra cost. |
previewScriptUrl | Google's preview script for ads that Google renders in a browser. It is a JavaScript file that draws the ad in a browser, not an image. |
products | Shopping ads: { title, price, currency, seller, url } for each product. |
headline, description, displayUrl, businessName, sitelinks | Ad text, only when Google puts it into the preview. |
EU ad details
With adDetails on, every row gets these fields. A real Allegro search ad shown in Poland and Germany:
{"impressionsMin": 20000,"impressionsMax": 25000,"impressionsAvailableFrom": null,"targeting": {"included": ["demographics", "locations", "contextualSignals"],"excluded": ["contextualSignals", "customerLists"]},"regionStats": [{"country": "PL","firstShown": "2023-03-01","lastShown": "2026-10-03","impressionsMin": 20000,"impressionsMax": 25000,"platforms": [{ "platform": "YOUTUBE", "impressionsMin": 0, "impressionsMax": 1000 },{ "platform": "GOOGLE_SEARCH", "impressionsMin": 20000, "impressionsMax": 25000 }]},{"country": "DE","firstShown": "2023-03-04","lastShown": "2026-08-23","impressionsMin": 0,"impressionsMax": 1000,"platforms": [{ "platform": "GOOGLE_SEARCH", "impressionsMin": 0, "impressionsMax": 1000 }]}]}
| Field | Description |
|---|---|
impressionsMin, impressionsMax | Google's impression range for the ad, summed over the countries where Google reports impressions. The top range is open, so an ad shown more than 10 million times has impressionsMin 10000000 and impressionsMax null. |
impressionsAvailableFrom | Google publishes impressions about 90 days after an ad starts. Until then the range is null and this field holds the date Google gives for it. |
targeting | Audience targeting categories from the advertiser's latest campaign: demographics, locations, contextualSignals, topicsOfInterest and customerLists. A category can be in both lists when some of its criteria were included and others excluded. Categories the advertiser did not use are in neither list. Google shows the categories only, not the exact ages, places or topics. |
regionStats | One entry per country where the ad ran, the countries with the most impressions first: country (two-letter code), firstShown and lastShown dates, the impression range in that country and its split by platforms (GOOGLE_SEARCH, YOUTUBE, GOOGLE_SHOPPING, GOOGLE_MAPS, GOOGLE_PLAY). |
These fields are null when adDetails is off. If the details of an ad cannot be read, the ad is still saved without them and its regionStats is null, while an ad whose details were read always has a regionStats list. The log says how many ads on each page were affected, and the run's status message gives the total. After 20 failed detail requests in a row the actor stops asking for details for the rest of the run and says so in the status message.
What we measured on 2026-10-04:
- Google gives impressions and targeting for ads shown in the EU. We also saw impression ranges for Norway, Iceland and Turkey. For other countries, such as the US, Brazil or Japan,
regionStatshas only the last shown date and the ad has no impressions or targeting. - On the newest 37 ads of Allegro, Zalando, mBank, InPost and Lidl, all 37 had targeting and 19 had an impression range. The other 18 had started less than 90 days before, and each of them had an
impressionsAvailableFromdate. - A US Nike text ad and a US Nike video ad came back with one country entry, the US, with its last shown date only.
What to expect from the other fields, from our test runs:
- Dates, advertiser, format and the ad page link are filled for every ad.
- 21 of 30 Samsung video ads and 26 of 40 video ads leading to nike.com came with a YouTube link. The other video ads had an image instead, or their preview held no YouTube video.
- Product data came with 12 of 40 Allegro image ads in Poland. Brands that do not run shopping ads have none.
- Ad text is rare: Google put a headline into the preview for about 1 to 2 ads in 100.
Download the ad images
Google's image links can stop working when an ad is taken down. Turn on downloadCreatives to keep a copy: the actor downloads the image in imageUrl of each saved ad (the archived ad image or the picture from its preview) and stores it in the run's default key-value store under creative-<creativeId>.<ext>, for example creative-CR13820127804068462593.png. The row gets creativeFileUrl, the link to that record:
{"creativeId": "CR13820127804068462593","format": "IMAGE","imageUrl": "https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9Gc...","creativeFileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/creative-CR13820127804068462593.png"}
- Video ads are not downloaded as video. They keep their YouTube link in
videoUrl; only when Google has an archived still image of a video ad inimageUrlis that image stored. - Only JPEG, PNG, GIF, WebP and AVIF files up to 5 MB are stored. Each download gets 20 seconds, 4 run at a time.
- A failed download leaves
creativeFileUrlnull, is not charged and never fails the run. After 5 failed downloads in a row the actor stops downloading for the rest of the run, saves the remaining ads withcreativeFileUrlnull and says so in the status message. - With
previewImagesoff, only images that Google archives are stored, not the pictures from ad previews. - Stored files are free for now; a per-file price will come later with two weeks' notice. Ads without an image cost the same as before.
Ready-made searches
Open one of these, change the input and run it:
- See every Google ad a competitor is running
- Find who advertises on Google for a domain
- Track Google Shopping ad prices for a retailer
- Download a brand's Google and YouTube video ads
- See which Google ads a brand runs in one country
- Get all ads of a Google advertiser ID
- Find a brand's Google ads from the last 30 days
Use it from Clay, Google Sheets or Make
These tools call the actor through the Apify API and send a spreadsheet column as one value, so the text fields take a whole column. This call asks "does this domain run Google ads?" for a list of domains and returns one row per domain, with your own value in input:
curl -X POST "https://api.apify.com/v2/acts/solidfetch~google-ads-transparency-scraper/run-sync-get-dataset-items?token=<YOUR_TOKEN>&maxTotalChargeUsd=1" \-H "Content-Type: application/json" \-d '{"mode": "domainCheck", "domainsText": "nike.com\nzalando.pl\nallegro.pl", "region": "PL"}'
domainsTextandadvertiserNamesTexttake one text: one value per line, or separated by commas, semicolons or spaces for domains. A brand name with a comma (Nike, Inc.) goes on its own line. The same fields work inadsandadvertisersmode. ThedomainsandadvertiserNameslists keep working, and also take a single string through the API.- Map rows back with
input. Every domain check row carries the value you sent. Values that are not domains (an IP address, an e-mail address, an empty cell) come back as free rows withhasAds: nulland anerror, so the row count matches your list, apart from repeated domains, which are checked once. - Set the limits in the caller. In
adsmode sendmaxAds(ads per search) and the maximum cost per run: addmaxTotalChargeUsd=<dollars>to the API URL, or set it in the integration. A tool that does not passmaxAdsgets the default of 100 ads per search, and the run log starts with a warning that says so. Domain check mode charges one answer per domain and needs only the maximum cost. - Pasted Transparency Center links go in
startUrls({"startUrls": [{"url": "https://adstransparency.google.com/advertiser/AR16735076323512287233"}]}) inadsmode. - Google Sheets and Make read the rows from the finished run's dataset, see Send results to Make, Zapier, n8n or a CRM.
Send results to Make, Zapier, n8n or a CRM
Long runs do not need to keep a Make or Zapier scenario waiting:
- Start the actor or one of its saved tasks on a schedule, or from your scenario with the asynchronous "Run Actor" step.
- Trigger the next step on the finished run. In Make use the Apify module "Watch Actor Runs", in Zapier the Apify trigger for a finished actor run, in n8n the Apify node.
- Read the rows with "Get Dataset Items" using the run's dataset ID.
For your own endpoint, open the actor's Integrations tab and add an HTTP webhook on "Run succeeded". Apify retries failed deliveries, and the payload contains resource.defaultDatasetId, which you can read at https://api.apify.com/v2/datasets/<datasetId>/items?format=json. Add &format=csv for a spreadsheet, or &flatten=<field> to turn nested objects into flat columns.
Pricing
You pay $1.50 per 1,000 saved ads on the free plan, $1.20 on Bronze, $1.00 on Silver and $0.80 on Gold and above. Every run also pays a start fee of $0.00005, including runs that fail or save nothing. A run that saves no ads costs only that start fee. If a run fails partway, the ads it saved before the failure stay in the dataset and stay charged. There is no extra charge for platform usage, and the run stops when it reaches the maximum cost per run that you set. The $5 of monthly free credit covers about 3,300 ads.
With downloadCreatives on, stored image files are free for now. A per-file price will be added later, announced on the Pricing tab two weeks ahead. Failed downloads and ads without an image will never be charged for a file.
Domain check mode is charged per checked domain instead of per ad: $6 per 1,000 domains on the free plan, less on paid plans, from 28 October 2026. Until then domain checks are free; you pay only the small start fee. Domains that could not be checked are never charged.
Advertiser lookup mode is free for now; a per-advertiser price will be added later with two weeks' notice. Names and domains without advertisers, and lookups that failed, will never be charged.
FAQ
Why do I get fewer ads than the Transparency Center website shows? The website shows a rounded estimate ("about 9,000 ads"). The actor returns the ads Google actually lists, and maxAds limits each search.
Can I get ads older than a year? Yes, leave the dates and shownInLastDays empty. Google allows the date filter only for the last 364 days.
Why are impressions empty for some EU ads? Google publishes the impression range about 90 days after an ad starts. impressionsAvailableFrom shows the date. Ads that ran only outside the EU have no impressions or targeting at all.
Does it need a Google account or cookies? No. It reads the same public data as the website.
Can I run it on a schedule? Yes. Save your input as a task in Apify with shownInLastDays instead of fixed dates and add a schedule, for example every Monday. Turn on onlyNewSinceLastRun to get only the ads that are new since the last run.
Use it from code or AI agents
curl -X POST "https://api.apify.com/v2/acts/solidfetch~google-ads-transparency-scraper/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-H "Content-Type: application/json" \-d '{"advertiserNames": ["Nike"], "maxAds": 50}'
The actor is pay per event with limited permissions, so it works through the Apify MCP server in Claude, Cursor and other AI agents, and with Apify integrations for Make, n8n, Zapier and Google Sheets.
Notes
- A name, domain or ID list that finds no ad is named in the run's status message (
Saved 4 ads. No ads for: shop.nike.com), and a name that matches no advertiser is listed too. An input value that cannot be searched (a bad advertiser ID, a link that is not a Transparency Center page) is saved as a free row withsearchQueryanderror. - The country filter uses Google's own targeting data.
- If Google changes its response format, the run fails with an error instead of finishing with an empty dataset.
- Found a problem? Open an issue on the actor page and include the input you used.
- When a run fails, an error report with the error message and the shape of the input (which fields are set, how long the lists are), never your input values or your results, is sent to the developer to fix bugs.
This is an independent tool and is not affiliated with Google.
Related actor
Core Web Vitals Competitor Benchmark compares the real-user page speed (LCP, INP, CLS) of the same competitors, from Google's Chrome UX Report.
If this actor saved you time, a short review on its Store page helps other people find it. If something broke or a field is missing, open an issue and I will answer within a day.