A QR code campaign with no tracking is a poster you can never learn from. Tracking is cheap to set up and costs nothing to run, but it has to be designed in before the codes are printed. This guide shows the two ways to do it, when to use each, and what the numbers mean.
Two ways to count a scan
There are two methods, and they measure slightly different things.
UTM parameters are extra pieces of text on the end of a URL, such as ?utm_source=poster&utm_medium=qr&utm_campaign=spring. The page loads exactly as normal, and your web analytics records the source, medium and campaign of the visit. UTM works with any static QR code and any analytics tool. The cost is a longer URL, which means a denser code.
A dynamic QR code holds a short redirect link. When scanned, the redirect service counts the scan, records the time and approximate location, then forwards the phone to the real page. The code stays short, and you can change the destination later. The cost is that you depend on the redirect service staying online.
| Feature | UTM on a static code | Dynamic code |
|---|---|---|
| Counts scans that reach the page | ✓ | ✓ |
| Counts scans even if the page fails to load | ✕ | ✓ |
| Keeps the URL short | ✕ | ✓ |
| Destination can change after printing | ✕ | ✓ |
| Works with no third-party service | ✓ | ✕ |
| Records approximate location | Country and city from analytics | Country and city from the redirect |
| Records device type | ✓ | ✓ |
Do this
A short dynamic link, or a short URL with three UTM parameters, planned before the artwork went to print.
Not this
Deciding to track after 2,000 leaflets are printed with a bare URL, so every scan is lumped in with direct traffic.
Step 1: give every placement its own code
Tracking only tells you something if you can compare. A campaign with one code on the poster, the leaflet and the bus shelter reports one number, and you cannot tell which placement earned it. Make a code for each placement you want to compare. Same destination, different tag.
Worked example, UTM style. Three codes, one destination:
- Poster:
https://example.org/spring?utm_source=poster&utm_medium=qr&utm_campaign=spring - Leaflet:
https://example.org/spring?utm_source=leaflet&utm_medium=qr&utm_campaign=spring - Bus shelter:
https://example.org/spring?utm_source=busshelter&utm_medium=qr&utm_campaign=spring
Keep utm_medium as qr on all of them. Then one filter in your analytics shows every QR visit, and the source splits them by placement.
Do this
Three codes, one per placement, all with utm_medium=qr and a different utm_source.
Not this
One code copied onto every item, then wondering in June which placement was worth the money.
Step 2: keep the URL short enough to print
UTM parameters add length, and length adds modules. The 56-character link below fits a version 4 code, 33 modules wide. Add three UTM parameters and it becomes 108 characters and version 7, 45 modules wide. At the same printed size each module is about a quarter smaller, so the code needs to be printed larger to stay readable. The two codes below encode the same base link, with and without tracking.
Two fixes. Shorten the parameter values, s=p&m=qr&c=sp still works if your analytics is set up to read them, though the standard utm_ names work everywhere. Or use a dynamic code, which keeps the printed URL short and adds the UTM parameters at the redirect. The capacity calculator shows the version for any URL.
Do this
A 40-character dynamic link on the poster, with UTM parameters appended by the redirect.
Not this
A 140-character URL with five UTM parameters and a session ID, printed at 3 cm on a leaflet.
Step 3: know what the numbers mean
Analytics can tell you how many scans happened, when, roughly where, and on what kind of phone. That is enough to compare placements, spot the best time of day and see whether a poster in a new location is worth keeping.
Analytics cannot tell you who scanned. It cannot tell you how many people saw the code and walked past. It counts scans, not people, so one person scanning twice looks like two. And a scan that never reaches the page, because the signal dropped or the reader gave up, is counted by a dynamic redirect but not by UTM.
Do this
The bus shelter code earned three times the scans of the leaflet code, so next year the budget shifts toward shelters.
Not this
Reading 300 scans as 300 new customers, when 300 scans included the design team testing it 40 times.
Common mistake
Testing the live codes yourself and leaving those scans in the report. Filter out your own office IP address, or test with a copy of the code that points at the same page with utm_source=test, before launch day.
Step 4: run a simple A/B test
An A/B test compares two versions of one thing, changing one variable. QR codes make this easy because each version can carry its own tag. Worked example: a café wants to know whether a call to action helps. It prints two batches of table cards. Batch A has the code alone. Batch B has the same code with Scan for today’s specials printed above it. Each batch has its own code, utm_content=plain and utm_content=cta, and the cards are spread evenly across tables for two weeks.
At the end, compare scans per card. If B wins clearly, the call to action stays. Change only one thing per test, or you will not know which change made the difference.
Do this
Two batches, one change, two tags, same tables, same fortnight.
Not this
Batch B has a call to action, a bigger code and a different colour, so a win proves nothing.
Step 5: respect privacy
Counting scans by time and country is aggregate data and rarely a concern. Anything that identifies a person is different. Under GDPR, storing an IP address or a persistent device identifier is processing personal data. You need a lawful basis, and you should say what you collect near the code, for example a short line and a link to your privacy notice on the poster. Check what your dynamic code provider records by default, and turn off anything you do not need. The analytics explainer goes deeper into what a redirect can and cannot see.
Do this
Scan counts by day and country, a one-line notice on the poster, and no personal data stored.
Not this
A redirect service logging full IP addresses and device fingerprints indefinitely, with no notice anywhere.
Try it in QR Studio
QR Studio’s dynamic codes count scans by day, country and device type, keep the printed link short, and let you change the destination without reprinting. Every dynamic code has a scan history you can export.
Checklist
- Chose UTM, dynamic codes or both before the artwork went to print
- One code per placement to be compared
- utm_medium=qr on every tracked code, a distinct utm_source per placement
- URL kept short enough for the printed size, checked with the capacity calculator
- Own test scans filtered out before launch
- A/B tests change one variable at a time
- Privacy notice near the code and no unnecessary personal data collected
- Destination page checked on a phone on mobile data
- Report reviewed after two weeks and again at the end of the campaign
Try it yourself
Paste your tracked URL and see the version and module count. Then paste the short version and compare how much smaller the code can print.