How to Detect Winning Ads Programmatically (Winner Scoring Explained)
Why ad longevity is the one performance signal advertisers can't fake, and how to turn it into automated winner detection: tiers, composite scores, dna_diff, and a scan-to-brief workflow with working code.

Sections
Every media buyer has a theory about which competitor ads are working. Almost nobody has evidence. The fix is to detect winning ads programmatically, with math instead of vibes, because one signal is nearly impossible to fake: longevity. Nobody pays to keep a losing ad live for 90 days.
TL;DR: Runtime is the one performance signal an advertiser cannot fake, because losing ads get killed within days while winners keep earning budget. To detect winning ads at scale, resolve a brand's Meta page ID with a free lookup, run an automated winners scan (10 credits, auto-refunded if empty), and read three outputs: the tier (
high_confidence_winner,winner,loser), the composite score, and thedna_diffshowing what the winner does differently from same-landing-page losers. Feed the top winner into an AI teardown and you walk away with a creative brief grounded in budget commitment, not opinion.
This guide covers the budget-commitment logic behind longevity, where manual sorting collapses, the tier system and composite scoring behind automated winner detection, and the honest limits of the approach. Working code included.
Nobody Pays to Run a Losing Ad for 90 Days
Here is the core insight behind every serious attempt to detect winning ads: an ad's runtime is a financial statement, not a creative opinion.
Think about what keeping an ad live actually costs. The advertiser pays for every impression. Their media buyer reviews performance weekly, sometimes daily. Finance sees the spend line. If a creative misses its break-even ROAS, three separate people have an incentive to kill it, and on direct-response accounts they usually do within two weeks.
So when you see a competitor's ad that has been running for 90 days, you are looking at roughly twelve consecutive weekly budget reviews where someone with full conversion data decided this ad earns its keep. You cannot see their ROAS. You can see their revealed preference, and revealed preference paid for in cash is the strongest evidence available from the outside.
Compare that to every other external signal. Likes and shares can be inflated by a meme format that never converts. A clever hook impresses other marketers without moving product. Runtime is different because it compounds cost. Faking 90 days of longevity on a loser means torching three months of real budget. No rational advertiser does that.
The flip side matters too. A seven-day-old ad tells you almost nothing. It might be a future winner or one of the dozens of creative tests that die in week one. Longevity only separates signal from noise once enough budget reviews have passed. That waiting period is also the signal's main weakness, more on that near the end.
The Manual Way to Detect Winning Ads (and Where It Breaks)
You can apply longevity logic by hand, and for a single competitor it works fine.
The manual workflow looks like this. Open Meta's Ad Library, search the brand, and note which ads carry the oldest start dates. Repeat in the Google Ads Transparency Center for their YouTube and search presence, then check the TikTok Commercial Content Library if they spend there. On each platform, sort by launch date and screenshot the long-runners. Our signal-reading workflow for Meta creative walks through the manual version in detail.
Do this for one brand once and it costs you an afternoon. Now run the realistic version: eight competitors, four platforms each, refreshed every two weeks because ad portfolios churn. That is 64 manual research sessions a month producing screenshots that no script or dashboard can consume.
Manual longevity sorting breaks down in four specific places:
- Variant blindness. A brand running one concept in eight near-identical executions looks like eight separate ads. You count duplicates instead of concepts, and the brand's true portfolio shape stays hidden.
- No cross-ad comparison. The platform libraries show you each ad in isolation. The question that matters, how this ad performs relative to its siblings on the same offer, requires data the manual view never assembles in one place.
- Recency bias. You notice whatever the library surfaces first, which is usually the newest creative, the exact opposite of what longevity analysis wants.
- Zero automation. Screenshots cannot trigger a Slack alert, populate a swipe database, or feed an agent. JSON can.
Concepts, Variants, and What "Winning" Means in Data Terms
Any system that claims to detect winning ads needs a unit of analysis, and the raw ad is the wrong one. A concept is one creative idea, the specific pairing of hook and offer. A variant is one execution of that concept with swapped copy, a different thumbnail, or another aspect ratio. Brands that scale run winning concepts in many variants, which is why raw ad counts mislead. Twenty ads might be three concepts.
"Winning" then becomes a comparative claim, not an absolute one. A winner is a concept that outperforms its siblings, the other concepts from the same advertiser competing for the same budget and often pointing at the same landing page. This within-advertiser comparison is what makes winner detection rigorous. Cross-advertiser comparisons drown in confounds like brand size and budget. Within one advertiser's portfolio, those variables are held constant, and runtime differences start to mean something.
In data terms, you want four things per concept: runtime in days, reach signals, an estimated spend trajectory, and the set of sibling concepts it beat. Meta's free Ad Library API deserves credit as the original transparency dataset, but it returns spend and impressions only for political and social-issue ads, and it covers Meta alone. Commercial ads come back without the performance fields this analysis needs. The same gap exists across the LinkedIn Marketing API, where ad performance data is restricted to your own accounts.
This is the gap the AdLibrary API fills as a paid power-user layer on top of those transparency sources. Every commercial ad comes back with runtime, bucketed impression ranges, an estimated spend figure, and a 0-1000 heat score, across Facebook, Instagram, TikTok, YouTube, Google, LinkedIn, and more from a single key. Meta's free API is fine for one platform's political ads. The moment you need commercial creatives with performance signals, you need a different tool.
The Automated Winners Scan, Step by Step
The winners scan is a single API call that does what a strategist would do with a free week: pull an advertiser's entire recent portfolio, dedupe it into concepts, score every concept on longevity and reach, and return the winners with evidence attached.
Step zero is free. Resolve the brand name to a Meta page ID before spending anything:
curl -G "https://adlibrary.com/api/winners/search-pages" \
-H "Authorization: Bearer adl_your_api_key" \
--data-urlencode "q=gymshark" \
--data-urlencode "country=US"
The response lists candidate pages with page_id, page_name, ad_count, likes, and verification status. Check two things: the page is actually the brand you mean, and ad_count is high enough to be worth scanning.
Then run the scan against the confirmed numeric page ID:
curl -X POST "https://adlibrary.com/api/winners/advertiser/123456789" \
-H "Authorization: Bearer adl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"country": "US", "top_enrich": 100, "max_pages": 20}'
Three parameters control depth. country sets the market (default US). max_pages caps how many pages of ads get fetched, up to 30 at 300 ads per page. top_enrich controls how many top candidates get deeper analysis, up to 300. The defaults of 100 and 20 are sensible for most brands.
The scan costs a flat 10 credits, the most expensive call in the API, and it carries a safety net: if the scan finds no ads or fails upstream, the credits are refunded automatically. One scan runs per user at a time. A second concurrent request returns a 429 with a Retry-After header, so serialize your scans when processing a competitor list.
What comes back is a summary block and a results array. The summary tells you portfolio shape at a glance: total_ads fetched, concept_count after variant dedup, tier_counts across the full portfolio, and a runtime_distribution. The results array contains the scored concepts sorted by composite score, each carrying the ad object plus the score block the next two sections decode. For full request and response shapes, the API documentation and implementation guide covers every field.
Reading the Tier System and the Composite Score
Every scored concept lands in a tier, and the tier is the headline you act on.
high_confidence_winner means multiple independent signals agree. The concept combines top-decile runtime for this advertiser with strong reach, and it typically beat several sibling concepts on the same landing page. These are the ads to study first, because the advertiser's own behavior marks them as proven.
winner means the longevity and reach evidence clears the bar but with less convergence. Maybe runtime is excellent while reach data is thin. Treat winners as strong leads worth a closer look rather than settled fact.
loser is the most underrated tier. These concepts were killed fast or starved of reach while siblings flourished. Losers tell you which angles the market already rejected, which saves you from spending your own A/B testing budget rediscovering a dead end. A swipe file of losers is nearly as valuable as one of winners.
Two more tiers appear in the summary counts without being emitted as results: middle for unremarkable concepts and emerging for ads too young to judge. That last one enforces an honest principle. A system built to detect winning ads should refuse to classify week-one creatives, because longevity math has nothing to say about them yet.
Each result also carries human-readable reasons, plain-language strings like "Runtime 89 days (top 10% of this advertiser)". Pipe those directly into a client deck or a creative brief without translation. The tier states the verdict, and the reasons show the work.
Tiers answer "is this a winner?" The composite score answers "how strongly?" It is a 0 to 1 value built from four weighted components:
| Component | Weight | What it measures |
|---|---|---|
| Runtime | 0.40 | Days live relative to the advertiser's own portfolio |
| Reach | 0.30 | EU reach figures reported for the ad |
| Reach concentration | 0.20 | Whether reach clusters on this concept vs spreading thin |
| Estimated spend | 0.10 | The spend estimate trajectory |
The weighting encodes this article's thesis. Longevity gets the largest share because it is the hardest signal to fake, and spend gets the smallest because it is an estimate rather than a platform-reported figure. Reach concentration is the subtle one. An advertiser whose impressions pile onto one concept is voting with delivery, a stronger commitment signal than reach spread evenly across a dozen tests.
Use the composite for ranking, not as an absolute grade. A 0.81 versus a 0.43 within one scan is a meaningful gap. A 0.81 from one advertiser versus a 0.78 from another is not, because scores are normalized within each advertiser's portfolio. The score also travels with variant_count and a variants array showing each execution's runtime and landing page, so you can see whether a high composite comes from one long-running execution or a coordinated variant rollout, which itself signals how much the brand trusts the concept.
dna_diff: What Winners Do Differently Than Same-LP Losers
Manual research cannot replicate this part at any reasonable cost, and it is where winner scoring stops being descriptive and starts being prescriptive.
When a scan finds a winner, it looks for losing concepts from the same advertiser that point at the same landing page. Same product, same offer, same audience intent. The only meaningful difference left is the creative itself. The dna_diff block captures that difference:
same_lp_loser_count— how many failed siblings targeted the identical pagewinner_runtime_daysversusloser_runtime_p50— the survival gap in daysdeltas— plain-language differences, like the winner using video where losers used static images, or a different hook structure
Read a concrete example the way a strategist would. A winner ran 89 days against twelve same-page losers with a median runtime of 21 days. The deltas show the winner opens with a customer testimonial while every loser opens with a product shot. That is no longer "this brand's video ad did well." That is "for this exact offer, social proof in the first frame outlasted product-first creative by a factor of four." One is an observation. The other is a testable creative hypothesis with a sample size attached, exactly the kind of input a reverse-engineering workflow turns into production briefs.
The honest caveat: dna_diff only appears when same-landing-page losers exist. A brand that never tests variants against one page gives the comparison nothing to chew on, and the field reports that gap rather than inventing a contrast.

From Scan to Creative Brief: The Full Workflow
Here is the complete loop in Python, from brand name to AI-generated brief:
import requests
BASE = "https://adlibrary.com"
HEADERS = {"Authorization": "Bearer adl_your_api_key"}
# Step 1: resolve the brand to a Meta page ID (free)
pages = requests.get(
f"{BASE}/api/winners/search-pages",
headers=HEADERS,
params={"q": "gymshark", "country": "US"},
).json()["pages"]
page = max(pages, key=lambda p: p["ad_count"])
# Step 2: winners scan (10 credits, refunded if empty)
scan = requests.post(
f"{BASE}/api/winners/advertiser/{page['page_id']}",
headers=HEADERS,
json={"country": "US", "top_enrich": 100, "max_pages": 20},
).json()
winners = [
r for r in scan["results"]
if r["score"]["tier"] in ("winner", "high_confidence_winner")
]
for w in winners:
print(w["score"]["composite"], w["score"]["reasons"])
# Step 3: turn the top winner into a creative brief (1 credit)
top = winners[0]
brief = requests.post(
f"{BASE}/api/enrichment",
headers=HEADERS,
json={"ad": top["ad"]},
).json()
print(brief["enrichment"]["analysis"])
The enrichment call in step 3 is what converts detection into production. It runs a full teardown of the winning creative and returns a strategic read plus a 1:1 replication brief tuned to the detected format, structured data your AI enrichment pipeline or design team can act on directly. The 60-minute research-to-brief workflow shows the human-paced version of this same loop.
From here, automation is plumbing. Schedule the script weekly over your competitor list and diff the winner sets between runs, the approach our competitor monitoring playbook covers for alerting. Wrap it in n8n if your team lives in no-code, or expose it to an agent through an MCP server so Claude can run the whole loop conversationally. Teams already running Claude Code workflows against the API treat the winners scan as the opening move of every competitor engagement, and the creative strategist workflow shows where it sits in a weekly cadence.
How to Detect Winning Ads Across a Whole Niche
Winners scans answer "what works for this brand?" The niche-level question, "what works in this market?", uses keyword search with longevity sorting instead:
curl -X POST "https://adlibrary.com/api/search" \
-H "Authorization: Bearer adl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"keyword": "protein powder",
"appType": "3",
"platform": ["facebook", "instagram"],
"adsType": ["2"],
"daysBack": 90,
"sortField": "-days"
}'
sortField: "-days" puts the longest-running creatives first, which applies the budget-commitment filter across every advertiser bidding on the niche at once. Restricting to video with adsType: ["2"] follows the spend, and daysBack: 90 keeps results current. Each search costs one credit per page and returns ads with runtime, engagement counts, bucketed impressions, and heat score attached.
The two modes complement each other. Niche search surfaces which brands have long-running creative worth a closer look. The winners scan then decomposes the interesting brands into scored concepts. Search wide, scan deep. Sorting by -heat_degree instead gives you a momentum view, ads trending right now, which catches fast risers the longevity sort will not flag for weeks. Cross-checking a brand's footprint on Google's Transparency Center confirms whether a Meta winner is being scaled to YouTube, a strong second commitment signal.
The Honest Limits of the Longevity Signal
Anyone claiming to detect winning ads with perfect accuracy is overselling. Longevity math has real failure modes, and knowing them is part of using it well.
Brand campaigns break the logic. The entire inference rests on direct-response discipline, where underperformers get killed fast. A brand-awareness campaign with a committed annual budget can run a mediocre creative for six months because nobody is watching a ROAS line. Check the CTA and landing page before trusting runtime. "Shop now" into a product page supports the inference. A vague brand film into a homepage does not.
Big advertisers tolerate losers longer. A company spending seven figures monthly might leave a marginal ad live simply because the review cycle is slow. Runtime thresholds that mean certainty for a lean DTC brand mean mere suggestion for an enterprise. This is why scoring normalizes within each advertiser's own portfolio rather than against a global bar.
Survivorship hides the retired winner. An ad that ran 150 days and stopped last month was a confirmed winner, and a live-only view misses it. Worse, a creative may persist past its prime, still running while fatigue erodes its returns. Our guide to diagnosing fatigue with competitor longevity signals covers reading the decay side of the curve.
The signal lags by design. Longevity needs weeks of budget reviews to accumulate, so pure runtime analysis will never catch a winner in its first ten days. The emerging tier exists precisely because classifying those ads would be guessing. Pair longevity with heat-score monitoring when speed matters.
Estimates are estimates. Impressions arrive as bucketed ranges, and spend is always modeled, never advertiser-reported. Use the ad spend estimator framing: good for relative comparisons, wrong to quote as fact. None of these caveats sink the method. They define its confidence interval, and the tier system encodes most of them already.
Credits, Costs, and Practical Guardrails
A realistic budget to detect winning ads programmatically, using the credit model:
- Brand resolution and page lookup: free. Both
advertisers/searchandwinners/search-pagescost nothing, so always probe before paying. - Keyword search: 1 credit per page. A weekly niche pull of three pages is 3 credits.
- Winners scan: 10 credits flat. The auto-refund covers empty results and errors, but a valid-yet-wrong page ID returns real results and charges you. The free lookup step exists to prevent exactly that, so never skip it.
- Enrichment: 1 credit for text and videos up to 180 seconds, scaling up for longer video. Enrich only concepts that cleared the winner tiers, not entire portfolios.
A weekly run over eight competitors lands around 80 credits for scans plus a handful for searches and briefs, comfortably inside the Business plan's 1,000+ monthly credits at €329/mo, which is also the tier that includes API access and integration help. Respect the 10 requests/minute rate limit and honor Retry-After on 429s. Cache results by ad_key so you never pay twice to learn the same thing. For estimating what competitors themselves spend, the spend-data estimation workflow chains nicely off scan output, and once you have validated winners of your own, the scaling playbook covers what to do next.
Frequently Asked Questions
Why is ad longevity a reliable way to detect winning ads?
Because runtime costs real money. An advertiser reviewing conversion data weekly will kill an unprofitable ad within the first one or two reviews, so a creative that survives 60 or 90 days has repeatedly justified its budget to people who can see the actual ROAS. Faking that signal would require deliberately funding a loser for months, which no rational advertiser does.
What do the winner tiers mean in a scan?
high_confidence_winner means several independent signals converge, such as top-decile runtime for that advertiser plus strong reach and beaten sibling concepts. winner means the evidence clears the bar with less convergence. loser marks concepts killed fast or starved of delivery, which reveals angles the market rejected. A middle and an emerging tier are counted in the summary but not emitted as results, because unremarkable and too-young ads support no verdict.
How much does it cost to detect winning ads programmatically?
Brand resolution and page lookups are free. A keyword search costs 1 credit per page, a full winners scan costs a flat 10 credits with an automatic refund if it returns nothing, and an AI teardown of one winner costs 1 credit for text or video up to 180 seconds. A weekly scan of eight competitors runs about 80 credits, well within the Business plan's 1,000+ monthly credits at €329/mo.
What is dna_diff and why does it matter?
dna_diff compares a winning concept against losing concepts from the same advertiser that point at the same landing page. With product, offer, and audience held constant, the remaining differences are creative choices, so the deltas read as tested hypotheses, such as the winner opening on a testimonial while losers opened on product shots. It turns winner detection from observation into a creative brief input.
Can I do this with Meta's free Ad Library API instead?
Partly, and for political ads, yes. Meta's free Ad Library API is built for transparency, so it returns spend and impressions only for political and social-issue ads on Meta platforms alone, behind an app review and tokens that expire every 60 days. For commercial ads with runtime, reach, estimated spend, and cross-platform coverage in one key, you need a paid layer like the AdLibrary API on top of it.
Evidence Beats Vibes
The whole argument compresses to one sentence: advertisers reveal their winners by paying for them, and runtime is the receipt. Learning to detect winning ads programmatically means reading those receipts at scale, with variant dedup so you count concepts instead of duplicates, and with tiers and dna_diff so every verdict arrives carrying its evidence.
The manual version works for one brand on a slow afternoon. The automated version covers your entire competitive set weekly and hands the output to whatever sits downstream, a Slack alert or a full monitoring pipeline. If your creative roadmap currently runs on screenshots and gut feel, run one scan against your loudest competitor and compare what the math says to what you believed. The gap is usually the most useful brief you will read all quarter.
API access ships with the Business plan at €329/mo with 1,000+ monthly credits and free integration help. Creating a key takes about two minutes, and a live response example sits on the API access page.
Related Articles

How to Find Winning Meta Ad Creative: A Signal-Reading Workflow
A 4-phase workflow to find winning Meta ad creative using run duration signals, AI enrichment, and pattern clustering. For media buyers and creative strategists.

Diagnosing ad fatigue with competitor longevity signals
Learn how to diagnose ad fatigue before your own metrics turn red — using competitor longevity signals from the ad library as a leading indicator.

How to Monitor Competitor Ads: The Ongoing Playbook for Diff-Detection and Alerts
Stop refreshing ad libraries manually. Build a competitor ad monitoring system with scheduled API pulls, diff-detection, and Slack alerts that tells you what changed since last week.

Full adlibrary API Documentation and Implementation Guide
Complete API documentation for AdLibrary. Extract Meta Ads, Google Ads, TikTok Ads and more via REST API. Code examples, endpoints, authentication, and rate limits.

Claude Code + adlibrary API: End-to-End Competitor Intelligence Workflows
Run five Claude Code workflows against the adlibrary API for automated competitor monitoring: Slack alerts, bulk teardowns, hook extraction across 500 ads, monthly landscape reports, and new entrant detection.

adlibrary MCP server: build your own in 60 lines of Python
Build a custom adlibrary MCP server with fastmcp in 60 lines of Python. Expose ad search, timeline, and enrichment as Claude tools—pair with Meta Ads MCP.

How to reverse-engineer winning ads: the creative strategist playbook
How to reverse-engineer winning ads as a creative strategist: hook decomposition, format detection, claim mapping, and fatigue signals from real ad libraries.