All case studies
[ CASE STUDY ]Performance-marketing agency · data + AI

I made an agency's ad spend chase real customers, not form-fills.

A performance-marketing agency ran Google Ads for ~30 real-estate investor clients, but half the "conversions" were junk leads, and the truth about which leads became customers lived in offline spreadsheets. I built the data platform that ties every dollar of spend to qualified leads and signed contracts (down to the keyword) across every client.

Anonymized: specifics under NDA, framing and lessons in the open.

~30
Clients unified
4
Sources joined
1.8M+
Rows processed
$5$0.25
Per query (−95%)
Cost / qualified lead
The metric they lacked
marketing-data-hub · multi-client
NDA: details redacted
Sources · 4
Google Ads
GA4
Website
Offline lead tracker
keyword · ad group · ad copy
Ingestion
parallel · fault-isolated
Data Lake
governed core
Raw
Curated
catalog + query engine
Outputs
REST API
Client dashboards
AI · natural-language querying
Conversion write-back → Google
true cost / qualified lead · per client
Spend joined to qualified outcomes, per keywordredacted
The problem

Google optimizes toward form-fill "conversions", but for this agency, roughly half of those were junk. The truth about which leads qualified (and which signed) lived offline, scattered across a spreadsheet per client, loosely keyed by UTM tags and click IDs. There was no system tying ad spend to qualified outcomes across ~30 accounts, and every client report was assembled by hand.

"Conversions" · this week
Form-fill #1041lead
Form-fill #1042junk
Form-fill #1043junk
Form-fill #1044lead
Form-fill #1045junk
Form-fill #1046lead
which became customers? 🤷
What I built

A serverless marketing-analytics platform. It ingests Google Ads (down to keyword, ad group, and ad copy), GA4, the website, and each client's offline lead-qualification tracker into one governed data lake. Two merge engines join ad spend to the offline qualified-lead and signed-contract data by keyword and click ID. A REST API and a React dashboard surface true cost-per-qualified-lead and cost-per-contract, wasted spend, and which headlines drive qualified leads, per client. Qualified conversions are fed back to Google to train Smart Bidding, and the whole warehouse is queryable in plain English.

The join
Spend
(by keyword)
Offline
qualification
Cost / qualified lead
The outcome

Real data from 30-plus clients and 1.8M+ rows, dashboards answering in ~4–5 seconds, and the whole stack reproducible as infrastructure-as-code. Query cost dropped ~95% ($5 → $0.25). The numbers finally matched each client's own Google Ads console. And, the point, the agency could optimize toward qualified leads and contracts instead of vanity form-fills, with self-serve dashboards replacing the manual spreadsheets.

What they optimize for
form-fillsqualified leads
$/lead
cost per qualified lead
−95%
query cost

[ specifics under NDA ]

[ CAPABILITIES ]On top of the lake

What runs on top of the lake.

Six capabilities running on top of the governed data lake: from ingestion to the offline-conversion loop to plain-English querying.

Multi-source ingestion

Google Ads, GA4, the website, and each client's offline tracker, pulled in parallel and fault-isolated, so one bad source can't stall the rest.

4 sources · parallel

Offline-conversion loop

Join ad spend to qualified leads and signed contracts (matched by keyword and click ID) to close the loop between a click and a customer.

by keyword & click ID

Governed data lake

Raw → curated layers with a data catalog and query engine, and partition projection so queries stay fast and cheap as the data grows.

raw → curated

Self-serve client dashboards

Per-client KPIs, wasted spend, and the ad copy that actually drives qualified leads, replacing reports that used to be built by hand.

per-client KPIs

Smart-Bidding feedback

Qualified conversions are pushed back to Google, so Smart Bidding learns to optimize toward real outcomes instead of junk form-fills.

qualified → Google

Ask in plain English

An AI assistant queries the whole warehouse in natural language, connected via MCP, so anyone can ask the data a question and get an answer.

AI · via MCP
[ UNDER THE HOOD ]How it holds up

Keeping the numbers honest.

The part most automation work skips: the bugs I hunted down and the engineering underneath the metrics. Anonymized, but real.

The "14× spend" bug

A dashboard once showed ~14× the real spend. A query was recursively reading every date folder in the lake. Fixed with partition projection, with no data reprocessing.

Cost-engineered queries

Partition pruning, pre-aggregation, columnar storage, and caching cut data scanned by ~95%, dropping cost from $5 to $0.25 per query.

Tables as code, not guesswork

Auto schema-crawling caused drift and broken joins. Defining the tables explicitly, as code, killed it for good.

Attribution from real joins

Hardcoded qualification percentages were replaced with real joins to the offline tracker. One config fix unlocked 100% keyword→qualified-lead attribution.

~30
clients
4
sources
1.8M+
rows
13
pipelines
−95%
query cost
~5.5-mo
build

[ anonymized: specifics under NDA ]

[ Let's build yours ]

Optimizing for the wrong number?

Tell me where your ad data lives and what you're optimizing for today. I'll map out the build that ties every dollar of spend to qualified leads and signed contracts, down to the keyword.