Soku AI
All blog posts

UTM Naming Conventions That Survive a Cross-Channel Audit

August 6, 2026 · 9 min read

Soku Team

Soku Team

UTM Naming Conventions That Survive a Cross-Channel Audit

Campaign tags are the join key for your entire measurement stack. Every channel report, every attribution model, every "which channel drove this" question resolves through them. And unlike almost everything else in analytics, they are set by hand, by many people, under deadline.

That is why a channel report so often contains Paid Social, paid-social, paid_social, and [cpc](/glossary/cpc) as four separate rows describing the same thing. No model fixes that. It is upstream of every model.

This is the spec we use, why each rule exists, and what to do when the tags are already a mess. For the wider setup this fits into, see Cross-channel marketing attribution: a practical setup.

Anatomy of a UTM: the five slots and the distinct reporting question each one answers
Anatomy of a UTM: the five slots and the distinct reporting question each one answers

The rule behind every other rule

One slot, one question. Each UTM parameter answers exactly one reporting question, and no two slots answer the same one.

Almost every tagging mess traces back to violating this. Someone puts the campaign name in utm_source because it seemed descriptive. Someone encodes the creative in utm_campaign because there were three creatives and it felt tidy. Each of those decisions is locally reasonable and globally destructive, because it means the same fact now lives in two columns and the two columns disagree.

The five slots and their questions:

  • utm_source — which platform sent the click?
  • utm_medium — which channel type does it belong to?
  • utm_campaign — which business initiative paid for it?
  • utm_content — which creative variant was it?
  • utm_term — which keyword or audience?

Casing and separators: pick once, enforce always

UTM values are case-sensitive in most analytics tools. Meta and meta are two different rows. This causes more broken reports than any modelling question ever will.

The spec:

  • Lowercase everything. No exceptions, no proper nouns, no acronyms in caps.
  • Hyphens between words, underscores between segments. paid-social is one word-level value; us_q3_retarget is three segments. Using both consistently means you can split a campaign name programmatically and still read it.
  • No spaces, ever. A space becomes %20 and then becomes a separate row the first time someone copies the URL out of a doc.
  • ASCII only. Non-ASCII in a UTM survives roughly as far as the first tool that re-encodes it.

Closed vocabularies for source and medium

These two slots are what your channel report groups by, so they get closed lists. Adding a value requires a decision, not a keystroke.

utm_source — the platform, never a campaign name:

meta · google · tiktok · linkedin · reddit · microsoft · pinterest · snapchat · youtube · klaviyo · newsletter · partner-{name}

utm_medium — the channel type:

paid-search · paid-social · display · video · email · affiliate · referral · sms · push · organic-social

Two rules that prevent most of the damage:

Never use cpc or ppc as a medium. They describe a billing model, not a channel. cpc collapses paid search and paid social into one row, and once that data is collected it cannot be separated retroactively. Google Ads auto-tagging will produce cpc on its own — that is a reason to align your manual tags with a real taxonomy, not a reason to adopt cpc everywhere.

Never put the campaign in the source. utm_source=summer-sale is the single most common tagging error. It destroys the platform dimension permanently: you can no longer answer "how much traffic came from Meta" for any period where it happened.

Structured patterns for campaign and content

These two slots are free text, which means they need a pattern instead of a list.

utm_campaign = {market}_{quarter}_{objective}

Examples: us_q3_retarget, uk_q3_prospecting, global_q4_blackfriday

Why structured: a channel report with 200 unique free-text campaign names is unreadable. A structured one can be split into three usable dimensions after the fact — market, period, objective — without re-tagging anything. Free text cannot be recovered into structure; structure can always be flattened back into text.

utm_content = {format}_{concept}_{ratio}

Examples: vid_melt_9x16, img_founder_1x1, carousel_socialproof_4x5

This is the slot most teams leave empty, and it is the one that makes creative testing readable outside the ad platform. With it populated consistently, you can answer "did vertical video outperform square across every campaign this quarter" from your own analytics rather than from three separate platform UIs with three different attribution windows.

utm_term — keyword on search, audience segment on social. The important part is that it means exactly one thing per channel and that the meaning is written down. A slot with two meanings is worse than an empty slot.

The validation rule

A spec nobody can check is a suggestion. This is the pattern we validate against before a link ships:

^utm_source=(meta|google|tiktok|linkedin|reddit|microsoft|pinterest|snapchat|youtube|klaviyo|newsletter|partner-[a-z0-9-]+)&utm_medium=(paid-search|paid-social|display|video|email|affiliate|referral|sms|push|organic-social)&utm_campaign=[a-z0-9]+_(q[1-4])_[a-z0-9-]+(&utm_content=[a-z0-9]+_[a-z0-9-]+_[0-9]+x[0-9]+)?(&utm_term=[a-z0-9-]+)?$

Three ways to enforce it, in ascending order of how well they actually work:

  1. A shared link builder — a form that composes the URL from dropdowns. Removes typos, does not remove people who paste links by hand.
  2. A pre-launch check — validate every destination URL in a campaign before it goes live. Catches the hand-pasters.
  3. An automated tag audit — a weekly job that pulls distinct source and medium values from analytics and flags anything outside the vocabulary. This is the one that actually holds, because it catches drift rather than trying to prevent it.

Most teams implement (1), skip (2) and (3), and are surprised six months later. Drift is not a discipline problem; it is what happens when links are created faster than they are reviewed.

Cleaning up tags that are already a mess

You cannot retroactively fix collected data. What you can do is stop the bleeding and make the historical mess readable.

  1. Pull distinct source/medium pairs for the last 12 months. Sort by sessions descending. The long tail does not matter; the top 30 pairs are usually 95% of the traffic.
  2. Write a mapping table from every observed value to a canonical one — Paid Socialpaid-social, cpc where the source is metapaid-social, cpc where the source is googlepaid-search.
  3. Apply the mapping in the reporting layer, not in the raw data. Keep the raw values so the mapping stays auditable and reversible.
  4. Cut over new links to the spec on a fixed date and note the date in the report. A visible seam in the data is much easier to explain than a gradual, undocumented drift.
  5. Turn on the weekly audit so you never do this again.

The single decision that matters most in step 2 is cpc. If you have historical cpc traffic from multiple platforms, resolve it using the source dimension. If the source is also unreliable, that traffic is genuinely unrecoverable — record that in the report rather than picking a split and hoping.

Common mistakes and what they cost

MistakeWhat it costs
utm_medium=cpcPaid search and paid social permanently merged into one row
Campaign name in utm_sourceThe platform dimension is destroyed for that period
Inconsistent casingEvery value split across two or more rows
Empty utm_contentCreative performance readable only inside each ad platform
Spaces in valuesRandom %20 rows and broken grouping
Free-text campaign names200 unique values, no way to group by market or quarter
utm_term meaning two thingsA dimension nobody can use without asking who set it
Tagging internal linksSessions restart mid-journey, inflating counts and breaking paths

That last one deserves its own sentence: never put UTMs on links between pages of your own site. It starts a new session and attributes the visitor to whichever internal link they happened to click, which corrupts both your session counts and your attribution paths.

Where Soku fits

Soku generates ad variants and launches them as structured tests across Meta, Google, TikTok, and ChatGPT Ads. Because the same system creates the variant and launches it, the utm_content value describing that variant is written by the system rather than typed by a person under deadline.

That does not make a tagging spec unnecessary — you still need one, and it still has to cover your email, affiliate, and organic-social links. What it removes is the slot with the highest volume and the highest error rate: per-creative tags, generated in the dozens, where a single typo turns one concept into two rows and makes the creative comparison unreadable.

Where to go next

FAQ

Are UTM parameters case-sensitive? In most analytics tools, yes. Meta and meta become separate rows. Lowercase everything, without exception.

Should I use cpc as utm_medium? No. It describes billing, not a channel, and it permanently merges paid search with paid social. Use paid-search and paid-social. Google Ads auto-tagging produces cpc on its own — handle that in your reporting mapping rather than adopting it as the standard.

Do I need utm_content if the ad platform already reports creative performance? Yes, if you want to compare creative across platforms or evaluate it against downstream CRM outcomes. Platform-reported creative data lives inside each platform's own attribution window and cannot be compared directly across three of them.

Should I tag internal links? Never. A UTM on an internal link starts a new session and reattributes the visitor mid-journey, corrupting both session counts and attribution paths.

How do I fix historical tags? You cannot rewrite collected data. Build a mapping table from observed values to canonical ones, apply it in the reporting layer while keeping the raw values, and cut new links over to the spec on a documented date.

What is the single highest-leverage rule? Closed vocabularies for utm_source and utm_medium. Those two columns are what every channel report groups by, and they are the two that cannot be reconstructed once the data is dirty.

Related Tools

Related Use Cases

Relevant Reads

We use essential cookies to operate and secure Soku. With your permission, we also use optional analytics and advertising cookies to measure usage and campaigns. You can change your choice at any time. Privacy Policy