Tags: analytics concept

Double Counting

Date: 2026-08-16


The same thing recorded twice. It inflates revenue, breaks reconciliation, and is one of the few tracking bugs that makes your numbers look better — which is why it survives longer than the ones that don’t.


What it is

Double counting is one real occurrence producing two or more recorded events. Distinct from over-attribution, where several parties claim the same conversion, which is Walled Garden Reporting.

The four causes

1. Page refresh and back-navigation. A purchase firing on load of the confirmation page fires again when the customer refreshes, screenshots, or navigates back.

purchase fires on page load of /order/confirmed
  → customer refreshes to check the order number
  → second purchase event, same order, same value

The classic, and the most expensive.

2. Duplicate tag deployment. The same tag installed hard-coded and in the tag manager. Common after a migration, when nobody removed the original — every event doubles, silently, from the day of deployment.

3. Retries without idempotency. A request that timed out but succeeded, retried, produces two records. Any retry policy without an event ID creates this — Idempotency and Deduplication.

4. Multiple triggers matching. In a tag manager, two triggers both matching one interaction — a click trigger and a data layer trigger for the same button.

Detecting it

  • Reconcile against the order system. Analytics revenue above the order system is the signature, and it’s unambiguous — nothing else does that — Guide - Auditing a Tracking Plan
  • Count distinct transaction IDs versus total purchase events. If they differ, you have duplicates and the ratio tells you the scale
  • Look for a step change. Doubling from a specific date is cause 2
  • Check the network panel on a real journey — two identical requests is the direct evidence — Instrumentation Debugging
  • Watch the distribution of events per session. A spike at exactly 2 for an event that should occur once is diagnostic

Preventing it

Idempotency keys are the general answer. Every transactional event carries a stable ID — transaction_id for a purchase — and the destination deduplicates on it. That handles refreshes, retries and duplicate triggers in one mechanism, without needing to enumerate the causes.

Beyond that:

  • Fire from the server for anything transactional. The order webhook fires once because the order was created once — Client-Side vs Server-Side Tracking
  • Never fire a purchase on page load. Fire on the server’s confirmation, or gate on a flag that survives a refresh
  • Audit for duplicate tags after any migration or replatform — Site Migrations and SEO
  • One trigger per event, checked in the container

Why it survives

Two reasons, and both are worth naming.

It makes numbers look better. Revenue up, conversion up, return on ad spend up. Nobody investigates a good month with the urgency they’d apply to a bad one.

It’s silent. No error, no alert, no gap in a chart. The only signal is a reconciliation nobody runs.

Which is why reconciliation against the order system is the single check worth automating — it’s the only routine that catches this class of bug without someone deciding to look. It’s the first row in the symptom list for exactly that reason.

The mirror image: under-counting from over-deduplication. A deduplication window that’s too aggressive can merge two genuine orders from the same customer minutes apart — a legitimate scenario in retail. Deduplicate on the transaction ID, not on user plus timestamp proximity.