Skip to main content

Why your GA4 data isn't matching

Once you've connected GA4, you'll see GA4 metrics under the metrics selector

If you've connected GA4 and metrics don't show any value, the data is almost always arriving fine. The issue is that we can't tie a GA4 session back to a specific ad.

This article explains how the matching works, why it breaks, and how to fix it.

How we match GA4 data to your ads

We read the UTM parameters on the landing page URL and try two rules, in order.

Rule 1: ad ID.

If we find the ad ID anywhere in the standard UTM params, we match on that and stop. This is the most reliable one, since ad IDs don't change.

These are the parameters we read:

  • utm_content

  • utm_term

  • utm_campaign

  • utm_source

  • utm_medium

The ad ID doesn't even need its own parameter. If it's mixed in with other values inside one of those five, we'll still find it. What won't work is a custom parameter like creative=123456, because we don't read parameters outside that list.

Rule 2: names.

If there's no ad ID, we fall back to matching the ad name, the ad set (or ad group) name and the campaign name against what we hold in your ad account. All three have to match. Two out of three gets no credit.

Why name matching many times fails

The campaign was renamed after the ads went live

This is a common one. UTM parameters are baked into the ad when it's published, so renaming a campaign in your ad platform doesn't update them. GA4 keeps sending the old name while your ad account holds the new one. Rule 2 needs all three names, so the events get dropped.

You'll often see the same ad split across both states: the traffic from before the rename fails to match, and anything published after it matches fine.

The UTMs were typed in by hand and differ from names in ad platform

If campaign and ad set names are written manually into the URL rather than pulled from the platform's dynamic variables, small differences almost certainately creep in.

A space instead of an underscore, a pipe instead of a hyphen, a truncated name.

Rule 2 needs exact matches, so near misses don't count.

You're not using ad name, ad set name AND campaign name

In many cases some users pass some names, but not all, as part of any of the standard utm params.

The quickest fix: pass the ad ID

Every major platform has a dynamic variable that resolves to the ad ID at click time. Add it to your tracking setup and the matching stops depending on names entirely, which means renames no longer break anything.

Platform

Variable for ad ID

Meta

{{ad.id}}

Google Ads

{creative}

TikTok

__CID__

LinkedIn

{{CREATIVE_ID}} or {{AD_ID}}

You can pass it on any standard UTM param.

Drop it into utm_content if that parameter is free. For example:

utm_content={{ad.id}}

Where to set dynamic variables on ad platforms

Ad platforms update their UI very frequently, so this may change, but currently:

Meta. The URL parameters field at ad level, under URL parameters

below the website URL, on the tracking section

Google Ads. Google gives you multiple options. You can find the tracking template under Admin, then Account settings, then Tracking. Setting it at account level applies it everywhere. Note that {creative} is a ValueTrack parameter, so it belongs in the tracking template or final URL suffix rather than typed into the final URL.

TikTok. The URL parameter field on the ad. TikTok has no parameter inheritance, so every ad needs its own configuration unless you use URL auto-attach at account level.

LinkedIn. Tracking parameters at campaign or ad account level, not per ad.

Also worth doing: use dynamic variables for names

Even with the ad ID in place, using dynamic variables for your campaign and ad names keeps everything else consistent with your ad account. Here's the pattern we use on Meta:

utm_source=facebook&utm_medium=cpc&utm_campaign={{campaign.name}}_{{adset.name}}&utm_content={{ad.name}}

If you need utm_content for the ad name, you could put the ad ID in utm_term instead.

Platform quirks to know about

TikTok macro names are misleading. __CID__ is the ad ID and __AID__ is the ad set ID, which is the opposite of what the letters suggest. Macros use two underscores on each side. A single underscore fails silently and passes the raw text into your URL.

Google Ads changes take time. Tracking template updates take roughly 24 to 48 hours to appear in serving ads.

Performance Max has no ad ID. {creative} won't resolve for PMax campaigns. The closest equivalent is {assetgroupid}, which sits at asset group level rather than ad level.

LinkedIn renamed its hierarchy. As of October 2025, Campaign Group became Campaign and Campaign became Ad Set. Depending on your account, the macro picker may show {{CREATIVE_ID}} or {{AD_ID}}. Check what's actually offered in the picker before you build the string. LinkedIn dynamic parameters also don't work on Conversation Ads or Message Ads.

After you make the change

New traffic will start matching once the updated URLs are live and the platform has picked them up.

Historical data won't backfill. Those clicks already happened with the old UTM values attached, and there's no way to rewrite them after the fact.

Checking it worked

  1. Preview or click one of your ads.

  2. Look at the landing page URL in your browser. The variables should have resolved into real values, not raw text like {creative} or __CID__.

  3. Confirm the ad ID is sitting in one of the five parameters we read.

  4. Give it a day, then check the GA4 columns in your report.

If it's still empty after that, send us the resolved URL from step 2 and we'll take a look.

Did this answer your question?