All posts

QR code tracking: what you can measure without following anyone

9 min readAnalyticsPrivacy

scans per dayno IPstored, everCOUNTED, NOT FOLLOWED

“Can I see who scanned it?” is usually the first analytics question and the wrong one. You can't, shouldn't want to, and don't need to: the questions a business actually acts on — did the poster work, which city, which hour, how many people — are answerable without identifying a single human. This post is what QR tracking really measures, and where the line is.

Where the data comes from at all

A static code is unobservable — the phone reads the grid and goes, touching no server of yours. Tracking exists only because a dynamic code routes each scan through a redirect, and the redirect can write one row per scan on its way to answering. That row is the entire dataset; everything else is arithmetic on it.

the printnever changesgo.qr-m.in/aB3xY9zanswered at the edge/spring-menu/autumn-menu/winter-menuEDITED ANY AFTERNOON · LIVE ON THE NEXT SCAN
The measurement point: because every scan passes through the redirect, it can be counted there — which is also why the counting adds nothing the scanner waits for.

What a scan record honestly contains

Here, per scan: country, region and city (resolved from the network edge the scan arrived at), device class, OS and browser family, the hour in the scanner's own timezone, the referring host if any, and campaign tags. Plus one derived fact — whether this phone already scanned this code today — computed as a one-way daily digest so “unique visitors” can be counted without anything reversible to a person.

scans per day — livecities, not addressescity Kolkatadevice iPhonehour 19:00 localreferrer instagram.comvisitor a91f… (daily digest)ip — no column existsTHE RECORD, IN FULL
A real record, in full — and the field that doesn't exist. Location stops at city, identity stops at a daily one-way digest, and there is no IP column to subpoena.

The line, drawn precisely

  • No IP addresses, ever. The address is resolved to a place at the edge and discarded before our application sees it. Not truncated, not hashed-and-kept: absent, with no column to put one in.
  • No cross-site anything. A scan tells us about the scan. There is no pixel following the person onward, and the redirect sends no referrer downstream — the destination's analytics never learn your short URL.
  • Coordinates are the city's, not the person's. Every scan from anywhere in Mumbai carries the same pin.

This isn't privacy theatre bolted onto analytics — it's what makes the numbers defensible. When a client, a DPO or a journalist asks what the code on your packaging collects, “a city, a device class and an hour” is an answer that ends the conversation. The security page carries the full statement.

Attribution that survives audits

For campaigns, tag each code with source, medium and campaign — the tags append to every scan's destination URL, so your own site analytics attribute the visit, while the per-code dashboard slices scans by the same tags. Two details matter at scale: tags snapshot per scan, so retagging next quarter never rewrites this quarter's history; and the prefilled message trick (a WhatsApp code whose first message names the poster) gives even chat channels attribution — covered in the WhatsApp generator.

Reading it without a dashboard habit

Numbers that require remembering to log in stop being read. Retention windows here run 90 days to a year by plan; the emailed scan report turns any period into a PDF a client or a board reads without a login; and the first scan of any new code can email you the moment the printing provably works — the smallest, most reassuring metric in the whole system.

Measured, honest, and boring in the right ways — which is what tracking should be. If a vendor can't tell you exactly what their scan record contains, that's an answer too: the questions worth asking are on record, with ours answered.