Reusable Campaign URL Templates for Recurring Marketing Programs

Your newsletter went out twelve times last year. How many campaigns does GA4 think that was?
If the answer is twelve unrelated rows, you have a template problem. Not a tagging problem. Every individual link was probably fine.
I run into this constantly with linkutm customers, and it took me a while to see why. Teams build a solid template for a channel, use it once, then rebuild it slightly differently the next month. Twelve months later there is no trend line. Just twelve strangers sitting next to each other in a report.
This post is about the one axis the rest of the template conversation skips: time. Not which fields a template holds, and not how to write the values. Those are covered. If you need the template as an object, with its locked and variable fields, read pre-built UTM templates for common campaign types. If you need to decide which fields exist at all, read the 5-field UTM structure template. If you need the naming grammar, UTM naming conventions has the rules.
Here we are dealing with what happens on the second run. And the twelfth. And the one twelve months later.
| Recurrence pattern | Example | Comparison you want |
|---|---|---|
| Dated editions | Monthly newsletter, weekly drop | This edition vs the last one |
| Always-on | Welcome flow, evergreen retargeting | This month vs last month, same campaign |
| Seasonal return | Black Friday, annual conference | This year vs last year |
Three patterns. Three different template treatments. Most teams use one template for all three and wonder why two of them report badly.
What a Reusable Campaign URL Template Actually Is
A reusable campaign URL template is a saved set of UTM parameter values, some fixed and some left open, that a recurring program reuses for every edition so each run stays comparable to the last.
The word doing the work is comparable. A channel template keeps links consistent across a campaign. A reusable campaign URL template keeps them consistent across time, which is harder, because the thing breaking consistency is not a teammate. It is you, six weeks later, with a slightly better idea about naming.
Here is the layering, because these three things get conflated constantly:
- A naming convention governs how a value is written. Lowercase, hyphens, no spaces.
- A template governs which values are already decided before anyone opens a builder.
- A recurrence pattern governs which of those decided values is allowed to change on the next run.
That third layer is the one nobody writes down. It is also the only one that matters once a program has run more than twice.
Honest limitation: this only pays off for programs that actually repeat. For unrelated one-off campaigns, a channel template is enough.

The Three Recurrence Patterns Every Template Has to Handle
Pick the pattern first, then build the template. Building the template first is how you end up with a launch date frozen into an evergreen flow.
| Pattern | What changes per run | What stays fixed | The trap |
|---|---|---|---|
| Dated editions | utm_campaign edition suffix, utm_content creative |
utm_source, utm_medium, program name |
Rebuilding the name from memory each time |
| Always-on | Nothing | Everything | Freezing a launch date into a campaign that never ends |
| Seasonal return | Edition year, creative, offer | Program name, source, medium | The comparison target is 12 months back |
Dated editions
These are programs with discrete, dated instances. A monthly newsletter, a weekly product drop, a quarterly webinar.
The template keeps utm_source and utm_medium locked forever. The program name inside utm_campaign stays locked. Only the edition marker moves.
https://yourbrand.com/offer?utm_source=newsletter&utm_medium=email&utm_campaign=newsletter-2026-10&utm_content=header-cta
Next month, exactly one character group changes: 2026-10 becomes 2026-11. Nothing else is retyped, which is the entire point.
Always-on programs
An always-on program runs continuously with no editions. Welcome flows, abandoned cart sequences, evergreen retargeting.
These should carry no date at all. I see the opposite constantly: someone builds the welcome flow template in March 2026, writes utm_campaign=welcome-flow-2026-03, and eighteen months later that flow is still sending with a March 2026 label on it. The date is not wrong exactly. It is just meaningless, and it makes the campaign look stale in every report anyone opens.
https://yourbrand.com/start?utm_source=lifecycle&utm_medium=email&utm_campaign=welcome-flow&utm_content=email-1
For always-on programs, time comes from the date range of the report, not from the tag. That is what date ranges are for.
Honest limitation: if you materially rewrite an always-on flow, you now have two different things sharing one campaign value, and the report averages them. I cover the fix in the versioning section.
Seasonal returns
Black Friday. The annual user conference. The back-to-school push.
Structurally these look like dated editions, but the run you want to compare against happened twelve months ago. That gap collides with GA4’s retention settings in a way most teams discover too late. Section seven deals with it.
The Rollup Problem: One Campaign Value or One Per Edition?
Give each edition its own utm_campaign value, and put the program name at the front of every one.
That is the short answer. Here is the trade it resolves.
| Approach | You gain | You lose |
|---|---|---|
One shared value (newsletter) |
Instant program-level totals and a clean trend | Any ability to see which edition did what |
One per edition (newsletter-2026-10) |
Full per-edition detail | Program totals need a filter or a grouping step |
Per-edition wins for an unglamorous reason: you can always roll detail up, but you can never split a total back down. If you tagged twelve months as newsletter, the October data is gone. Not hidden. Gone. It was never recorded separately.
The prefix is what makes rollup cheap. Because every edition starts with newsletter-, a single “contains” filter on the campaign dimension in your Traffic acquisition report gives you the program total in about five seconds. You get both views from one tagging decision.
This only works if the prefix is genuinely identical every time. newsletter-2026-10 and news-letter-2026-11 do not roll up together, and GA4 will not tell you they were supposed to.
Why the Date Format Decides Whether Your Trend Line Works
Use YYYY-MM. Not month names, not MM-YYYY.
This sounds like a nitpick. It is the most consequential character-level decision in a recurring template, because campaign values sort alphabetically, not chronologically. GA4 has no idea your campaign name contains a date.
| Format | Example | How it sorts | Verdict |
|---|---|---|---|
YYYY-MM |
newsletter-2026-10 |
Chronological, for free | Use this |
| Month name | newsletter-oct-2026 |
apr, aug, dec, feb, jan, jul… | Unusable |
MM-YYYY |
newsletter-10-2026 |
All Octobers together, then all Novembers | Breaks across years |
| Sequence number | newsletter-012 |
Chronological, but nobody knows when 012 was | Workable, unfriendly |
Look at row two. Month names sort into apr, aug, dec, feb, jan, jul, jun, mar, may, nov, oct, sep. April first, September last. Every report you sort by campaign name puts your editions in an order that means nothing, and you re-sort by hand every single time.
YYYY-MM sorts correctly as plain text because the digits line up with chronology. It costs nothing to adopt and it fixes every report you build on that program.
For weekly programs, YYYY-Www works the same way: drop-2026-W07. Zero-pad the week number or week 7 sorts after week 40.

Where utm_id Earns Its Place in a Recurring Template
Add utm_id when a recurring program gets renamed mid-life, imports cost data, or needs to survive a rebrand.
utm_id is the campaign ID parameter. Google’s Campaign URL Builder exposes it alongside the five parameters most people know. In GA4 it maps to Session campaign ID, a dimension separate from Session campaign.
For recurring programs it does one specific job: it gives every edition a stable identifier that does not depend on the human-readable name staying put. When marketing renames “newsletter” to “the weekly brief” in month nine, utm_campaign splits into two program names and your trend line snaps. If utm_id carried a stable value like nl-2026-10 all along, you still have one continuous series to fall back on.
Skip it if your program names never change and you do not import cost data. It adds template complexity for a benefit you may never draw on. I add it to any program expected to outlive the person who named it.
The 14-Month Wall That Breaks Year-Over-Year Comparison
Annual programs hit a limit the others never touch, and it is worth getting the details exactly right, because half the advice on this is overstated.
GA4’s data retention setting controls how long event-level data stays available. Standard properties get two options: 2 months or 14 months. Analytics 360 adds 26, 38, and 50 months. Large and XL properties are capped at 2 months regardless of tier.
Here is the part most articles get wrong. Retention affects explorations and funnel reports only. Standard aggregated reports are not affected, because they are pre-aggregated when the data arrives.
So what actually happens when you compare Black Friday 2026 to Black Friday 2025:
- Traffic acquisition and other standard reports: fine. The campaign rows are still there.
- Any exploration: blocked past your retention window. On the 2-month default, blocked almost immediately.
- Anything needing a segment, funnel, or cohort across both years: blocked.
Two fixes. First, set retention to 14 months in Admin if you are on the 2-month default. Do it now rather than when you need it, because raising the limit only recovers data that has not already been deleted.
Second, for genuinely annual comparison, get the data somewhere without a ceiling. The GA4 BigQuery export has no retention wall at all, and rows stay until you delete them. Point Data Studio at BigQuery instead of at GA4 and the year-over-year view just works.
The cheap version, if BigQuery is overkill: export the campaign rows to a sheet the week after every annual program ends. Thirty seconds of work that makes next year’s comparison possible.
Honest limitation: neither fix is retroactive. If your annual program ran last November on a 2-month retention property and you never exported it, that comparison is not recoverable through any of this.
How to Build a Reusable Campaign URL Template in 6 Steps
- Name the pattern first. Dated edition, always-on, or seasonal return. Everything downstream depends on this answer, and it takes ten seconds.
- Lock source and medium permanently. These describe the channel, and the channel does not change because it is a new month. They should never appear in the variable column.
- Split
utm_campaigninto program and edition. Program name on the left, edition marker on the right, joined by a hyphen. Always-on programs stop after the program name. - Pick the edition format and write it down.
YYYY-MMfor monthly,YYYY-Wwwfor weekly,YYYYfor annual. Write the chosen format into the template file itself, not just into someone’s head. - Decide the
utm_contentvalues up front. Cap the list. Three or four named placements per edition is plenty, and an openutm_contentfield is where recurring programs quietly generate hundreds of values. - Save it as an enforced preset, not a document. A template in a doc gets copied wrong on run seven. Saved UTM templates fill the locked fields automatically and only leave the edition marker open. The full path from document to enforced preset is in switching from manual UTM tagging to presets.
Step six is the one teams skip, and it is the one that makes the other five durable. A written convention degrades with every person who reads it slightly differently. An enforced field does not.

When to Version a Recurring Template
Change a recurring template at an edition boundary, never mid-edition.
A recurring template is a measuring instrument. Changing it mid-program is like swapping rulers halfway through and pretending the numbers still line up. The data on each side is fine. The comparison across the change is not.
Three rules I hold to:
- Change at the boundary. Between the October and November sends, not during. Every link inside one edition should come from one template version.
- Never retroactively edit a published link’s parameters. You cannot. The parameters live in URLs that are already sitting in inboxes and ad platforms.
- Annotate the seam. Write down what changed, on what date, and which edition was the first to use the new version. Six months later, when a metric steps up or down for no visible reason, the annotation is the only thing that explains it.
For always-on programs there is no natural boundary, which is the awkward case. If you materially rewrite an evergreen flow, treat the rewrite as a version and append a version marker: welcome-flow-v2. It breaks the single-value cleanliness, and that is the correct trade. Two different experiences should not average together into one number that describes neither.
Honest limitation: versioning discipline is a people problem, not a tooling one. Enforced presets stop typos. They cannot stop someone deciding the template needed a rethink on a Tuesday.
Where Reusable Campaign URL Templates Break
Four failure modes, and the first is the one that gets everyone.
Cardinality accumulates. Cardinality is the number of unique values a dimension holds. A weekly program produces 52 campaign values a year on its own. Add five utm_content placements and you are at 260 combinations from one program. GA4 collapses high-cardinality dimensions into an (other) row past its daily limit, and the rows you lose are the long tail you were trying to measure. The mechanism and the defenses live in the UTM framework that scales and the high cardinality definition. For recurring programs specifically, the fix is capping utm_content, not capping editions.
Retired programs never get retired. Recurring programs end, and their templates sit in the library forever. Someone uses the dead one eighteen months later. Prune the template list quarterly.
The template outlives the reporting need. Plenty of teams keep tagging an edition marker nobody has looked at in a year. If no one has segmented by edition in two quarters, that granularity is costing you cardinality for nothing.
Parameter-stripping channels ignore all of it. Some platforms and privacy tools strip query parameters. A perfect template does not survive a channel that discards the parameters, and no amount of naming discipline changes that.

Frequently Asked Questions
Should every edition of a recurring campaign have its own utm_campaign value?
Yes, with a shared program prefix. Give October’s newsletter newsletter-2026-10 and November’s newsletter-2026-11. The shared newsletter- prefix lets you filter the campaign dimension by “contains” to get the program total whenever you want it. The reverse does not work: if every edition shares one value, the per-edition numbers were never recorded and cannot be recovered.
What date format should I use in a recurring campaign URL template?
Use YYYY-MM, such as newsletter-2026-10. Campaign values sort alphabetically, so month names sort as apr, aug, dec, feb, jan, and your editions land in meaningless order in every report. Numeric year-first dates happen to sort chronologically as plain text. For weekly programs use YYYY-Www and zero-pad the week number.
How do I compare a campaign year over year in GA4?
Standard reports like Traffic acquisition are pre-aggregated and are not limited by data retention, so a year-over-year campaign comparison works there. Explorations and funnel reports are limited: standard properties retain event-level data for 2 or 14 months, and the default is often 2. Set retention to 14 months in Admin, and use the BigQuery export for anything genuinely annual.
How should I tag an always-on campaign that never ends?
Leave the date out entirely. Use a stable value like utm_campaign=welcome-flow with no edition marker, and get your time dimension from the report’s date range instead. Dating an evergreen flow freezes its launch month into every report for the life of the program, which makes an active campaign look stale.
Can I change a campaign URL template while the campaign is running?
You can change the template, but not the links already published. Parameters live inside URLs that are already in inboxes and ad platforms, so they cannot be edited after the fact. Change templates at an edition boundary so every link within one edition matches, then annotate what changed and which edition was first to use it.
How many campaign URL templates does a recurring program need?
One per program, not one per edition. That is the whole point: the template is the thing that stays constant while editions move through it. A team running six recurring programs needs six templates. If you find yourself building a new template for each run, the edition marker is not doing its job.
Start With the Pattern, Not the Parameters
Most tagging advice starts at the parameter level. For recurring programs that is the wrong end. The parameters are easy once you have answered the question underneath them: is this a dated edition, an always-on program, or a seasonal return?
Answer that, and the rest falls out. Dated editions get a program prefix and a sortable date. Always-on programs get no date at all. Seasonal returns get both, plus a plan for reaching data older than your retention window.
Three things worth doing this week:
- Pick your most-repeated program and check whether its last six runs share a campaign prefix.
- If they use month names, switch to
YYYY-MMat the next edition boundary and annotate the switch. - Save the result as an enforced preset so run seven does not depend on anyone’s memory.
Campaign URL templates are not really about saving time on link creation. That is a nice side effect. They exist so that the twelfth run of a program can be compared to the first one, which is the only way you ever find out whether the program is working.
Ready to stop rebuilding the same campaign links every month? Save your recurring programs as UTM templates in linkutm and leave only the edition marker open.