How do I set up UTMs on QR codes so GA4 shows which poster sent each visit?
Add UTM parameters to each QR code's destination URL, with utm_medium set to ooh or print and utm_content set to the individual placement, then read them in GA4 under Reports, Acquisition, Traffic acquisition. Without UTMs, visits from a printed QR code usually show up in GA4 as Direct, with nothing to say which poster they came from.
By Daniel Zaremba, founder of BetterQR · Updated
Why do QR code visits show as Direct in GA4?
When someone clicks a link on a website, the browser usually tells the next site where the visitor came from (the referrer). A phone camera opening a URL from a QR code passes no referrer, so GA4 has nothing to go on and files the session under Direct, alongside people who typed your address or used a bookmark. UTM parameters fix that by carrying the source in the URL itself.
What are the five UTM parameters?
| Parameter | Answers | Print and OOH convention | GA4 dimension |
|---|---|---|---|
utm_source | Who or where sent the visit | Media owner, venue or print run, e.g. bus-shelters, leaflet-drop | Session source |
utm_medium | What kind of channel | Always ooh for outdoor, print for flyers, packaging and menus | Session medium |
utm_campaign | Which campaign | Campaign and period, e.g. spring-sale-26 | Session campaign |
utm_content | Which placement or creative | The individual site, e.g. shelter-station-rd-c | Session manual ad content |
utm_term | Paid search keyword | Optional; some teams use it for the creative version (design-a) | Session manual term |
utm_source, utm_medium and utm_campaign are the ones to always set. For OOH, utm_content matters just as much, because it's the only tag that separates one poster from another.
What UTM naming scheme should I use for print and OOH?
GA4 treats UTM values as case-sensitive text, so OOH and ooh become two separate rows. Use these rules:
- Lowercase everything.
- Hyphens inside a value, no spaces (spaces turn into
%20). - Keep a small fixed list of mediums:
ooh,print, and nothing else for offline. - Put the most specific detail in
utm_content, never in the campaign name, so campaign totals still add up. - Keep the scheme in a shared spreadsheet and copy from it rather than typing values by hand.
Example values for one campaign across formats:
| Placement | source | medium | campaign | content |
|---|---|---|---|---|
| 48-sheet billboard, A40 | billboard | ooh | spring-sale-26 | 48sheet-a40-west |
| Bus shelter, Station Road | bus-shelter | ooh | spring-sale-26 | shelter-station-rd-c |
| A4 poster in a partner café | partner-venue | ooh | spring-sale-26 | poster-corner-cafe |
| Door-drop flyer, north postcodes | leaflet-drop | print | spring-sale-26 | flyer-n-postcodes |
| Insert card in parcels | packaging | print | spring-sale-26 | parcel-insert-v1 |
The finished URL for the bus shelter looks like this: https://example.com/spring?utm_source=bus-shelter&utm_medium=ooh&utm_campaign=spring-sale-26&utm_content=shelter-station-rd-c. The QR code UTM builder assembles the URL and its QR code in one step.
Should the UTMs go inside the QR code?
Not if you can avoid it. A static QR code has to contain the whole URL, and a 130-character UTM link makes a much denser pattern than a 30-character one, which is harder to scan from a distance (see billboard QR code size). It also fixes the tags forever: a typo in utm_content means reprinting.
With a dynamic code, the printed pattern holds a short redirect link and the UTMs live on the destination. In BetterQR you set source, medium, campaign, term and content on the code (Pro and up); they're added to the destination when someone scans, so the printed code stays short and you can correct a tag after printing.
Where do I see QR code traffic in GA4?
GA4's interface changes from time to time, so menu names may differ slightly, but the standard path is:
- Open Reports, then Acquisition, then Traffic acquisition.
- The table defaults to the session channel group. Use the dimension drop-down above the first column to switch to Session source / medium. Your codes show as rows such as
bus-shelter / ooh. - Switch to Session campaign to see campaign totals.
- To split by placement, add a secondary dimension (the plus icon next to the dimension name) and pick Session manual ad content, which holds your
utm_contentvalues. - Use the search box above the table to filter to
oohor your campaign name.
For a reusable view, build a free-form exploration under Explore: rows for Session campaign and Session manual ad content, values for Sessions, Engaged sessions and Key events, and a filter where Session medium exactly matches ooh. This becomes your per-placement report.
Why does my OOH traffic show as Unassigned?
GA4's default channel group has rules for mediums like email, cpc and social, but none for offline media, so sessions tagged ooh or print usually land in Unassigned in the channel view. Your data is fine; look at source / medium or campaign instead. If you want an "Offline" channel, an admin can create a custom channel group (under Admin, Data display, Channel groups) with a rule matching those mediums.
How do I track conversions from QR codes in GA4?
Mark the action you care about (a purchase, a form submission, a booking) as a key event in GA4. Once it is, the Traffic acquisition report and your exploration show key events per source, medium, campaign and placement. Make the landing page match the offer on the poster: a QR code that lands on a generic homepage loses people before they convert.
Why are there more QR scans than GA4 sessions?
A dynamic code counts the scan during the redirect, before your page loads. GA4 only counts a session if its tag runs on your page and is allowed to. So scans are almost always higher than sessions. Common reasons:
| Reason | Effect |
|---|---|
| Visitor declines analytics cookies | GA4 records nothing, or only modelled data if consent mode is set up |
| Ad or tracker blockers on the phone | GA4 tag never loads |
| Visitor closes the page before it loads | Scan counted, no session |
| Same person scans twice in a few minutes | Two scans, one session |
| Destination isn't your website (app store, PDF, map) | No GA4 tag at the destination at all |
| Redirect on your site strips the query string | Session arrives untagged, shows as Direct |
The gap is normal. What matters is that it's roughly stable across placements. If one placement has far fewer sessions per scan than the rest, check its destination URL first: a broken tag or a redirect that drops the UTMs is the usual cause.
The reverse can happen too. If someone copies the tagged URL from their browser and shares it, those visits count as sessions with no scan. That's another reason to keep the scan count from the code as your primary response number and use GA4 for what happens after the visit.
Can I see UTM data without GA4?
Yes. On Business and up, BetterQR breaks each code's scans down by the UTM source, medium and campaign they carried, and you can export scans to CSV for a spreadsheet or Looker Studio. The OOH attribution guide explains how scan data and GA4 fit together in one dashboard.
Quick setup checklist
- One code per placement, each with its own
utm_content utm_mediumisoohorprint, nothing else- All values lowercase, hyphenated, copied from one spreadsheet
- UTMs on the destination of a dynamic code, not printed in a static one
- Open each tagged URL once and confirm it appears in GA4 Realtime
- Your main conversion marked as a key event
- A saved exploration: campaign × manual ad content × sessions and key events
Related
- QR code UTM builderBuild a UTM-tagged link and its QR code in one step, so GA4 shows which print placement sent each visit. Free, no sign-up.
- OOH attribution for small brandsHow small brands measure billboards, posters and flyers: one QR code per placement, UTMs into GA4, and a simple cost-per-scan comparison.
- Track billboard QR code scansTrack scans from each billboard separately: one dynamic QR code per site, clear naming, and a scan report you can compare week by week.
- Measure billboard ROIMeasure billboard and poster ROI without an enterprise tool: cost per scan, scan-to-visit and visit-to-sale, worked through with example numbers.