Google Ads Transparency Monitor: New Competitor Ads
Pricing
from $7.00 / 1,000 competitor checks
Google Ads Transparency Monitor: New Competitor Ads
Get only your competitors' NEW ads from Google Ads Transparency Center as a daily Slack/webhook digest per competitor and client. Flags launches and new countries. $0.01 per run + $0.01 per competitor check + $0.005 per new ad. First check: free 30-day baseline summary. Not affiliated with Google.
Pricing
from $7.00 / 1,000 competitor checks
Rating
0.0
(0)
Developer
Sumitsubo
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
9 days ago
Last modified
Categories
Share
Get a Slack or webhook digest of your competitors' NEW ads from Google Ads Transparency Center - nothing you've already seen. Independent tool, not affiliated with or endorsed by Google. It reads only publicly available ad library pages and does not log in. Pricing: $0.01 per run (run start) + $0.01 per competitor check + $0.005 per new ad; very large advertisers add $0.0005 per 40 ads read beyond 200 (the ads read in each country, added up over its countries). A day without new ads costs $0.01 + $0.01 per competitor checked, plus large-inventory pages when a competitor's read passes 200 ads, added up over its countries (each run reads the ads shown since the previous check plus the overlap, in every country; an ad shown in 3 countries counts 3 times). The first check of a competitor records a 30-day baseline: the baseline summary is free, the check itself is billed as usual (plus large-inventory pages for very large advertisers). Enter competitor domains, press Start, see results in about a minute. Then schedule it daily.
- Watches Google Ads Transparency Center by domain or advertiser ID, per country.
- Reports only ads it has not reported before, grouped per competitor (and per client, if you run an agency).
- Flags campaign launches (many new ads in 7 days), new countries (a watched country the competitor was not seen advertising in before, including known ads spreading to it) and new ad formats.
- Reads each competitor's list newest first and stops at the previous check (minus an overlap, default 48 hours, so ads that show up late in the Transparency Center are still caught). Competitors with many ads shown since then are read deeper, up to your read limit; their recently shown ads are read again on every run. Your memory of seen ads stays in your own Apify storage.
What you get
Slack digest (one message per run):
Competitor ad alerts: 7 new ads across 2 competitorsClient: AcmeExample Brand · Google (GB, US)◆ Campaign launch: 6 new ads in 7 days6 new · about 100-200 ads live• new · IMAGE · first shown 2026-10-10 · US (links to the ad)• new · VIDEO · first shown 2026-10-10 · US,GB...Other Brand · Google (DE)1 new• back again · VIDEO · first shown 2026-09-28 · DENo new ads: Brand C, Brand DRun ... · full rows in the dataset · dates in UTC
Competitors with news are listed first (in every client they belong to); competitors with nothing to say are folded into one line per client and reason ("No new ads: ...", "Skipped (checked recently): ...", "Not checked (maximum charge reached): ..."), so a long watchlist does not push the news out of the message. The header counts only the competitors that were actually checked; if none could be checked it says so. English digests show dates in UTC, Japanese digests in Japan time.
Dataset row (one per new ad, type: "new_ad"):
{"type": "new_ad", "platform": "google", "category": "new_launch","label": "Example Brand", "client": "Acme", "advertiserName": "Example Brand Inc.","adId": "CR00000000000000000000", "format": "IMAGE","firstShownAt": "2026-10-10T07:12:00.000Z", "lastShownAt": "2026-10-11T05:40:00.000Z","regions": ["US"], "previewUrl": "https://adstransparency.google.com/advertiser/AR.../creative/CR...?region=US","previewImageUrl": "https://tpc.googlesyndication.com/simgad/...", "detectedAt": "2026-10-11T06:00:00.000Z"}
Each run also writes one free type: "advertiser_summary" row per competitor with its status, counts, signals and (on the first check) the baseline summary. On a competitor's first check, the 5 latest ads of the baseline are also written as free type: "baseline_sample" rows, so the New ads view shows what a check returns before the first real new ad arrives. Filter on type in n8n/Make/Zapier if you only want new ads. The dataset has two views: New ads and Per-competitor status.
Input
{"watchlist": [{ "label": "Patagonia", "client": "Acme", "googleDomain": "patagonia.com", "regions": ["US", "GB"] },{ "label": "Competitor DE", "googleAdvertiserId": "ARxxxxxxxx", "regions": ["DE"] }],"slackWebhookUrl": "https://hooks.slack.com/services/...","maxChargeUsd": 1,"language": "en"}
| Field | What it does |
|---|---|
label / client | Your names. The digest is grouped by client. |
googleDomain | The advertiser's website domain as shown in the Transparency Center (e.g. patagonia.com, not "Patagonia"). A Transparency Center advertiser URL pasted here is used as the advertiser ID. |
googleAdvertiserId | More precise: the AR... id, or paste the Transparency Center advertiser URL. Recommended when a domain returns nothing or too much. |
regions | Countries to check (ISO codes like US, GB, JP, or anywhere; UK is read as GB). A list, or a comma-separated text such as "US, GB". Default US. An empty entry is an error. One check covers all listed countries; at most 20 countries per competitor (also when it is listed on several entries). |
slackWebhookUrl, webhookUrl | Optional, stored as secrets. Slack: an Incoming Webhook URL (https://hooks.slack.com/services/...). A URL that cannot receive the digest as written (a Slack workflow or trigger URL, a webhook that is not https, an internal address) is refused before anything is read or charged: the run ends as aborted with "Invalid input" (the URL itself is not shown). A Slack message holds at most 50 blocks and about 40,000 characters; competitors beyond that are counted as "+N more competitor entries in the dataset". Without them, results are in the dataset and the run log. A failed delivery is re-sent to the same URL with the next run as a summary (new-ad counts, signals and the top 5 ads per competitor; the full rows are in the dataset). A summary too long for one Slack message is spread over the following messages, each competitor once. Webhooks must be public https addresses; redirects are not followed; each delivery carries an idempotencyKey. A webhook body is kept under 1 MB: above that each competitor lists its top 5 new ads (with the full count), and competitors or carried runs that still do not fit are left out and counted in truncated, which points to the dataset (every row is there). |
maxChargeUsd | Default $1. The run stops between competitors before its charges would exceed this; competitors are taken least recently checked first, so the ones left out go first next run. A competitor that alone does not fit in what is left (a very large inventory) is listed as not checked and the run goes on with the others. A competitor that was read but could not be paid for goes after the others next run, and while what is left of the cap stays below its last read it is not read again (for up to 7 days; the estimate is at most what the current read limit allows, and is dropped when its countries change). The run reports the amount that would cover every competitor (run start, checks, the large-inventory pages of the competitors read, and the new ads seen). The lower of this and the platform's "Maximum cost per run" applies. For example 120 competitors cost about $1.21 per run in checks alone; the run warns when the checks exceed the cap. |
maxNewAdsPerAdvertiser | Default 50. Beyond that, new ads are counted but not listed or billed. |
minHoursBetweenChecks | Default 1. A competitor whose last check was in a run that started less than about this many hours ago (10% slack for schedule jitter) is skipped (not read, not billed). 0 turns it off. |
maxRunMinutes | Default 30, at most 120. No new competitor is started after this (or 2 minutes before the platform timeout); the rest go first next run. |
stateKey | Optional name of the seen-ads memory. In a task, leave it empty: each task gets its own memory automatically. Outside a task (API calls, a schedule that starts the Actor itself), an empty state key uses the shared memory main. The memory is kept per competitor, so editing the watchlist keeps it: an added competitor starts with a free baseline, a removed one is simply no longer checked (added back after a long pause, it gets a fresh free baseline). To run a different watchlist outside a task, give it its own stateKey: two watchlists on one key share one memory, so one would miss the new ads the other already saw. The run log and status message warn when one key is used with different Slack/webhook destinations. |
The same competitor listed for two clients is checked and billed once per run, shown under both clients, and read in the union of the countries of both entries (so client A may also see ads from client B's countries). If you enter the same Google advertiser once by domain and once by advertiser ID, both are read, but when both reads return the same ads (or both return nothing and memory already knows they are the same advertiser) only one check is billed and each new ad is billed once.
Pricing (pay per event)
| Event | Price | When |
|---|---|---|
| Run start | $0.01 | once per run that delivers a result |
| Competitor check | $0.01 | one competitor read: all its countries, or as many as the read limit allowed (the unread countries are read first next run) |
| Large inventory page | $0.0005 | per 40 ads read beyond the first 200 for one competitor (the ads read in each country, added up over its countries) |
| New ad | $0.005 | per new ad delivered |
| Baseline row | $0.001 | only if you turn on "Write baseline ads as dataset rows" |
Free: the first-check baseline summary (counts, countries, formats, latest 10) and its 5 sample rows (the first check itself is billed like any check), the digest, launch/new-country/new-format signals, quiet-day status.
Not billed: competitors that failed or were read only partly, competitors skipped as recently checked, and a competitor read a second time in the same run.
Competitor checks and new ads are discounted on higher Apify plans (for example $0.008 per check and $0.004 per new ad on SILVER). When maxChargeUsd (or the platform's maximum cost) is reached, the run stops between competitors before reading the next one, lists the rest as "not checked", and keeps any unpaid new ads for the next run; a single competitor too large for what is left is listed as "not checked" while the others are still checked. maxChargeUsd: 0 reads nothing.
Example monthly cost: 5 competitors, daily, ~60 new ads/month - about $2 (30 x ($0.01 + 5 x $0.01) + 60 x $0.005). 20 competitors (4 clients x 5) daily with ~200 new ads/month - about $7.30.
What counts as "new"
| Category | Meaning | Billed |
|---|---|---|
new_launch | First shown after the previous check minus the overlap (and inside the baseline window minus the overlap, and not before the window of the country's first check) | yes |
reactivated | Not seen before, first shown earlier but inside that window, shown again after the previous check | yes |
first_seen_old | First check of a competitor or country (the baseline); an ad first shown before the window (or before the window of the country's first check); an ad the previous check did not reach because of the read limit | no |
- Ads already reported are not reported again. The memory keeps every ad first shown in the last 90 days (up to 20,000 per competitor; beyond that the oldest are dropped, and an unseen ad first shown before the newest dropped one is not billed), plus up to 5,000 older ones per competitor (shown in the last 180 days).
- A competitor or country not checked for clearly longer than the baseline window (the window plus the larger of the overlap and a quarter of the window - for the default 30 days, 37.5 days) gets a fresh free baseline instead of a bill for everything that appeared meanwhile. A schedule only slightly slower than the window (for example weekly with
baselineDays: 7) keeps billing new ads normally.
Zero results vs. a failed check
no_ads- the check worked and the competitor has no ads for that domain/country. Check the domain, or use the advertiser ID.failed/partial- the read was blocked or interrupted. Nothing is billed for that competitor and its memory is not moved forward, so the next run picks up everything it missed. When a country is blocked (HTTP 429/403/5xx, timeouts or network errors), the competitor's other countries are not read in that run. If 3 competitors in a row cannot be read for reasons outside the request (HTTP 429/403/5xx, timeouts or network errors), the run stops reading and lists the rest asnot_checked_rate_limited. A competitor that could not be read goes after the others next run (it is still tried every run), so competitors that fail every time do not stop the run before the others are checked.truncated- the competitor's ad list is deeper than "Max ads read per competitor". The ads most recently shown are covered; countries the limit left unread are read first next run.skipped_recent- checked recently (seeminHoursBetweenChecks); not a gap.not_checked_*- the run reached its maximum charge or time limit, or stopped for another listed reason. These competitors go first next run (a competitor that was read but could not be paid for goes after them).
If every competitor that was due fails, the run ends as aborted (nothing charged, memory unchanged) so your schedule shows it. A failed Slack/webhook delivery does not stop the run: it ends as succeeded after everything was checked, written and charged; the new ads are in the dataset, the status message starts with "Slack notification FAILED (...): sent again with the next run" (or "Webhook ..."), RUN_SUMMARY has notificationFailed: true and the same words in warnings, and the digest is re-sent to the same URL with the next run. A digest with no new ads and no signals (a first check, a quiet day) has nothing worth re-sending: its status message says "no new ads or signals in it, nothing to send again" instead. Temporary failures (timeouts, network errors, HTTP 408, 429 and 5xx) are retried once and then re-sent with the next run, however many times they happen. When the destination refuses it 3 runs in a row with a permanent answer (HTTP 3xx/4xx other than 408/429, such as 404 or 410 or a redirect, or an address refused at connect time), that run and every next run it refuses ends as aborted, and the status message says to check the URL in the input (RUN_SUMMARY.checkDestination); the 1st and 2nd refusal end as succeeded with the failure shown first, and one successful delivery resets the count. Checks and new ads are still charged while a destination fails. If a run is stopped half-way (timeout, crash, migration), the competitors it already delivered are notified by its restart or by the next run, and are not read or charged again.
Scheduling and integrations
Create a task with your watchlist and a schedule (daily is typical). Each task keeps its own memory. Runs without a task (API, or a schedule that points at the Actor) share the memory main unless stateKey is set: give each different watchlist its own stateKey. For n8n, Make or Zapier, either point webhookUrl at your workflow, or use the Apify integration and read the dataset items with type = "new_ad". If a run finds its memory still in use by an earlier run, it waits up to 5 minutes and then ends as aborted without checking or charging.
Known limits
- Opening the dataset in a spreadsheet? Turn on Spreadsheet-safe text (
spreadsheetSafeText): text that starts with=,+,-or@(advertiser names are written by the advertiser) gets a leading apostrophe, so it is shown as text instead of being run as a formula. It is off by default because it changes those values for JSON consumers. - Proxy: Residential (the default). Google Ads Transparency Center answers datacenter proxy IPs with HTTP 429 on every request, so Apify Proxy without a group is switched to Residential (the run warns). Proxy costs are included in the event prices.
- Shortest recommended schedule: once an hour (daily is typical). A run started sooner than
minHoursBetweenChecks(default 1 hour) after the previous one skips every competitor (nothing read, nothing charged), but every start still takes a few seconds of compute; a schedule every minute mostly starts runs that do nothing. - A webhook body is kept under 1 MB and a Slack message under 50 blocks and about 40,000 characters; what does not fit is counted and is in the dataset.
- While a destination keeps failing, checks and new ads are still charged (the new ads are in the dataset) and up to 10 undelivered digests are kept for the next send. A failed delivery is shown first in the status message of a succeeded run; only a destination that refused 3 runs in a row with a permanent answer ends the run as aborted. A digest the destination refuses for its content (HTTP 400/413/422) stays among them and is sent again each run until it is pushed out.
- Setups that share one state key with different destinations: new ads of a run that was stopped before its digest are sent only by a run with that run's destination, or by a run that watches the same competitor under the same label and client. The rest are held for another destination:
RUN_SUMMARY.warnings,heldForOtherDestinationand the status message say "run X: N new ad(s) held for another destination" (they are in that run's dataset). N counts only the competitors the reporting run can see (those of its own watchlist, and entries kept in the run journal); new ads of competitors its watchlist does not have are not counted there. At most 3 undelivered runs are kept; one dropped with something left is said inwarnings,carryDroppedand the status message, again by the following runs until a run that said it has finished its digest (runs with nothing left go first). - Changing the Slack/webhook URL: what is still undelivered for the old URL - a run stopped before its digest, and the digests kept in the old URL's outbox after failed deliveries - is sent to the new URL for the competitors the watchlist still has under the same label and client. The rest (a competitor removed or relabelled) is not sent and is said in
warningsand the status message (they are in that run's dataset): "held for another destination" (heldForOtherDestination) for a stopped run, "run X: N new ad(s) not sent, held for a previous destination" (heldForPreviousDestination) for an old outbox; it is sent only if a run uses the old URL on this state key again. An old outbox is moved only by a run with the same competitors as the run that used it last (so another setup sharing the state key does not take it): change the URL in one edit and the competitors in another, or its digests stay held. Up to 5 earlier URLs per kind are looked at; outboxes left by v0.3.6 and older are not (their URL was not recorded). - Two setups on one state key that watch the same competitor under the same label and client (for example both without a client) are treated as one setup: new ads one of them paid for, in a run stopped before its digest or left in its outbox, can be sent to the other's destination, and the setup that paid is not told. Give each setup its own state key.
- A run stopped after a successful delivery but before it cleared its outbox and run journal has that digest sent again by the next run (a duplicate notification; nothing is charged twice). The copy carries the same run id: in the webhook body it is in
carriedOver[].runId(the original had it asrunId).
Need every ad, not just the new ones?
This Actor watches and alerts. For full exports, creative details and image archiving from the Transparency Center, the separate Google Ads Transparency Scraper Actor (same author, priced separately) reads complete ad lists.
Only Google Ads Transparency Center is covered (not Meta or other ad libraries).
日本語
競合の新着広告だけを毎日 Slack・Webhook に。Google 広告透明性センター(公開ページのみ・ログインなし)を確認します。Google とは無関係の独立したツールです。実行の開始 $0.01、競合の確認 1 件 $0.01、新着 1 件 $0.005。初回の 30 日分のベースライン要約は無料(初回も確認料はかかり、広告の非常に多い競合は大量在庫の読み込み料もかかります)。
language を ja にすると Slack のまとめが日本語になり、日付は日本時間で表示します。0 件(no_ads)と取得失敗(failed/partial)は区別して表示し、失敗した競合は課金せず既読も進めません(次回に取り直します)。1 回の実行の課金上限は maxChargeUsd(既定 $1)で、上限で確認できなかった競合は次回に先に確認します(読めなかった競合と、読んだのに上限で払えなかった競合は後ろに回します)。タスクごとに既読は別々に保存されます。タスクを使わずに実行する(API・Actor を直接起動するスケジュール)と、stateKey が空欄なら共有の既読 main を使います。既読は競合ごとなので、ウォッチリストを編集しても続きます(足した競合は無料のベースラインから、外した競合は確認しないだけ)。タスクの外で別のウォッチリストを回すときは、それぞれに別の stateKey を指定してください(同じキーに別の送信先の設定が来るとログと状態メッセージで警告します)。Slack・Webhook の URL が形式の時点で届かないもの(Slack のワークフロー URL・https でない・内部アドレス)は、読む前・課金の前に入力エラーで止めます。通知の送信に失敗しても実行は止めず succeeded で終わり、状態メッセージの先頭に失敗した通知と「次回に同じ URL へ再送」を示します(新着も出稿の動きも無い回は再送するものが無いので、その旨を示します。RUN_SUMMARY の notificationFailed・warnings にも残します)。一時的な失敗(タイムアウト・ネットワーク・HTTP 408/429/5xx)は何回続いても succeeded です。404 などの恒久的なエラーが 3 回続いた実行からは aborted で終わり、状態メッセージで URL の確認を促します(1・2 回目は succeeded。1 回でも届けば数え直し)。送信先の URL を変えると、古い URL 向けに残っていた未通知分(途中で止まった run の分と、送信失敗で送信待ち箱に残った分)は、同じラベル・同じクライアントで今も見ている競合の分だけ新しい URL に届きます。届かない分は状態メッセージと RUN_SUMMARY.warnings で件数を示します(新着はその run のデータセットにあります)。Webhook の本文は 1 MB 以内に収めます。スケジュールの最短の推奨間隔は 1 時間です。