- A conversion count is a claim assembled by six systems in a row, none of which validates meaning. Every layer returns success, so a broken pipeline reports a healthy-looking number.
- Most tracking failures inflate the count rather than suppress it — and nobody investigates a metric that's going up.
- The shape test is the fastest check available. Export fifty conversion timestamps and look at the distribution. Real demand is lumpy; anything automated is evenly spaced.
- Hosted form services bin honeypot spam server-side but still return HTTP success, so client-side code counts blocked spam as a lead unless it checks the honeypot itself.
- Hash your tag file in three places — repo, origin, and the bare URL a browser actually gets. A cache-busted request proves the origin is right, not that visitors are getting it.
- The thing you count is the thing you get more of: nothing automated should act on a number that hasn't passed the audit.
A conversion count is not a measurement. It's a claim, assembled by five or six systems in a row, each one handing a number to the next, and not one of them raises an error when it hands over the wrong one.
That's what makes conversion tracking different from the rest of your marketing stack. A broken page 404s. A broken deploy goes red. A broken conversion pipeline keeps producing a number that looks exactly like a working one — often a better-looking number, because most tracking failures inflate rather than suppress. Nobody investigates a metric that's up.
This is the audit we run on a Google Ads funnel before we'll act on anything it reports. It takes an afternoon, it needs no specialist tooling, and in our experience some version of it is overdue on most accounts under about ten thousand a month, because the tracking was set up once by whoever built the site and never checked again.
Why these failures stay invisible
Three properties conspire to keep tracking problems alive for months.
Every layer returns success. The form service accepts the submission. The tag fires. Analytics records the event. The ad platform imports it. Every handshake in that chain reports OK, because each system is doing its own job correctly — the failure lives in the meaning being passed between them, and no system is responsible for meaning.
The errors mostly inflate. Test traffic, bot submissions, double-fired tags and internal QA all push the count up. A number that's too high looks like success, gets screenshotted into a report, and becomes the thing everyone defends.
The consumer is a machine. This is the part that turns a reporting error into a spending problem. Smart Bidding isn't a dashboard you can choose to ignore. Its objective function is whatever you told it a conversion is, and it will spend real budget reshaping your traffic to produce more of that thing. Point it at a bad definition and it will pursue it faithfully and expensively.
So the audit isn't bookkeeping. It's the difference between an algorithm optimising toward customers and an algorithm optimising toward noise.
The seven stages, and what breaks at each
Work the stages in order, because a failure early on invalidates everything you'd conclude later. There's no point reconciling attribution windows if the tag is firing on spam.
Stage 1–2: the submission, and what the form service does with it
Start at the form, because the most under-known failure in the whole chain lives here.
Most hosted form services — Web3Forms, Formspree, Basin, and the form handlers built into site builders — include honeypot spam filtering. A hidden field that humans never see and naive bots fill in automatically. The service checks it, and silently discards flagged submissions.
It discards them and still returns HTTP success to the browser.
That behaviour is reasonable from the service's side: telling a bot it was detected just teaches the bot. But it means your client-side code — the code deciding whether to fire a conversion — cannot distinguish "we delivered your lead" from "we binned your spam." Both are a 200.
If your conversion fires on the form's success response, and the overwhelming majority do, every bot that fills your honeypot is being counted as a lead. The form service is protecting your inbox and your tracking is counting the attack.
Two checks:
- Submit your own form with the honeypot field filled in, using developer tools to unhide it. Then look at whether a conversion appeared. If it did, that's your number one source of phantom leads.
- Compare the count of conversions against the count of submissions your form service reports as delivered. Those two numbers being different is the whole finding.
The fix is to stop trusting the response and check the honeypot client-side before counting anything. It's a few lines, and it belongs in front of every conversion call you make.
Stage 3: the tag — is the browser getting the file you think it is?
The next failure is the one that fools careful people, because every artefact you inspect looks correct.
Tracking code usually lives in a static JavaScript file served through a CDN. The version in your repository can be perfect, your deploy can be green, and the copy the CDN is actually handing to browsers can be weeks old — missing the endpoint you added, or the consent logic, or the suppression flag.
Reading the file in your editor proves nothing. Reading the page source proves nothing. Compare what's actually being served:
# 1. what your repository has
shasum -a 256 assets/js/track.js
# 2. what the origin has, bypassing the cache
curl -s "https://example.com/assets/js/track.js?cb=$(date +%s)" | shasum -a 256
# 3. what a real browser actually receives
curl -s "https://example.com/assets/js/track.js" | shasum -a 256
Three hashes, and they should be identical. If the third differs from the first two, every conclusion you've drawn from tracking since your last deploy is provisional.
A cache-busted request is not proof, and this catches people constantly. A CDN happily serves fresh content on ?cb=12345 while still serving stale content on the bare URL, because those are two separate cache entries. We've watched a sitemap serve a twenty-minute-old version on the bare URL while the cache-busted version was current. Origin-correct and visitor-correct are different questions, and after any deploy you need to ask both.
While you're in the tag, check two more things:
Consent states. Load the page and accept the consent banner. Watch the network tab for your analytics and conversion requests. Now do it again in a clean session and decline. What you're looking for is either failure mode: events firing when consent was refused, which is a compliance problem, or events silently never firing in a region where they legitimately should. This has to be done in a real browser watching real requests. You cannot determine whether a tag fires by reading HTML, and confident people are wrong about this constantly.
Double-firing. If a conversion is implemented both in a tag manager container and hard-coded in the page — far more common than you'd expect, usually because two different people solved the same problem a year apart — every conversion counts twice. Watch the network tab on a single real submission and count the requests. One action, one request.
Stage 4: the count — whose traffic is in there?
Now audit the population being counted, which almost always contains traffic that isn't a customer.
Your own team. Staff testing forms, developers checking a fix, a contractor QAing a release. Filter your lead records by your own email domains; the answer should be zero and rarely is.
Automated checks. Anyone sensible runs a scheduled job that submits their contact form to prove it still works — forms break silently and constantly, and a synthetic submission every morning is good engineering. But that submission hits the same tracking code a real visitor does, so unless it's explicitly suppressed, your monitoring is manufacturing conversions. The fix is a parameter on the URL that the tracking code recognises and bails on, made sticky for the session because a form POST can rewrite the URL and lose the flag. Then — and this is the part people skip — have the check assert that suppression is actually active before it submits, and fail if it isn't. That way a stale CDN copy that doesn't honour the flag breaks the check loudly instead of quietly poisoning the data.
Bots and spam that got past the honeypot. Structurally invalid addresses, disposable domains, obvious placeholder addresses.
People selling to you. This one is routinely missed and it distorts cost-per-lead badly. Agencies pitching SEO services, outsourcing firms, list vendors — real humans, filling in your form, with no intention of buying anything. They are indistinguishable from leads in every dashboard. Judge on the message body where the form has one.
The principle underneath all four: mark non-customer traffic at write time, never clean it up later. A flag column, a reserved address domain, a suppression parameter. "We'll remember which ones were tests" is a promise no team keeps past the second week, and once the marker is lost it cannot be reconstructed.
The shape test: the fastest check in this article
Before you audit anything else, do this. It takes one minute and it catches the largest category of problem.
Export the timestamps of your last fifty conversions. Not the total — the timestamps. Then look at the distribution.
Real demand is lumpy. It clusters on weekday mornings, dies over holidays, spikes when a campaign lands and then goes quiet for nine days. It correlates with things you did. Machine-generated conversions are even, because a scheduled job runs on a schedule.
What you're looking for:
- Suspicious regularity. A conversion on most days, including weekends and holidays, is not human demand. Something automated is producing it.
- The same minute past the hour, repeatedly. That's a cron job.
- Several sharing a timestamp to the second. That's a script, or a bot filling every form on the page.
- A cluster of submissions inside one short window across several different forms. That's somebody testing the site — and if you find one, check whether those records are still sitting in your production lead table.
- No relationship to anything you did. If the trend doesn't move when you launch, pause, or change spend, it isn't measuring your marketing.
Counts hide all of this. A monthly total of sixty leads looks identical whether it arrived as two big campaign-driven clusters or as a metronome. Only the distribution tells you which one you have.
Stage 5–6: why the three numbers never agree
Once you start reconciling, you hit the second problem immediately: your inbox, your analytics, and your ad platform give three different numbers for the same week, and none of them is obviously the liar.
Some of that gap is normal and worth understanding before you go hunting for a bug.
They count different events. Your inbox counts messages that arrived. Analytics counts an event the browser fired. The ad platform counts conversions it can attribute to a click it sold you. A spam submission your form service binned is zero emails, one analytics event, and possibly one ad conversion — and nothing in that picture is broken.
They count at different times. Ad platforms credit a conversion to the click, not to the day it happened. A click on the 3rd that converts on the 17th is reported on the 3rd, appearing retroactively — a week that looked flat on Monday can grow by Friday. Analytics credits the day the event fired. The two are permanently out of step at the edges of any date range, and short windows are worst.
Attribution windows are configurable and rarely checked. A 30-day click window and a 7-day window disagree by design. If nobody has looked at that setting, nobody knows which one they're reading.
Some of the number is modelled. When consent is denied or a browser blocks the tag, platforms estimate the conversions they believe occurred. That estimate can be perfectly reasonable and it is still not a list of humans you can email.
The practical stance: pick one number as your source of truth, and make it the one closest to money. For most businesses that's a record you own — a row in your own database or CRM, with a timestamp and a click ID, that can be reconciled against an inbox. Analytics and the ad platform become instruments you cross-check against it, rather than authorities you defer to.
That's also why a first-party record is worth building even when the platforms already report a number. Two independent counts let you ask whether they agree. One count you cannot check is not a measurement, it's a belief.
Stage 7: what the number is allowed to control
The last stage of the audit isn't technical. List everything that consumes the conversion count and acts on it:
- Automated bidding strategies
- Budget rules and automated recommendations
- Alerting
- Reporting agents and dashboards
- Client-facing reports
Then apply one rule: nothing automated should act on a conversion number that hasn't passed stages 1 through 6. A wrong report is an argument. A wrong number wired into a bidding algorithm is a budget being spent, daily, to buy more of something that doesn't exist. If you can't complete the audit this week, the safe interim move is switching the affected campaigns to manual or maximise-clicks bidding until you can — a blunt strategy on good faith beats a sophisticated one aimed at a fiction.
Counting the right thing
Everything above assumes the conversions you're counting are the ones you want counted. That assumption deserves its own scrutiny, because a technically perfect pipeline pointed at the wrong action will still steer your budget somewhere useless.
Three failures show up repeatedly.
Counting micro-actions as conversions. Newsletter signups, PDF downloads, video plays, scroll depth, clicks on a phone number. Each is a reasonable thing to measure and a terrible thing to bid on. If a newsletter signup and a sales enquiry both count as "a conversion," the bidding algorithm will correctly conclude that signups are cheaper and more plentiful, and buy you a great many of them. Measure micro-actions; optimise on the one action that turns into revenue.
Counting every action at the same value. Most accounts leave conversion values blank, which tells the platform that a tyre-kicker and a qualified enquiry are worth exactly the same. If you can attach even a crude value — a low number for a general contact, a higher one for a quote request, higher still for a booked call — bidding gets substantially better at finding the second and third kind. Rough values beat no values by a wide margin.
Never feeding qualification back. This is the most valuable improvement available to most service businesses and almost nobody does it. Your ad platform knows which clicks produced form fills. It does not know which of those form fills were real prospects, because that judgement happens later, in a human's head or a CRM. Closing that loop — sending back which leads actually qualified — turns the optimisation target from "people who fill in forms" into "people who become customers." Those are different populations, and the gap between them is where most wasted spend lives.
The general principle: the thing you count is the thing you get more of. Before auditing whether the number is accurate, be sure it's counting an action you'd genuinely want a machine to pursue relentlessly on your behalf — because that is precisely what will happen.
The checklist
| # | Check | How | What failure looks like |
|---|---|---|---|
| 1 | Timestamp shape | Export the last 50 conversion timestamps | Even daily spacing, repeated minutes, several in one second |
| 2 | Internal traffic | Filter lead records by your own domains and placeholder addresses | Any result at all |
| 3 | Automated checks | Find every scheduled job that touches a form | One that isn't explicitly suppressed |
| 4 | Honeypot | Submit with the hidden field filled | A conversion fires |
| 5 | Script parity | Hash the tag file: repo, origin, bare URL | Three hashes that differ |
| 6 | Edge staleness | Compare ?cb= against the bare URL after deploying |
Different bodies for 10–20 minutes |
| 7 | Consent states | Real browser, network tab, accept and decline | Firing when declined, or never firing |
| 8 | Double-fire | Count requests from one real submission | More than one conversion request |
| 9 | Attribution | Count conversions carrying a click ID | Paid conversions with no click ID |
| 10 | Reconciliation | Compare inbox, analytics and platform for one week | A gap nobody can explain |
| 11 | Consumers | List everything acting on the number | Anything automated, acting on a number that failed 1–10 |
Run 1 and 2 first. Between them they settle most accounts in about ten minutes, and if they come back clean you can proceed through the rest with real confidence rather than hope.
Keeping it fixed
An audit is a snapshot, and tracking rots. Deploys go out, someone adds a tag manager container, a plugin updates, a consent tool changes its defaults. Three habits keep the finding from coming back:
Put the audit on a calendar. Quarterly, and after any site redesign, form change, or consent-tool change. Redesigns are the highest-risk event: tracking is frequently the thing nobody remembered to port.
Make the guards self-testing. Any suppression you add should be asserted by an automated check that fails when the suppression stops working. A protection nobody verifies is a protection that has already silently expired somewhere.
Keep two independent counts. One you own, one from the platform. You don't need them to match. You need to be able to see when the gap between them changes, because that change is the earliest signal you'll get that something in the chain has moved.
If you only remember four things
A conversion count is a claim assembled by six systems, none of which validate meaning. Everything returns success, so nothing tells you it's wrong.
Look at the shape, not the total. Fifty timestamps will tell you in one minute what a monthly total never will. Real demand is lumpy; machines are even.
Three hashes, one file. Repo, origin, and the bare URL a browser actually receives. If they disagree, your tracking isn't what you think it is.
Nothing automated should act on an unaudited number. That's the difference between a reporting error and a budget being spent against a fiction.
If your lead count has looked suspiciously steady, or your ad platform reports conversions your inbox can't account for, send it over. The first two checks take ten minutes and usually settle the question — and we'd much rather tell you your tracking is sound than have you find out it wasn't after a quarter of bidding on it.


