In short
- Google Ads and GA4 are joined on a campaign name, and that only works if the names agree.
- Linked accounts with auto-tagging give GA4 the Google Ads name; unlinked accounts give "(not set)"; hand tags give whatever was typed, capitals included.
- A campaign with spend and no revenue may be a name that did not match, not a failure.
- In our example, one mistyped tag turned a €3.60 return into €2.00 and hid the best campaign.
- Keep unmatched rows, never read blanks as zero, and leave revenue with no spend out of the ads' return.
You want to know what each Google Ads campaign brought in. Google Ads knows what each one cost; GA4 knows what each one earned. So you export both, put them side by side in a spreadsheet, and look the campaigns up by name. Half the rows come back empty.
A campaign with spend and no revenue looks like money thrown away. A row in GA4 with revenue and no spend looks like a gift. Usually neither is true: the two names simply did not line up. The join is on a name, and the name in GA4 comes from one of two places, only one of which you can trust to match.
An example
An invented shop, one month. Its Google Ads account is not linked to its GA4 property, so its ads were tagged by hand with UTM parameters. Spend comes from Google Ads; revenue from GA4's Session campaign.
| Name in Google Ads | Spend | Name in GA4 | GA4 revenue |
|---|---|---|---|
| Brand - Exact | €400 | Brand - Exact | €3,200 |
| Spring Sale | €1,200 | spring_sale | €4,800 |
| Generic - Shoes | €1,400 | Generic - Shoes | €2,800 |
| — | — | autumn-newsletter | €900 |
Look the campaigns up by exact name and three things go wrong at once:
- Spring Sale finds nothing. Somebody typed
spring_salein the tag. If the blank is read as zero, the shop's best campaign (€4.00 back per €1) shows €0 back, and is the obvious one to pause. - The total comes out too low. The revenue that did match is €6,000 against €3,000 of spend: €2.00 back per €1. The truth, with Spring Sale matched, is €10,800 against €3,000: €3.60.
- Or too high, if you take every GA4 row. Adding all of GA4's campaign revenue gives €11,700, or €3.90 per €1, because it credits the ads with the newsletter's €900.
Why it happens
The dimension to join on is GA4's Session campaign, which Google describes as "the marketing campaign name for a session", including Google Ads campaigns and manually tagged ones (GA4 dimensions). Where the name comes from decides whether it matches.
- Linked accounts with auto-tagging. Auto-tagging adds a click identifier (
gclid) to the ad's link, and Google's documentation says it "automatically imports Google Ads data into Analytics". The campaign name GA4 shows is then the one in Google Ads, so the two agree. - Not linked. Google says that new data from clicks on an unlinked Google Ads account appears in GA4's Google Ads dimensions, such as campaign name, as (not set) (Link Google Ads to Analytics). The visits arrive; the campaign name does not.
- Tagged by hand. The name is whatever was typed in
utm_campaign. GA4 does not correct it, and Google notes that "parameter values are case sensitive" (URL builders), soSpring Sale,spring saleandspring_saleare three different campaigns to GA4. Google also says that missing UTM parameters "will result in (not set) values" in reports.
Rows on one side only are therefore of two kinds. Spend with no revenue is either a campaign that really earned nothing, or one whose name did not line up; you cannot tell which until you look. Revenue with no spend is usually not an ad at all: an email, an affiliate, a link somebody tagged by hand.
Where it bites
- Spreadsheets and Looker Studio blends that join Ads cost to GA4 revenue on the campaign name. A lookup that finds nothing returns an error or a blank, and a blank in a sum is a zero.
- Campaigns renamed in one place only. Hand-tagged links keep the old name until somebody edits them.
- A blended return per €1 worked out from whichever rows happened to match, or from every GA4 campaign row. In the example that is €2.00 or €3.90 for a business making €3.60.
How to get the right number
- Link Google Ads to the GA4 property and turn auto-tagging on. In GA4: Admin > Product links > Google Ads links; the linking screen has an "Enable Auto-Tagging" option. You need the Editor role or above on the property. Google says Ads data appears in Analytics reports within 48 hours of linking.
- Check the names GA4 actually has. Open Reports > Acquisition > Traffic acquisition, change the table's dimension to Session campaign, and compare the list with your Google Ads campaigns.
- Where you tag by hand (other ad networks, email), copy the campaign name exactly, capitals and spaces included, and keep a single list of names everyone uses.
- Match on the name trimmed and ignoring capitals, and no looser.
Generic - Shoesandgeneric - shoesare the same campaign typed twice.spring_saleandSpring Saleprobably are too, but deciding that is a human's job: an automatic guess will one day put real money on the wrong row. - Keep the rows that do not match, and never read a blank as zero. Before calling a spend-only campaign a waste, search GA4's campaign list for a name like it.
- Work out the overall return from the ad campaigns only: the revenue matched to campaigns you paid for, divided by everything you spent. Leave revenue-only rows out of it.
How Glancely handles it
Glancely's Revenue page puts Google Ads spend and GA4 revenue on one row per campaign, joined on GA4's Session campaign. Names are matched trimmed and ignoring capitals, and no looser. Rows found on one side only are kept: their missing figures show as a dash and their return is left blank, never zero, and a sentence under the table counts them, adding that unlinked accounts are the usual reason the names do not line up. GA4 visits with no campaign at all, such as "(not set)", are left out of the table.
Kosmas Botsis · Founder of Glancely
Kosmas Botsis is the founder and lead data engineer of KB Analytics, an analytics engineering practice in Athens working with clients across Europe and the US: more than 250 projects for over 194 clients, and in Toptal's top 3% of talent. Glancely is what came out of that work.
About Kosmas at KB AnalyticsMore from the blog
- Why your return on ad spend counts sales your ads didn't make
Divide all of your revenue by your ad spend and the ads take credit for every organic, direct and email sale. Here is how to work out the return on the channels you actually pay for, and why it can be five times lower.
- Where Performance Max shows up in GA4
GA4 files Performance Max visits under a channel called Cross-network, not Paid Search or Paid Shopping. Put its spend anywhere else and every channel's return is wrong; here is where it belongs and how to read it.
- Why averaging daily rates gives you the wrong number
Average a week of daily click-through rates and you get a number for the week that is almost always wrong. Rates can't be averaged; they have to be worked out again from the totals. Here is why, and how.