lamapixel
0%
E-commerce · 9 min read

Why your GA4 revenue does not match: data layer, consent and duplicates

A missing data layer, value without currency, duplicate purchase events, consent and thresholding. Six causes of the gap between GA4 and accounting, and how to tell them apart.

Why your GA4 revenue does not match: data layer, consent and duplicates

The gap between revenue in Google Analytics and revenue in your accounts is a normal state, not a fault. What is a fault is its size. A few per cent is measurement error; thirty per cent or more means something is not being measured at all, is being measured twice, or is thrown away on the way.

There are six causes and they can be told apart in twenty minutes without touching any code. The procedure is at the bottom. First, though, the most unpleasant possibility, because it is also the most common.

First find out whether measurement ever happened at all

A Google Tag Manager container export describes intent, not behaviour. It does not and cannot contain a single fact about a tag having fired. A container can be wired up impeccably and stay silent for a year.

On the last day of August 2026 we went through two containers from live accounts, and both had a fault of exactly this kind.

The first container was our own. Eleven tags, ten GA4 events, the base tag on the right trigger, the form split into form_start and form_submission_success, and the lead qualification sent inside the event. Better wiring than the average account that comes to us for an audit. And yet: conversion value is not sent in any of the ten events, there are zero Google Ads tags in the container, and consent is set to "not set" on all eleven tags. The form asks about budget and the answer travels all the way into GA4, where it stops.

The second container belonged to a client and its fault was quieter still. The conversion tag was configured correctly, the goal and the condition made sense. Except that on the site the dataLayer.push call does not appear even once on any of the seven pages we checked. The container is waiting for data nobody sends, and the interface shows green.

The check that settles this takes two minutes: GA4, Reports > Engagement > Events, three months back, and look at the counts for purchase, add_to_cart and begin_checkout. Zero means every other consideration is pointless.

A data layer has six steps, not one

Most shops measure purchase and nothing before it. That is like having a till without a shop floor: you know how much you sold and not where people left.

The recommended event sequence for e-commerce is longer: view_item_list, view_item, add_to_cart, view_cart, begin_checkout, add_shipping_info, add_payment_info, purchase (Google Analytics developer documentation, measuring e-commerce).

The practical minimum is four: view_item, add_to_cart, begin_checkout, purchase. Only from those do you get a ratio between steps, which is the one number you can actually work with. The purchase count on its own tells you how the month went but not what to do about it.

A value without a currency is not a value

Two things that look like formalities and are not:

Currency is sent with the value. The documentation says so directly: the currency parameter is set at event level when you send value. Without a currency, value is a number with no unit, and the revenue report stops making sense the moment you sell your first order abroad.

transaction_id is required on purchases and refunds. Without it, the deduplication covered in the next section does not work.

And one trap from practice you cannot see in the interface: "value is not sent" and "zero is sent" look identical in reports. In the second container above the conversion value column was zero on every row, and only looking inside the container showed the value parameter was not there at all. The diagnosis is made in the container, not in the report.

Duplicate purchases: GA4 can filter them out, but on two conditions

The classic scenario: a customer refreshes the thank-you page, or returns to it from browser history, and the purchase is counted again.

Google Analytics deduplicates purchase events with the same transaction ID. The two conditions this rests on tend to be overlooked (Analytics help):

  1. Deduplication only works on web data streams, not on app streams.
  2. An empty transaction_id is worse than none. Google Analytics deduplicates all purchase events with transaction_id="", so a thousand orders collapse into one and the numbers fall far enough to look like a sales outage.

The second point is a silent fault in its purest form. It happens whenever the thank-you page template cannot reach the order number and sends an empty string instead of not sending the event at all.

What to do about it: take the transaction ID from the order number, not from a random number generated on the page. Send it once when the order is created, not on every page view. And if the order number is not available, do not send the event.

Consent mode comes in two forms and the difference between them is financial.

Basic mode: tags do not fire at all before the banner is answered and nothing is sent to Google, not even the consent state. Filling in the missing conversions then relies on a general model.

Advanced mode: tags load immediately in a "denied" state and send cookieless pings until the user decides. Those produce a model specific to the individual advertiser, which is more accurate (Google Ads help).

And now the number that changes the whole calculation for an ordinary Czech shop. For conversion modelling to start at all you need 700 ad clicks over seven days, separately for each country and domain-group combination (Google Ads help).

Anyone in Czechia spending tens of thousands a month on performance advertising sits below that line. In practice that means: on a small account, lost conversions do not get modelled back and the hole left by refused consent stays a hole. That is not an argument against consent - consent is a legal obligation. It is an argument against expecting Google to even it out for you.

Thresholds: GA4 hides some of your data and does not say so out loud

A little-known thing that explains a lot of "missing" rows in reports.

Google Analytics applies thresholds so that demographic and interest signals cannot be used to infer an individual's identity. Data can be withheld when you have Google signals enabled and a low number of users in the period. The thresholds are systemic and cannot be configured. Widen the period, the user count rises and the previously withheld data appears (Analytics help).

From this comes a habit that saves a lot of pointless searching: before you look for a measurement bug, widen the date range. If the rows appear, there was no bug.

GA4 and Google Ads will never agree, and it is documented

The difference between the conversion count in Google Ads and in Analytics is not a configuration error. Google itself lists a number of reasons (Google Ads help), and four of them explain most of what you see in practice:

  • Google Ads reports a conversion on the day the ad was shown, other tools on the day of the conversion. When somebody clicks on Monday and buys on Thursday, both numbers are right and still differ.
  • A conversion can be attributed up to 90 days after the click, so yesterday's figure is not final and will be higher in three weeks.
  • Invalid traffic is filtered out. Google removes some conversions when it judges the source to be spam.
  • Data processing takes 24 to 48 hours. Comparing sooner is pointless.

On top of that come the conversion counting setting (every versus one), cross-device conversions and view-through conversions.

The practical conclusion worth remembering: campaigns are optimised on conversions in Google Ads. Analytics is there to show behaviour and the path, not to argue with Google Ads about a number.

Three traps that look like details. All three from our own account

The last section is about us, not about clients. We found these on 31 August 2026 in our own container, and it is more instructive than somebody else's examples.

An event called portfolio_view_75 that measures 50 per cent. The trigger has a minimum visibility of 50 %, the name promises 75 %. In six months somebody will cite that number as "they read three quarters of the portfolio". Renaming the event is cheaper than changing the threshold.

The engaged_30s event fires on every page of the site. The timer's condition matches any address, so after thirty seconds the event also fires from a blog article. Harmless in itself. It becomes dangerous at exactly one moment: if such an event is marked as a key event in GA4 and imported into Google Ads, bidding starts optimising for readers who will never enquire about anything.

A click-ID cookie nobody reads. A custom tag stores gclid for 90 days, and no other tag, variable or hidden form field ever reads that cookie. It looks like working offline measurement and is an empty space. Either the value goes into the form, or the tag goes - the middle state is the worst one.

The rule that follows from those three examples: only events that genuinely mean business belong among GA4 key events. Engagement, scroll depth and section views never do.

A twenty-minute check that tells you which of the six causes you have

Step by step. None of it changes anything or breaks anything.

  1. GA4, Reports > Engagement > Events, three months back. Are purchase, add_to_cart and begin_checkout there with non-zero counts?
  2. In the same place, the purchase count against the order count in your admin for the same period. A difference over twenty per cent sends you to step 3.
  3. Is the purchase count higher than the order count? Duplicates - see the transaction_id section. Lower? Missing measurement or consent.
  4. Browser, developer tools, Console tab, complete a test order and print dataLayer. Is there a purchase event with value, currency and a non-empty transaction_id?
  5. GA4, Admin > Key events. Are only the ones that mean business in there?
  6. GA4, widen the range to a year and check whether previously missing rows appeared. If they did, it was thresholding.
  7. Google Ads, Goals > Conversions > Summary, the conversion value column. Zero alongside live conversions means the value is not being sent.

After those seven steps you know whether you are dealing with a missing data layer, duplicates, consent, thresholds, or the ordinary difference between tools.

Accounting and Analytics will never agree. The goal is not agreement; the goal is to know the size of the gap and its cause - because you can manage that, and you cannot manage matching numbers.

You want your container reviewed

Send us your Google Tag Manager container export and the shop address. We will send back a list of what is measured, what is measured wrongly, and what only looks measured. We will say up front that a container export cannot prove whether a tag ever fired - that can only be verified in GA4, and it is the first thing we will ask you for.

Measurement is part of every shop we build; what it covers is described on the e-shop development page. A technical review of an existing site including measurement is what we do on the site audit and speed page; the scenarios that push data onwards into your systems are on the automation page.

Write to info@lamapixel.com or call +420 775 599 009.

Need a hand?

Write to us and we'll figure it out together.

Book a consultation →