What Is a UTM Code, and How to Build One
A UTM code tags a link so your analytics tool knows exactly where a click came from. Here's how to build, track, and audit one correctly.

A visitor lands on your site at 11:47pm. Your analytics tool has no idea who sent them.
It just says "direct."
A UTM code is the fix for that blank spot. It's a short string of text added to the end of a URL, and it tells your analytics tool exactly where a click came from, what sent it, and which specific piece of content earned it, before the visitor even lands.
Without one, every click from a newsletter, a paid ad, a partner's blog post, and a listing you paid good money for gets folded into the same bucket: direct, or referral, or nothing at all. You can't tell a $200 ad from a free mention in a Slack channel. They look identical in the report.
That's not a reporting gap. That's a budget decision made blind.
The fix takes about ninety seconds per link, and once it's built, it never has to be rebuilt again. Here's what a UTM code actually is, what each of its parameters does, and how to build, track, and audit one without wrecking your own data in the process.
What a UTM Code Actually Is
UTM stands for Urchin Tracking Module. Urchin was the web analytics company Google bought in 2005, and the tracking approach it built became the backbone of what turned into Google Analytics itself (Adjust glossary).
Twenty years later, the name stuck to the parameters marketers still call UTM codes, even though the company that coined it disappeared into Google's stack long ago.
Here's what one looks like in practice. Take a plain URL:
https://yoursite.com/pricing
Add three parameters and it becomes a tagged link:
https://yoursite.com/pricing?utm_source=newsletter&utm_medium=email&utm_campaign=fall_launch
Nothing about the page changes. Strip everything after the question mark and it loads identically. The parameters exist only for the system reading the request, your analytics tool, sitting quietly in the address bar until somebody clicks.
Google's own documentation now lists nine possible parameters, not five: utm_id, utm_source, utm_medium, utm_campaign, utm_source_platform, utm_term, utm_content, utm_creative_format, and utm_marketing_tactic. Two of them, utm_creative_format and utm_marketing_tactic, aren't even reported inside Google Analytics properties yet. They're accepted and stored, but nowhere in the interface shows them (Google Analytics Help).
For almost every brand reading this, three or four parameters do the whole job. The other five exist for people running attribution across a dozen ad platforms at once, and you'll know when you're that person.

UTM Parameters Explained, One at a Time
| Parameter | Required | What it tells you | Example |
|---|---|---|---|
| utm_source | Yes | Where the click came from | newsletter, linkedin, google |
| utm_medium | Yes | What kind of channel sent it | email, cpc, social, referral |
| utm_campaign | Yes | Which specific push it belongs to | fall_launch, spring_sale |
| utm_term | No | The paid keyword, if there is one | running+shoes |
| utm_content | No | Which specific creative or link | header_cta, footer_link |
Google's own Campaign URL Builder only marks two fields required, campaign source and campaign medium, with campaign name recommended alongside them and everything else optional (Google's Campaign URL Builder).
The values are case sensitive, and this is where most tagging quietly falls apart. Tag one email utm_source=Newsletter and the next utm_source=newsletter, and your report now shows two sources instead of one, splitting a single channel's numbers in half without any error message telling you it happened. Google says so directly: utm_source=google and utm_source=Google are treated as different values in every report that reads them (Google Analytics Help).
Lowercase everything, always. Pick one word for each medium, email, not Email or newsletter or e-mail, and write it down somewhere everyone tagging a link can actually see it. That one habit prevents most of what breaks a few sections down.
How to Create a UTM Link
- Start with the destination URL you already have. Point it at the specific page the click should land on, not the homepage by default.
- Set utm_source to the specific place the click starts. Use linkedin, not "social." Use the partner's exact name, not "partnership."
- Set utm_medium to the type of channel, not the platform: email, referral, cpc, paid_social. This is the field your analytics tool groups traffic by, so keep it generic on purpose.
- Set utm_campaign to the specific push this link belongs to. Use fall_launch, not marketing.
- Add utm_content only when two links point to the same URL from the same campaign and you need to tell them apart, a header button against a footer link inside the same email.
- Build the URL with a tool rather than typing it by hand. The Board's own UTM builder fills the fields and assembles the link, and it saves you from a stray ampersand breaking the whole string.
Say you're launching a directory listing this week and want to know if it actually sends anyone. The listing's outbound link becomes:
https://yoursite.com/?utm_source=directoryname&utm_medium=referral&utm_campaign=launch_week
Now every click from that one listing shows up as its own row, separate from organic search, separate from the other directories you also paid for that month.
Save the finished link somewhere before you use it: a spreadsheet, a doc, a shared note. Six months from now you will not remember which campaign name you used for which push, and neither will whoever tags the next one.

How to Track UTM Links in Google Analytics
- Open your GA4 property and go to Reports, then Acquisition, then Traffic acquisition.
- Change the primary dimension to Session source / medium. The report now reads clicks by exactly the utm_source and utm_medium pair you set on the link.
- Add Session campaign as a secondary dimension to break each source and medium pair down by the utm_campaign value.
- Filter to the date range the campaign actually ran, and check the total against what the platform sending the traffic reports for clicks sent. A gap past 10 to 15 percent usually means a tagging mistake, not a real discrepancy.
- Use the Realtime report on launch day to confirm the tag is firing at all. Waiting until tomorrow to find out a link was broken wastes the day it mattered most.
If a campaign never shows up in the report, check the URL first. A missing question mark, an ampersand swapped for a plus sign, or a parameter typed as utm_soruce instead of utm_source, and Analytics has nothing consistent to group by. The click still happened. It's just filed under (not set), the bucket GA4 uses for traffic it can't classify.
Once the campaign shows up cleanly, the number that tells you whether the click was worth sending matters more than the click count by itself. That's the whole argument behind what counts as a good CTR: a raw click total without a rate to compare it against is just a bigger number that feels like progress.
The UTM Mistakes That Quietly Break Your Reports
- Tagging your own internal links. A UTM parameter overwrites the visitor's recorded source the moment they click it, so a banner on your homepage pointing to your own pricing page with utm_source=homepage erases whatever brought that visitor to the homepage in the first place.
- Reusing one campaign name across different channels. utm_campaign=launch on both an email and a paid ad flattens two different costs into one row, and you lose the ability to tell which one paid for itself.
- Letting an ad platform's own auto-tagging fight your manual tags. Google Ads and Meta add their own tracking parameters automatically once auto-tagging is turned on in the account. Layering a manual UTM on top of that can override detail the platform was already giving you for free.
- Skipping utm_medium and leaving it blank. Without it, the visit still gets a source but no channel type, and the default channel grouping has nowhere consistent to file it.
- Never writing the naming convention down. The first campaign anyone tags sets no rule for the second person doing it, and by the tenth campaign nobody agrees what "social" was supposed to mean.
None of these break the click itself. The visitor still lands on the page. What breaks is your ability to trust the report that's supposed to tell you where that visitor came from, which is the entire reason to tag the link in the first place.
That trust matters more once real money is riding on the answer, because the ROAS formula only comes out accurate when the campaign cost and the campaign traffic can actually be matched to each other.

UTM Codes vs Other Ways to Track a Click
A UTM code isn't the only way to know where a click came from, and it isn't always the right one.
Google Ads and most major ad platforms auto-tag outbound clicks with their own parameter (gclid for Google, fbclid for Meta) the moment auto-tagging is switched on in account settings. That data feeds cost and conversion numbers back into the platform's own dashboard without you touching a URL. Manual UTM tags still matter alongside it, as a layer underneath, not a replacement for it.
Link shorteners built around UTM tracking exist for a real reason. A UTM-tagged URL with several parameters filled in can run past 100 characters, which is fine buried in an email footer and ugly on a business card, a QR code, or a single social post. A shortened link with the UTM baked in behind it solves the length problem without losing the tracking underneath it, and the tools built specifically for this were still being checked against their own pricing pages as recently as September 2026 (Terminus, UTM builder comparison).
For most brands running a website and a handful of channels, a UTM code covers the whole job on its own. The shortener and the platform auto-tagging only earn their place once the number of channels, or the length of the link itself, becomes the actual problem.
![]()
Where a UTM-Tagged Link Earns Its Keep
Any link you don't fully control the placement of deserves a tag, because the platform showing it and your own analytics rarely agree on what a "click" means.
A guest post, a directory listing, a newsletter mention: how to promote a new website walks through a dozen of these across the first 90 days, and every one of them is worth tagging the moment it goes live, before the first click happens, not after somebody asks why traffic went up last Tuesday.
The Board is one example worth naming directly, because it's a paid placement and says so plainly. Rank there is sorted purely by what's been paid, and the platform's own count of impressions and clicks tells you how many people saw and clicked a listing. What it can't tell you is what those clicks did once they landed, whether they bounced, signed up, or bought something. That's the gap a UTM code closes. Tag a listing's outbound link with utm_source=theboard and utm_medium=referral, and the platform's own click count and your own session count should land close enough to trust either one going forward.
Whoever is currently paying to hold those top three spots is exactly the kind of placement worth tagging separately from everything else you're running that week, precisely because the price attached to it makes the traffic worth checking.
Run the tagged clicks against what the placement actually cost and you have a real number instead of a guess. What CAC actually measures is the cost of acquiring one paying customer, and a UTM-tagged link is what lets you attribute that cost to the channel that earned it instead of splitting it evenly across everything you tried that month.
Everything else about measuring what a channel actually returns lives under Traffic, and none of it works without a clean tag underneath it.
Pick one live campaign this week: the newsletter going out Thursday, the directory listing you paid for last month, the guest post that's already published. Tag its link before you touch anything else. Check the Traffic acquisition report the day after it runs. If the source and medium show up clean, you've built something that keeps working on every link after it. If they don't, you've caught the typo before six months of data disappeared into direct traffic.