QR code tracking: what scan analytics measure, and what they don't

Every provider sells you a dashboard. Almost none of them will tell you how precise the numbers behind it are. Here are ours, field by field.

You can track scans only on a dynamic QR code, because tracking needs a server in the middle to record each visit. A static code sends the phone straight to your link with nothing watching, so there is nothing to count. If scan data matters to you, that decision is made before you ever print.

What follows is not a feature list. It is what a redirect can honestly measure, with our own implementation as the worked example, because "advanced analytics" is how this category avoids the question.

Tracking happens at the redirect, not in the image

A dynamic code encodes a short URL you control. A phone scans it, asks that short URL where to go, and the server answers with an HTTP 302 and the real destination. That single request is the entire measurement surface. Everything a scan report can ever tell you has to be readable from one HTTP request: when it arrived, which address it came from, and whatever the browser volunteered about itself in its User-Agent header. No cookie, no second visit, no script running on the scanner's phone.

That sets a limit worth knowing early. A 302 is cacheable when the response says it is, so if the redirect can be cached, a repeat scan may be answered from a cache and never reach the server at all. It is then invisible, permanently. Our redirect answers with Cache-Control: private, no-store for exactly this reason: a faster redirect that quietly undercounts is not a better redirect.

What each metric means

  • Total scans. One row per request that reached the redirect. It is a request count, not a people count, and nothing is filtered out of it.
  • Unique devices. The number of distinct address and User-Agent pairs. It is a proxy and it errs in both directions: a cafe full of phones on one network can collapse toward a single device, and one phone that moves from wifi to cellular can split into two.
  • Country. Resolved from the request address by Cloudflare, which hands us a two-letter country code. Country is the whole resolution. We do not store a city, because we never receive one.
  • Device class. The User-Agent string matched into five coarse buckets: mobile, desktop, tablet, bot, unknown. Deliberately crude, because a real scan filed under the wrong label corrupts the report worse than a vague label does.
  • Time of day. Twenty-four hour buckets, in UTC. Not the scanner's local time and not yours.
  • Scans over time. Thirty daily buckets, also UTC, with empty days present as zero so a gap reads as a gap instead of disappearing.

Check a code before you trust anyone's numbers

You can establish whether a QR code is trackable at all in about thirty seconds, with nothing printed and no account anywhere. Run this against your current provider before you compare dashboards.

  1. Decode the code to read the URL inside it. Our QR code URL checker does it in your browser.
  2. If that URL is your final destination, the code is static. Stop here: nobody can count its scans, including whoever sold it to you.
  3. If it is a short URL on the provider's domain, ask the URL what it does: curl -sI https://tgo.sh/EXAMPLE. A trackable code answers 302 with a Location header pointing at the real destination.
  4. Read the Cache-Control line in that same response. no-store means every scan reaches the server and gets counted. A long max-age means repeat scans can be served from a cache and go uncounted.

Step four is the one nobody runs, and it is the one that decides whether the totals in a dashboard are counts or estimates.

What scan data cannot tell you

This is the section most competitors leave out, so it is worth being blunt.

  • A scan is not a person. The same phone scanning twice counts twice. Unique devices is a floor on your audience, never a headcount.
  • Link previews get counted. Paste a short link into Slack, WhatsApp or iMessage and the app fetches it to build the preview card. That is a genuine request to the redirect, so it is a genuine row. We file the ones that identify themselves into the bot bucket, but they still count toward the total, because silently deleting traffic we guessed about would be the worse error.
  • A region is a country. "Approximate region" in most marketing copy means a country code. Ours does too. Treat any city-level claim in this category with suspicion, and ask which header it came from.
  • The clock is UTC. A dinner rush in California lands in the 01:00 and 02:00 buckets. Convert before you draw a conclusion about human behaviour.
  • The trail stops at the redirect. We know a phone asked where to go. Whether it arrived, read the menu, or bought anything happens on your site, where your own analytics live. Tag the destination with UTM parameters if you need to join the two.

Looking for the codes you scanned yourself?

"Scan history" means two different things and they have nothing to do with each other. If you want the history of scans on a code you own, that is everything above. If you are trying to find a QR code you scanned with your own phone last week, that is a phone question rather than a QR code question, and we answered it separately in where to find QR codes you've scanned.

Retention is the number to compare

Tracking comparisons are usually feature checklists, which is convenient for whoever wrote them. The number that decides whether you can answer a question in six months is how long the provider keeps the data, and it is generally printed nowhere near the word "analytics". The table below is what we found when we went looking.

Bitly is the honest comparison here because it is the strongest product in the category, and there are real reasons to choose it: a UTM builder, campaign grouping, integrations with HubSpot and Salesforce, and a bulk API that we do not offer. If you manage hundreds of links across a marketing team, that is worth paying for. If you printed one code on a menu and want to know whether anyone scanned it, the retention row is the whole story.

Every TangoQR code records scans from the moment it exists, on every plan, and keeps them for as long as the code exists. The free plan shows the full breakdowns for a code's first 50 scans and the running total forever, so upgrading later reveals history that was being collected the whole time rather than starting a new clock.

What one scan records, and how precise it is

Field Where it comes from Precision
Time The server clock when the redirect is hit To the second, stored and bucketed in UTC
Country Cloudflare's CF-IPCountry request header Country only, two letters. No city is received or stored
User agent The browser's own User-Agent header Self-reported, truncated at 1024 characters
Address The connecting IP address Stored to deduplicate devices, never shown in a report
Total scans One row per redirect request An exact request count, including repeats and previews
Unique devices Distinct address and user-agent pairs A proxy. Shared networks collapse it, roaming splits it
Device class User-Agent matched into five buckets mobile, desktop, tablet, bot, unknown
Time of day The hour read off the scan timestamp 24 buckets in UTC, not the scanner's local time

Source: TangoQR implementation: the scan_events table, RedirectsController#log_scan, and Analytics::CodeReport in this codebase

An example report for one code over 30 days

Line Example value How to read it
Total scans 412 Requests that reached the redirect, repeats and previews included
Unique devices 287 Distinct address and user-agent pairs. A floor on people, not a count
Last scan 3 hours ago The freshness signal. Silence here is the useful alarm
Top country US, 301 scans Country resolution. The remaining 111 are spread or unknown
Busiest hour 18:00 UTC Convert to local time before concluding anything about behaviour
Device mix 88% mobile, 6% bot The bot share is your link-preview and crawler noise
Scans over time 30 daily buckets Shape beats total: a launch spike and a steady trickle differ

Source: Illustrative. The lines and their meanings match the real per-code report; the values are invented so the shape is readable

How long QR scan analytics are kept, verified 17 August 2026

Plan Price How long scan data is kept
TangoQR Free $0 For the life of the code. Full breakdowns for a code's first 50 scans, total scans always
TangoQR Pro $10/mo For the life of the code. Full breakdowns on every code, no scan limit
Bitly Free $0 No click or scan analytics at all. Scans are collected, none are shown
Bitly Core $10/mo 30 days
Bitly Growth $29/mo 120 days
Bitly Premium $199/mo 1 year

Source: Bitly's own pricing page and its Free Plan post, both checked 17 August 2026

Common questions

Can you track a static QR code?

No, and no provider can. A static code has the destination encoded in the pattern, so the phone goes straight there and no server you can see is involved. There is nothing to instrument after the fact. This is why the static or dynamic decision has to happen before the print run, not after.

Do I need to add anything to my website to track scans?

No. Measurement happens at the redirect, one hop before the browser reaches your site, so it works even if your destination is a PDF or a page you do not control. The flip side is that your own analytics will not label that traffic as a QR scan on its own. Add UTM parameters to the destination if you want the two reports to line up.

Does QR code tracking identify who scanned my code?

No. A scan record is a timestamp, a two-letter country code, a self-reported browser string, and an address used only to deduplicate devices. There is no name, no phone number, and no cross-site tracking. It is enough to tell you that scans are arriving from these countries on these devices at these hours, and not enough to tell you anything about a person.

Can I start tracking a code I already printed?

Only if it was dynamic to begin with. If it points through a redirect, its scans have been recorded all along and are waiting for you. If it was static, the printed pattern points straight at your destination and cannot be retrofitted. Check which kind you have with the QR code analytics checker before you assume either way.

Why is my scan count higher than the number of people I watched scan it?

Three reasons, usually all at once. Repeat scans from the same phone each count. Link previews from chat apps and crawlers fetch the redirect without a human involved. And a person who scans, loses the page, and scans again counts twice. Compare the total against unique devices to get a sense of the gap, and read the bot share in the device mix.

How many scans does a free TangoQR code track?

All of them, forever. Collection is never limited or throttled by plan. What the free plan limits is the detailed view: the country, device and time-of-day breakdowns stay clear for a code's first 50 scans, and after that the running total keeps showing while the breakdowns move behind an upgrade. The data underneath is still being recorded, so upgrading shows you the history rather than starting a new one.

Sources

Keep reading

Try it here

Make a QR code you can change later

Every TangoQR code is an editable redirect, free to start. Print once, repoint forever.

  • No account needed to start
  • Editable on the free plan
  • PNG and SVG download

← All posts