UTM Builder

Tag a link with UTM parameters so the visit shows up in GA4 as "newsletter / email" instead of landing in the Direct row. The builder keeps existing query strings and #fragments, and lowercases values so Facebook and facebook do not become two sources. Bulk mode tags a whole list of links with the same parameters.

Campaign

For links from other sites, ads and emails only. A UTM on a link between your own pages starts a new session with the wrong source and wipes out the real attribution.

Campaign URL

1 UTM Builder (Campaign URL Builder) example

The UTM parameters go after the existing color=red and before the fragment. Keep UTM tags in front of the #. The browser never sends the fragment to the server, and GA4 does not read tags placed there.

Fields

URL          https://example.com/shoes?color=red#reviews
utm_source   Newsletter
utm_medium   email
utm_campaign Spring Sale 2026
utm_content  hero button

Campaign URL

https://example.com/shoes?color=red&utm_source=newsletter&utm_medium=email&utm_campaign=spring-sale-2026&utm_content=hero-button#reviews

2 What each parameter is for

utm_source and utm_medium

Who sent the visit and what kind of link it was. GA4 sorts traffic into channels (Paid Search, Email, Organic Social) from these two, so the spelling matters. For example, email as a medium lands in Email, and newsletter as a medium does not.

utm_campaign and utm_id

The campaign name people read in reports, and an optional ID for joining with cost data you import. Pick a naming scheme once and keep it, because GA4 treats spring-sale and Spring_Sale as two campaigns.

utm_term

The paid keyword. Google Ads supplies keyword data through auto-tagging (gclid), so set utm_term for other ad platforms.

utm_content

Separates two links in the same campaign, such as the header button and the footer link in one email, or ad variant A and B.

3 Two ways UTM tracking breaks

Never tag internal links. Say a homepage banner points at your own sale page with UTM tags. Every click on it replaces the visitor's traffic source with that banner, so the ad or email that brought the visitor in loses the credit for what happens next. For on-site clicks, use an event instead.

Test the link through every redirect. A shortener or a server redirect that drops the query string removes the tags without any error, and the visit lands in Direct. Click the finished link once and check the address bar before you send it to 40,000 subscribers.

How to use the UTM Builder (Campaign URL Builder) tool

  1. Paste the landing page into Website URL. The builder keeps existing query parameters and the #fragment, and adds https:// if it is missing.
  2. Pick a pair from Preset to fill utm_source and utm_medium, or type your own, then add utm_campaign and, if needed, utm_id, utm_term and utm_content.
  3. Leave Lowercase on and choose how Spaces are written: dash, underscore, %20 or plus.
  4. For many links at once, switch Mode to Bulk and paste one URL per line. Every line gets the same tags and the output keeps the line order.
  5. Read the notes under the result for missing fields or a medium GA4 will not recognize, then click Copy URL.

How to add UTM parameters to a URL in code

JavaScript

function addUtm(link, tags) {
    const url = new URL(link);
    for (const [key, value] of Object.entries(tags)) {
        if (value) url.searchParams.set("utm_" + key, value.trim().toLowerCase());
    }
    return url.toString(); // the #fragment stays at the end
}

console.log(addUtm("https://example.com/shoes?color=red#reviews", {
    source: "newsletter",
    medium: "email",
    campaign: "spring sale 2026",
}));
// https://example.com/shoes?color=red&utm_source=newsletter
//   &utm_medium=email&utm_campaign=spring+sale+2026#reviews

searchParams rewrites the whole query in form encoding, so a space becomes + and an existing %20 in another parameter becomes + as well. Both decode to a space. If you need the original bytes untouched, append to the string instead.

PHP

<?php
function add_utm(string $link, array $tags): string {
    $parts = parse_url($link);
    parse_str($parts['query'] ?? '', $query);
    foreach ($tags as $key => $value) {
        $query['utm_' . $key] = strtolower(trim($value));
    }
    return $parts['scheme'] . '://' . $parts['host']
        . (isset($parts['port']) ? ':' . $parts['port'] : '')
        . ($parts['path'] ?? '/')
        . '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986)
        . (isset($parts['fragment']) ? '#' . $parts['fragment'] : '');
}

echo add_utm('https://example.com/shoes?color=red#reviews', [
    'source' => 'newsletter', 'medium' => 'email', 'campaign' => 'spring sale',
]);

parse_str turns dots and spaces in parameter names into underscores, so a query like a.b=1 comes back as a_b=1. This is rare, but PHP makes the change without a warning.

Python

from urllib.parse import urlsplit, urlunsplit, parse_qsl, urlencode, quote

def add_utm(link, **tags):
    parts = urlsplit(link)
    query = [(k, v) for k, v in parse_qsl(parts.query, keep_blank_values=True)
             if not k.startswith("utm_")]
    query += [("utm_" + k, v.strip().lower()) for k, v in tags.items() if v]
    return urlunsplit(parts._replace(query=urlencode(query, quote_via=quote)))

print(add_utm("https://example.com/shoes?color=red#reviews",
              source="newsletter", medium="email", campaign="spring sale"))
# https://example.com/shoes?color=red&utm_source=newsletter&utm_medium=email&utm_campaign=spring%20sale#reviews

Common errors and how to fix them

Campaign traffic shows up as Direct or (not set)
Something between the click and the page dropped the query string: a redirect that rebuilds the URL, a link shortener, or an app that strips parameters. Open the tagged link and check the address bar after it lands.
The same campaign appears as several rows in GA4
The links use inconsistent spelling or case, such as spring-sale, Spring_Sale and spring sale, and GA4 does not merge them. Keep Lowercase on and pick one space style for the whole team.
Sessions from your own site are credited to a banner
An internal link carries a UTM tag, which overwrites the visitor's real source. Remove it and track the click with an event instead.
Traffic lands in the Unassigned channel
The utm_medium does not match any GA4 default channel rule. Use values like cpc, paid_social, social, email, display, affiliate or referral, or build a custom channel group for your own names.

Good to know

UTM parameters come from Urchin, the analytics company Google bought in 2005, which is where the U comes from. They are ordinary query parameters: the server ignores them and the analytics script reads them from the landing page URL. This is why they have to survive every redirect on the way in. They also appear in any URL a visitor copies and shares, so keep anything private out of them.

Google Ads adds a gclid parameter automatically when auto-tagging is on, and GA4 prefers it to manual UTMs for Google Ads traffic. Tag Google Ads links by hand only if auto-tagging is off or you also send the traffic to a tool that does not read gclid.

Specifications and references

This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.

Frequently Asked Questions

Which UTM parameters are required?

Always set utm_source, which identifies who sent the visit. Google also recommends utm_medium and utm_campaign, plus utm_id if you import cost data. Any that are missing show up as (not set) in GA4 reports.

Should UTM values be lowercase?

Yes. GA4 is case-sensitive, so Facebook, facebook and FACEBOOK become three separate sources. The Lowercase option is on by default for that reason.

Can I use UTM parameters on internal links?

No. A UTM on a link between your own pages replaces the visitor's traffic source with that link, so the ad, email or search that brought the visitor in loses the credit. Track on-site clicks with events instead.

What utm_medium should I use for paid social?

Use a value GA4's default channel rules recognize, such as paid_social, or cpc with a social source like facebook or linkedin. A medium like fb-ads matches no rule, and the traffic may be filed as Unassigned.

Do UTM parameters work after the # in a URL?

No. The browser never sends the fragment to the server, and GA4 reads the query string, so the tags must come before the #. The builder inserts them before any fragment and keeps your existing query parameters.

Related

To see what a tagged link already carries, paste it into the query string parser or the URL parser. For encoding a single value by hand, use the URL encoder.

More URL Tools

Browse all url tools

Copied to clipboard!