Winning Ad Creatives - Top Performing Competitor Google Ads avatar

Winning Ad Creatives - Top Performing Competitor Google Ads

Pricing

from $5.00 / 1,000 results

Go to Apify Store
Winning Ad Creatives - Top Performing Competitor Google Ads

Winning Ad Creatives - Top Performing Competitor Google Ads

Find the best performing competitor ads on Google. Ranks winning ad creatives by how long each has actually been served - the honest proxy for what works, since Google publishes no spend or impressions. Returns a scored top-N list with every input to the score, so you can audit it.

Pricing

from $5.00 / 1,000 results

Rating

0.0

(0)

Developer

mochi

mochi

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Winning Ad Creatives — Top Performing Competitor Google Ads

Find the best performing competitor ads on Google — ranked, not dumped.

Every other Google ad scraper hands you a pile of unsorted creatives and leaves you to guess which ones matter. This actor fetches a wide pool of a competitor's ads, measures how long each creative has actually been served, and returns a short ranked list of the ads that are working — with the score and every input to it, so you can check the ranking instead of trusting it.


Why longevity identifies winning ad creatives

Google's Ads Transparency Center publishes no impressions, no spend, no clicks. Nobody can scrape performance data from it, because it isn't there.

What it does publish is how long each creative has run. And advertisers behave in a very consistent way: they kill losing ads quickly and keep winners running. Nobody pays to serve the same creative for 400 days unless it earns its slot. So run-length is the best publicly available read on which of a competitor's ads are working.

That is a proxy inferred from advertiser behaviour, not a measurement of performance — see What this is not before you rely on it.


What you get

A ranked table, best-performing first:

ranklongevityScoreadvertiserdaysShowndaysRunningdutyCycleisActive
199.7Booking.com17401739.71.0true
292.4Hubspot, Inc.589587.71.0true
361.1Example Co73365.00.2true

Plus the creative itself (previewUrl, imageUrl), a deep link back to Google's own page (adLibraryUrl), and the advertiser's identifiers.

Use cases

  • Competitor ad performance analysis — see which of a rival's creatives survive, and for how long.
  • Creative research before a launch — copy the angles that have been running for years, not the ones that were tested for a week and dropped.
  • Top Google Ads competitor tracking — run several domains at once with topPerAdvertiser to get each competitor's best ads side by side.
  • Messaging teardowns — the longest-running ad in a niche is usually its strongest offer.

How the score works

Three signals, all measured, all emitted on the row so you can recompute the score yourself.

SignalFieldWhat it means
Days showndaysShownDistinct calendar days the creative was actually served.
Calendar spandaysRunningDays from firstShown to lastShown.
ConsistencydutyCycledaysShown / daysRunning — the share of its lifetime the ad was actually live.

dutyCycle is the one that does the real work. Two ads can both be "2 years old", but one ran every single day and the other ran for six weeks and sat dormant. We measured live examples as low as 0.11 — 499 elapsed days, only 57 of them served. The first ad is a proven workhorse; the second was tested and shelved. Longevity alone cannot tell them apart; dutyCycle can.

The formula

sustainScore = ln(1 + daysShown) / ln(1 + 1825) capped at 1.0
consistencyScore = dutyCycle (falls back to sustainScore if unmeasurable)
recencyScore = 1 if still running, else max(0, 1 - daysSinceLastShown / 90)
longevityScore = 100 x (0.55 x sustainScore + 0.30 x consistencyScore + 0.15 x recencyScore)

Why these numbers:

  • Log scale on days shown — the gap between a 3-day ad and a 90-day ad matters enormously; the gap between 1,200 and 1,300 days barely matters. A log curve keeps the score discriminating where the decisions actually are.
  • 1825 days (5 years) is just beyond the oldest ad the Transparency Center can report, so the longest-lived ads approach 1.0 without a crowd of them pinning to it. (An earlier 365-day reference tied 50 of 359 test creatives at exactly 100 — the whole top of the list was arbitrary. Fixed.)
  • 0.55 / 0.30 / 0.15 — staying power is the primary signal, consistency separates workhorses from shelved ads, and recency is a modest penalty so a strong-but-dead ad cannot top the list.
  • 90-day decay — an ad stopped three months ago tells you about last quarter, not this one.

Don't like the weights? Every input is in the output. Sort by daysShown, dutyCycle or daysRunning with sortBy, or re-rank the dataset yourself.


What this is not

Read this bit. It is the difference between using the actor well and being disappointed by it.

  • This is not measured ad performance. No impressions, no spend, no clicks, no CTR, no ROAS. Google does not publish them, so nobody can scrape them. longevityScore is an inference from how long an advertiser chose to keep serving a creative.
  • Long-running does not always mean high-performing. Always-on brand campaigns, evergreen remarketing, and plain neglect all produce long-lived ads. The signal is reliable in aggregate and across a competitor's whole account; treat any single row as a hypothesis, not a fact.
  • Durations are a lower bound for older ads. The Transparency Center reports nothing before 2021-10-25. Ads that started earlier are reported at that floor, so their true run is longer than shown. Those rows are flagged isDurationCensored: true.
  • Most of what the source returns is currently running. Across 359 creatives captured by domain search, every single one had been shown within a day or two — so for well-known brands the ranking mostly compares how long each still-live ad has survived, which is the useful comparison anyway. Stopped ads do appear for some advertisers (we measured creatives last shown 44 and 206 days ago), and those are pushed down by the recency term. Don't expect a full graveyard of long-dead ads, though: this is not a historical archive.
  • A single dominant advertiser can fill the top page with a cohort of evergreen ads that all tie at the ceiling. Use topPerAdvertiser to force a cross-competitor view.
  • Keyword mode is an advertiser autocomplete, not a keyword-bid lookup. Google's suggestion endpoint matches advertiser names and domains, so "asana" and "hubspot" resolve well while "project management software" returns nothing — no advertiser is called that. To analyse a category, use domain mode with the competitor domains you care about. The run logs a warning explaining this whenever a keyword resolves to nothing.

Input

Every field has a default — running with no input at all works.

FieldDefaultWhat it does
searchModedomaindomain (brand/domain), keyword (auto-resolve advertisers), advertiser (AR… IDs).
queries["nike.com"]The competitors to analyse; must match searchMode.
regionanywhereCountry name or code (US, United Kingdom, DE). Unknown values fall back to global.
maxResults20How many winning ads to return. This is what you are billed for.
maxAdsToAnalyze200Candidate pool fetched and scored before picking winners. Not billed.
sortBylongevitylongevity, daysShown, dutyCycle, daysRunning, recency.
minDaysRunning0Drop ads with a shorter span. Use ~30 to skip recent tests.
minDaysShown0Drop ads served on fewer distinct days.
minDutyCycle0Drop inconsistent ads. 0.8 = near-continuous evergreen only.
topPerAdvertiser0Cap each advertiser's rows so one competitor cannot fill the list.
adFormats[]Keep only text / image / video.
dateFrom / dateTo""Only ads overlapping this window (YYYY-MM-DD).
activeWithinDays7How recently an ad must have run to count as still running.
includeUnrankablefalseAppend ads that couldn't be scored, with rank/score null.
maxAdvertisersPerKeyword3Keyword mode: how many resolved advertisers to expand.

You pay for winners, not for the search. Fetching the candidate pool is free; only returned rows are billed. The default run scores 200 ads to give you the best 20.

Example — each competitor's 5 best ads

{
"searchMode": "domain",
"queries": ["hubspot.com", "salesforce.com", "zoho.com"],
"region": "US",
"maxAdsToAnalyze": 600,
"maxResults": 15,
"topPerAdvertiser": 5,
"minDaysRunning": 60
}

Example — proven evergreen creatives only

{
"queries": ["booking.com"],
"minDaysShown": 180,
"minDutyCycle": 0.8,
"sortBy": "dutyCycle",
"maxResults": 25
}

Output fields

FieldDescription
rank1-based position in the ranking (null if unrankable).
longevityScore0–100 composite. A proxy — see What this is not.
daysShownDistinct calendar days the creative actually ran.
daysRunningCalendar span from firstShown to lastShown.
dutyCycledaysShown / daysRunning, 0–1. null for ads under 3 days.
isActiveServed within activeWithinDays.
daysSinceLastShownDays since the creative last ran.
isDurationCensoredtrue = started on/before the 2021-10-25 data floor; duration is a lower bound.
sustainScore, consistencyScore, recencyScoreThe three sub-scores, 0–1.
daysShownEstimatedtrue if daysShown was absent and the span was used instead.
rankable, excludedReasonWhether the ad could be scored, and why not.
advertiserName, advertiserDomain, advertiserIdThe advertiser (company-level).
creativeId, adFormatThe creative and its type (text/image/video).
firstShown, lastShownISO 8601 UTC. firstShownTs/lastShownTs are unix seconds.
previewUrl, imageUrlThe rendered creative; direct image URL for image ads.
adLibraryUrlDeep link to Google's own page for the ad — verify any row by hand.
analysisPoolSize, analysisCoveragePctHow many ads this row was ranked against, and what share could be scored.
matchedQuery, searchMode, region, source, scrapedAtRun context.

Ads with missing or contradictory dates are excluded from the ranking rather than scored wrongly; they carry rankable: false and an excludedReason, and are only emitted if you ask for them. The RUN_SUMMARY key-value record reports the coverage rate and a breakdown of every exclusion, so nothing disappears silently. Across 359 real creatives from five separate captures, timestamp coverage measured 100% — but the handling is there because a sample is not a guarantee.


Use with AI agents (MCP)

This actor is available to any MCP-compatible AI agent (Claude, ChatGPT, Cursor, LangChain) through the hosted Apify MCP server — no wrapper or extra setup needed. Connect once, then ask the agent to run mochiboo/winning-ad-creatives-scraper.

Want only this actor as a tool, with no store search in the way? Point the agent at https://mcp.apify.com/?actors=mochiboo/winning-ad-creatives-scraper and it loads as a single named tool — the agent calls it directly instead of searching for it and possibly picking someone else.

It is built to be agent-friendly:

  • Small, cheap default run (20 ranked creatives) so an agent can probe before committing budget.
  • The ranking is the point for an agent — instead of handing back hundreds of raw ads for the model to reason over, it returns the few creatives that survived longest, already scored. Far less context burned per useful answer.
  • Short, literal input field descriptions — an agent can fill the schema with no extra documentation.
  • Flat output — one level deep, no decode logic required.
  • RUN_SUMMARY in the key-value store reports coverage and every exclusion, so an agent can confirm completeness without reading each row.

These pair with this actor and share the same flat schema style:

Support & feedback

Something wrong, or a field you need that is not here? Open an issue on the actor's Issues tab — it is read and answered. If the actor did what you needed, a rating on the store page genuinely helps other buyers judge it.


Notes

  • Data source: Google's public Ads Transparency Center. No login, no personal data.
  • Company-level only. Advertisers are organisations; the output has no personal-data field to strip, which is verified by an automated test on real output rather than assumed (PDPA/GDPR-clean).
  • Reproducible. The same pool ranks the same way every time; ties are broken by days shown.