The business problem
Marketing spend follows a simple rule: an ad that keeps running is an ad that keeps paying. Native ad networks are the closest thing to a public ledger of what works — every promoted headline on a publisher site is someone spending real money — but that ledger is unreadable by hand.
Research was a person with twenty browser tabs. Finding what advertisers in a niche were running meant browsing publisher sites, screenshotting promoted widgets, and pasting links into a spreadsheet. Hours of work produced a snapshot that was stale by the afternoon and answered none of the questions that matter: how long has this ad run, on how many sites, and what does the page behind it actually sell?
The signal is longevity, and longevity needs history. A single sighting of an ad is trivia. The same creative observed daily across 48 publishers for 97 days is evidence that it converts. No manual process produces that history.
The same ad appears everywhere, slightly different. Networks recut images, append tracking parameters and rotate headlines. Without visual deduplication a collection fills with hundreds of copies of one ad, and the interesting question — how many distinct winners exist in this niche — becomes unanswerable.
Commercial spy tools stop at the report. They show ads; they do not connect to anything. What the business needed was intelligence one click away from action inside its own sending platform — see a proven ad, generate assets, and launch through the same suppression-gated pipeline every other campaign uses.
Previous workflow
Before: competitive research by hand
Nothing here accumulates. Every research session starts from zero and its output rots in a folder.
- Human action
Operator browses publisher sites
Manually visiting sites known to carry native ad widgets in the target niche.
- Problem: Coverage limited to the sites one person thinks to check
- Problem: No record of what was already seen last week
- Human action
Screenshots and links into a spreadsheet
- Problem: No dedup — the same ad recut three ways counts three times
- Problem: No landing-page capture; links die when campaigns end
- Human action
Judgment call from a snapshot
Decision point: Does this ad look like a winner, based on having seen it once?
- Problem: Longevity — the actual signal — is invisible in a snapshot
- Problem: No advertiser view: one operator running ten creatives reads as ten advertisers
- Human action
Template built from the hunch
A creative brief written from memory of what the ad looked like.
- Problem: The landing page that made the ad work is gone by build time
- Problem: Nothing links the campaign back to the evidence it came from
- Human action
Goals and non-goals
Goals
- Capture native ads and their landing pages continuously, with per-day observation history, so longevity becomes a queryable number.
- Deduplicate visually at ingestion — perceptual hashing on creatives — so the corpus counts distinct ads, not recuts.
- Resolve advertiser identity across networks from sponsor names, click-chain hosts and creative clusters.
- Make the corpus searchable the way an analyst thinks: keyword plus facets for network, geo, device, tracker signature, landing platform and days alive.
- Connect intelligence to action: from any ad, generate assets or start a campaign draft in the sending platform — behind human review.
- Stay inside AdFetch's shared proxy-bandwidth ceiling — a property of the fetch service this pipeline depends on, not something built here.
Non-goals
- No interaction with ads beyond capture. The pipeline records what is publicly served; it does not click, vote or engage.
- No autonomous sending. Nothing this system produces reaches a recipient without passing the platform's suppression gate and a person's decision to launch.
- Not a resale product. This is an internal instrument for one business's creative decisions, which is why it can stay opinionated.
My role
I own AdSpy on my own: product direction, requirements, architecture decisions, prioritization, delivery coordination, testing and production operations — with implementation built under the documented AI-assisted workflow. AdSpy calls a separately owned service, AdFetch, for every page it touches — that service's own story, and its own ownership, live on its own case study.
The three decisions I would point to as mine:
- Delegate fetching entirely. AdSpy never touches a browser, a proxy or an anti-bot challenge directly. Every fetch goes through AdFetch's HTTP API, which means AdSpy's own codebase is exactly as large as the ad-intelligence problem, and not one line larger.
- Dedup before storage, not after. Perceptual hashes are computed at ingestion, so the corpus never accumulates the recut-duplicates problem that would otherwise need a cleanup project later.
- Gate the exciting part. The one-click ad-to-campaign build is the demo everyone wants — and it is the part held behind a risk gate, human review and an empty production ledger until the surrounding controls earn trust.
What was built
AdSpy is one service with one job: turn captured ads into decisions about what to promote. It does not fetch pages itself — every fetch is delegated to AdFetch, a separately owned service consumed over HTTP.
AdSpy, and how the pieces connect
Collection is delegated entirely. Intelligence in the middle, action at the top — the proxy budget and the human review gate are load-bearing, not decoration.
Collection
Fetching is delegated entirely — this pipeline never touches a browser or a proxy directly.
AdFetch
Automated
Separately run fetch, scrape, search and map service, consumed over HTTP. Also used by the lead-discovery pipeline. See the separate case study.
Intelligence — AdSpy
The product layer inside the platform: pipeline, corpus and analysis.
Publisher crawl pipeline
Automated
Scheduled crawl jobs against publisher sites across six native ad networks; 1.29 million jobs recorded to date.
Ad ingestor
Automated
Normalizes captured ads, computes dedup signals and writes provenance for every record.
Landing capture
Automated
Follows ad click-throughs, snapshots the destination and classifies it — advertorial, sales page, lead form, quiz, article.
Visual dedup and signatures
Automated
Perceptual hashing collapses recut creatives; a tracker-signature registry identifies the analytics and affiliate stacks on each page.
Advertiser resolution
Automated
Fuzzy-matches operator identity across networks from sponsor names, click-chain hosts and creative clusters.
Corpus store
Data store
Ads, landing pages, daily observations, publishers and advertisers — every row traceable to the crawl job that produced it.
Search and facets
Automated
Weighted full-text search over headlines, sponsors and landing-page text, faceted by network, geo, device, tracker and platform.
Action — product surface
Where intelligence meets the sending platform.
Research UI
Human
Grid, ad detail with observation history, landings, advertisers, boards, favorites, analyst notes and saved watches.
Analytics dashboards
Monitoring
Creative reuse, cross-publisher spread, top hosts and trending views over the corpus.
Auto-build (gated)
AI / LLM
Four sequential LLM stages turn a proven ad into a landing page and email template — with saga-style rollback, idempotent retries and a risk gate before anything is committed.
Human launch decision
Approval gate
Campaign drafts route into the sending platform, where the mandatory suppression gate and a person's approval stand between intelligence and an actual send.
Diagram is sanitized for publication: it shows responsibilities and data flow, not hostnames, addresses or credentials.
The corpus numbers on the day of writing: 66,870 distinct ads, 114,098 captured landing pages, 120,076 publisher placements and a daily observation history that answers "how long has this run" for any of them.
How it connects to the rest of the stack
This system earns its place by being wired into everything else, not by being a standalone report.
- Into campaign creation. From any ad detail page an operator can generate assets or start a campaign draft in MailerClub. The auto-build path produces a landing page and an email template using the platform's own link-routing macros, so anything it drafts inherits click tracking and unsubscribe handling instead of reinventing them. Nothing sends without the platform's suppression gate.
- A fetch service AdSpy doesn't own. AdFetch is a separately run service — its own repository, its own API, its own operator. AdSpy owns none of the browser fleet, the anti-bot handling or the proxy pool behind its fetches; it just calls the API. The same service independently backs the lead-discovery pipeline's page fetches, with no shared code between the two consumers.
- One proxy budget across the business. Residential proxy transfer has a hard monthly ceiling, split across consumers with per-day byte budgets and hard stops, plus a request audit log — a mechanism AdFetch owns, not AdSpy. Ad intelligence, ad fetching and everything else draw from the same governed pool, so one enthusiastic crawler cannot starve the rest of the month.
- The same model discipline as everywhere else. The auto-build's LLM calls go through an explicit provider allow-list — the same select-the-model-per-task approach as the delivery workflow — and its spend is bounded by the pipeline's own timeout and stage structure.
- Into the agency's creative research. The boards, notes and analytics views are how proven angles and formats inform the creative production work — as research input, with a person deciding what any client actually receives.
Where AI is used, and where it is not
AI does
- The four auto-build insight stages: read a proven ad and its landing page, extract the angle, draft the funnel copy and the email template.
- Ad and landing-page analysis batches, when enabled — classification and angle extraction over the corpus.
Fixed rules do
- Everything that counts: perceptual-hash dedup, tracker signatures, advertiser matching, search ranking, observation history.
- The controls around the build: destination-URL validation, a risk gate between insight and build, saga-style rollback on failure, idempotent retries, one build per ad at a time.
- The proxy byte budgets and their hard stops.
The pattern is the same one the rest of this portfolio uses: AI interprets, fixed rules enforce, and a person owns anything that could reach a customer. An LLM never chooses what gets sent — it drafts, inside a pipeline whose guardrails are ordinary software.
Evidence
The corpus at a glance
Per-network counts, the filter bar and the facet row — including tracker-signature facets like the analytics and affiliate stacks detected on landing pages. Account identifier covered.
Placeholder — evidence pending sanitization review before publication. One ad, with its history and its actions
97 days across 48 publishers and 8,441 observations is the longevity signal the manual process could never produce — and the Launch campaign and Generate assets buttons are the bridge into the sending platform. Account identifier covered.
Placeholder — evidence pending sanitization review before publication. Captured landing pages, classified
Pages are snapshotted at capture time — campaigns die and links rot, but the evidence keeps. The classification chips (advertorial, sales, lead form, quiz, article) are what make funnel-type queries possible. Account identifier covered.
Placeholder — evidence pending sanitization review before publication. Advertisers, not just ads
The entity layer: one operator running thousands of creatives reads as one advertiser with a history, not thousands of unrelated rows. Account identifier covered.
Placeholder — evidence pending sanitization review before publication. Analytics — creative reuse
An ad recycled across 246 landing pages is a signal no snapshot could surface. Account identifier covered.
Placeholder — evidence pending sanitization review before publication. Watches — stated exactly as they are
The page itself says the quiet part: criteria can be saved and pre-staged today, and the evaluator wiring is still rolling out. Honest status labels are a product feature on this system too. Account identifier covered.
Placeholder — evidence pending sanitization review before publication.
What runs, and what does not
The same honesty rule as everywhere else in this portfolio: the state below is what this pipeline actually does, verified against its own records in July 2026.
| Component | State | The evidence |
|---|---|---|
| Publisher crawl and ingestion | Running | 21,731 ads first seen in the 30 days to July 30 |
| Landing capture and classification | Running | 31,327 captures in the same window |
| Search, facets, boards, notes | Running | Daily research use over the 66,870-ad corpus |
| Advertiser entity resolution | Running | Operator entities resolved across networks with first/last-seen history |
| Analytics dashboards | Running | Creative-reuse and cross-publisher views over live data |
| Watches | Partial | Criteria saved and pre-staged; evaluator wiring still rolling out — the page says so on its face |
| Facebook Ad Library connector | Stalled | 27 ads captured before the source began blocking unauthenticated capture; parked rather than fought |
| LLM ad analysis | Dormant | 40 analyses recorded, none since May; the batch layer is switched off while collection compounds |
| One-click auto-build | Built, gated, unused | Full pipeline with rollback, idempotency and compliance tests; zero production funnels committed |
Collection is scheduled work rather than a 24/7 stream — the header in the first screenshot honestly reads "0 new / 24h" on a day between crawl runs.
Results
- The corpus exists and compounds. 66,870 distinct ads, 114,098 landing pages and 1.29 million crawl jobs of provenance, growing at 21,000+ ads a month — a queryable asset where there used to be screenshot folders.
- Longevity is now a number. "97 days, 48 publishers, 8,441 observations" replaces "I think I've seen this one before" as the basis for creative decisions.
- Research became a query. Niche, network, geo, device, tracker stack, landing platform and days-alive are filters, not afternoons.
- Intelligence sits one click from action — with the click still owned by a person, and everything downstream passing the same suppression gate as any other campaign.
What this has not yet produced, stated plainly: a production campaign built end-to-end by the auto-build path. That ledger is empty on purpose, and opening it is a deliberate decision that has not been taken.
Lessons
- Depend on infrastructure you don't have to build. Every page AdSpy captures goes through a fetch service someone else owns and operates. The discipline that took was resisting the urge to reach past the HTTP boundary "just this once" when a fetch behaved unexpectedly — the fix always belonged on the other side of that boundary, not inside AdSpy.
- Deduplicate at the door. Perceptual hashing at ingestion cost little; retrofitting it onto a corpus of recuts would have been a project. The same lesson as suppression on the sending side: enforce at the choke point.
- Collection before analysis. The corpus compounds every week the crawler runs; analysis layers can stall — and one did — without devaluing the asset. Sequencing the durable part first made the stall survivable, and saying so beats pretending the analysis layer is live.
- An intelligence tool is judged by its distance to action. The features that changed behavior were not the dashboards — they were the Launch campaign and Generate assets buttons that shortened the path from evidence to draft.
What's next
- Wire the watch evaluator so saved criteria fire without a person opening the grid — the page already promises exactly this, and nothing more.
- Revive the analysis batch layer against the now much larger corpus, with the same spend ceilings the auto-build path uses.
- Take the first auto-build funnel to production behind the existing risk gate and review step — the deliberate opening of a deliberately empty ledger.