QR Codes in Print: UTM Tracking and Measuring Results
By the CirculateQR team · Updated · 7 min read
Short answer
Give every print placement its own QR code, tag each code’s link with UTM parameters (at least utm_source, utm_medium and utm_campaign), and capture what happens after the visit with an offer code, a landing-page-only offer or a form. Then divide each placement’s cost by its scans and by its leads. Scans show which piece got attention; leads show which one paid for itself. A holdout group that receives nothing shows how much of the result the campaign actually caused.
On this page
- How do you set up tracking for a print campaign?
- What are the UTM parameters, and how should you name them?
- What does a tagged URL look like, and does it make the code denser?
- Where do QR code visits show up in Google Analytics 4?
- How do you measure results beyond the visit?
- What does a worked direct-mail example look like?
- How does CirculateQR handle UTM tags?
- Frequently asked questions
Key facts
- Google recommends always setting utm_source, utm_medium and utm_campaign (as of October 2026).
- GA4 treats tag values as case-sensitive: utm_source=Postcard and utm_source=postcard are reported separately.
- In GA4’s default channel group, a medium such as print or direct_mail matches no rule and is reported as “Unassigned”.
- Four UTM tags turned a 26-character URL (25 × 25 modules at error correction M) into a 123-character one (49 × 49).
- Cost per scan = cost ÷ scans. Cost per lead = cost ÷ leads. Compare placements on both.
- A QR visit with no tags usually shows up as Direct, because a camera scan sends no referrer.
How do you set up tracking for a print campaign?
All of this has to happen before anything is printed:
- Decide what counts as a result. Pick the action that pays for the campaign, such as a booking, a lead form or a sale, and decide how you will capture it.
- Make one code per placement. List every placement and every version of each piece, and create a separate dynamic QR code for each one.
- Tag every code. Give each code utm_source, utm_medium and utm_campaign values from a written, lowercase naming convention.
- Send scans to a measurable page. Point the codes at a landing page with its own offer, offer code or form, and mark the form submission as a key event in your analytics.
- Test before printing. Scan every proof and check that the visit appears in your analytics with the right campaign values.
- Compare cost per scan and cost per lead. After the campaign, divide each placement’s cost by its scans and by its leads, and compare against a group that got nothing.
The second step matters most. If a flyer, a poster and a mailer share one code, you can count the total but never tell which piece earned it. A dynamic code per placement also lets you fix a broken link after printing.
What are the UTM parameters, and how should you name them?
UTM parameters are tags added to the end of a URL. Analytics tools read them and file the visit under the campaign you named. There are five classic ones:
| Parameter | Describes | Print example |
|---|---|---|
utm_source | The specific piece or publication | postcard, city_magazine |
utm_medium | The type of channel | direct_mail, print, signage |
utm_campaign | The promotion | 2026_spring_tuneup |
utm_content | The version or spot | version_a, back_cover |
utm_term | A paid search keyword | Usually left out in print |
Google’s URL builder guidance says to always use the first three and notes that values are case-sensitive (as of October 2026). A convention that avoids most mess:
- Lowercase only. “Postcard” and “postcard” become two rows in your reports.
- No spaces. Use underscores or hyphens, and pick one.
- Fixed vocabulary. Keep the allowed values in a shared sheet, so nobody invents “directmail” next quarter.
- No personal data. Never put names or email addresses in tags.
What does a tagged URL look like, and does it make the code denser?
Here is the postcard link from the example below, with four tags:
https://example.com/tuneup?utm_source=postcard&utm_medium=direct_mail&utm_campaign=2026_spring_tuneup&utm_content=version_aIf you put that whole URL into a static code, every extra character becomes extra modules. The encoder CirculateQR uses produces a version 2 code (25 × 25 modules) for the bare 26-character address and a version 8 code (49 × 49) for the 123-character tagged one, both at error correction M. Printed 25 mm wide, the modules shrink from 1 mm to about 0.5 mm, which is harder to scan from a distance or with an older phone. The size guide covers the print side.
No tags: 25 × 25
Four tags: 49 × 49
A dynamic code avoids the trade-off. The printed pattern holds only a short link, and the tags are added when the server redirects the scan.
Where do QR code visits show up in Google Analytics 4?
As of October 2026, open Reports → Acquisition → Traffic acquisition, as described in Google’s Traffic acquisition report help page. The table groups sessions by channel at first. Change the primary dimension to Session campaign or Session source / medium to see your tags. According to Google’s traffic-source dimensions page, utm_source, utm_medium and utm_campaign feed the source, medium and campaign dimensions, and utm_content feeds Session manual ad content, which you can add as a secondary dimension.
Expect your print traffic in the Unassigned channel. GA4’s default channel group has rules for email, SMS, display and others, but none for print or direct_mail. If that bothers you, a custom channel group (Admin → Data display → Channel groups) can define a “Print” channel and applies to past data too.
How do you measure results beyond the visit?
A visit is not a sale. Use at least one of these to connect scans to money:
- Offer codes per placement. “Quote SPRING-A when you call” catches people who saw the piece but phoned instead of scanning.
- An offer that exists only on the landing page. Anyone redeeming it came through the code.
- Form submissions as key events. In GA4, mark the form’s event as a key event (Admin → Data display → Events, then the star icon, as of October 2026), and it appears next to each campaign.
- A holdout group. Leave out a random set of addresses, or comparable neighborhoods, and compare their sales over the same period.
The holdout answers the question the other methods can’t: would these customers have bought anyway? Suppose you mail 5,000 households and hold back another 500, chosen at random from the same list. The mailed group books 60 jobs; the held-back group books 3, a 0.6% rate. At 0.6%, the mailed households would have booked about 30 jobs without the postcard (5,000 × 0.6%), so the mailing caused roughly 30. That is $100 per extra booking on a $3,000 mailing. Groups this small give noisy results, so treat it as a rough check.
What does a worked direct-mail example look like?
A hypothetical heating company mails a spring tune-up postcard to 5,000 homes at $0.60 per piece, printing and postage included. Half get version A ($20 off), half get version B (a free filter). Each version has its own QR code and its own phone offer code.
| Version A | Version B | Total | |
|---|---|---|---|
| Pieces mailed | 2,500 | 2,500 | 5,000 |
| Cost | $1,500 | $1,500 | $3,000 |
| Unique scans | 120 | 75 | 195 |
| Scan rate | 4.8% | 3.0% | 3.9% |
| GA4 sessions | 101 | 63 | 164 |
| Web form leads | 15 | 10 | 25 |
| Phone leads (offer code) | 9 | 17 | 26 |
| Total leads | 24 | 27 | 51 |
| Cost per scan | $12.50 | $20.00 | $15.38 |
| Cost per lead | $62.50 | $55.56 | $58.82 |
On scans alone, version A wins by 60%. Count the phone calls and version B produces more leads at a lower cost per lead. Judged on scans, the company would have reprinted the wrong postcard. Sessions run below scans, as usual; how scan tracking works explains why.
How does CirculateQR handle UTM tags?
Each CirculateQR dynamic code has its own utm_source, utm_medium and utm_campaign fields, with a UTM builder in the dashboard. They are added to the destination when the scan is redirected, overriding the same keys if the destination already has them, and they are recorded with each scan in your analytics. This works on every plan. For utm_content or utm_term, put them in the destination URL yourself, for example ?utm_content=version_b on the second code.
The printed code holds only the short link, so tags never make it denser, and one code per placement is just one more code with its own tags. For one code per recipient, see bulk QR codes and the API.
Frequently asked questions
Why do my QR code visits show up as Direct in Google Analytics?
A phone camera opens the link without a referring web page, so an untagged QR visit looks the same as someone typing the address. Add utm_source, utm_medium and utm_campaign to the link. If tagged visits still show as Direct, check whether your site redirects the landing page and drops the query string.
Should utm_medium be print, qr or direct_mail?
Any of them works if you use it consistently. A common approach is to describe the channel (print, direct_mail, signage) in utm_medium and keep the QR detail for utm_content. GA4’s default channel group has no rule for any of these values, so expect them under Unassigned unless you create a custom channel group.
Why do I see (not set) in my campaign reports?
Google Analytics shows (not set) when a parameter it reports on is missing from the URL. A code tagged with utm_source and utm_medium but no utm_campaign, for example, shows (not set) as its campaign. Tag every code with the same three parameters.
Can I use one QR code on several different flyers?
You can, but you will only ever see the combined total. If you want to know which flyer, location or version worked, each needs its own code with its own tags. With dynamic codes, extra codes cost nothing to print, so separating them is cheap.
Can I add UTM tags to a QR code that is already printed?
Yes, if it is a dynamic code: edit the destination or the code’s tag fields, and every later scan carries the new tags. A static code cannot change, so its tags are fixed at whatever was encoded when it was generated.
Last updated . Written by the CirculateQR team. Spotted something wrong or out of date? Email support@circulateqr.com.
Tag every code without making it denser
Create a dynamic code per placement and set its UTM source, medium and campaign; the tags are added at scan time on every plan.
Related guides
- How to Track QR Code Scans, and What Analytics MissHow do I track how many people scan my QR code?
- Dynamic vs Static QR Codes: What Is the Difference?What is a dynamic QR code, and how is it different from a static one?
- Bulk QR Codes: Create Hundreds at Once or by APIHow do I create QR codes in bulk or automatically?
- QR Code Size Guide for Print: Minimum Size and DistanceWhat size should a printed QR code be?