← Guides·Data Integrity

Data Integrity

Reconcile GA4 with your backend: why revenue doesn’t match

Why GA4 transactions and revenue never equal your orders database, which gaps are expected, which are defects, and a step-by-step way to reconcile them.

David PombarSwiss army knife at Trackingplan
9 min read · 2019 words

GA4 will not match your orders database, and it is not meant to. The backend records every order it accepts. GA4 records the purchase events a browser managed to send and Google agreed to keep. Reconciling the two is not about making them equal: it is about explaining the gap, knowing its normal size, and noticing the day it moves. That is the practical side of data integrity: a number you can defend because you know exactly why it differs from the source.

This guide separates the gaps you should expect from the ones that are defects, shows how to set your own baseline instead of trusting a rule of thumb, and walks through a reconciliation by order ID that you can repeat every month.

Why GA4 and the backend count different things

The backend counts orders. GA4 counts purchase events that survived the trip. Between the order and the report there are several places where an event can be lost, doubled, or changed.

A funnel from orders in the backend to purchases in GA4 reports, with losses at the confirmation page, consent, blockers, collection, and processing.
Every step between the order and the GA4 report can lose an event. Some losses are expected, some are defects. Illustrative.
BackendGA4
UnitAn order accepted by the shopA purchase event received by Google
SourceYour database, server-sideA tag in the browser, or a server-side hit
Depends on consentNoYes
RevenueWhatever your finance team definesThe value the tag sends, in the currency it sends
DayYour server’s time zone, often UTCThe property’s reporting time zone
Refunds and cancellationsUsually applied to the orderOnly if you send a refund event

Every reconciliation starts by making those rows as equal as you can. The gap that remains after that is the one worth explaining.

The gaps you should expect

These differences exist on a correctly implemented site. They make GA4 lower than the backend, never higher.

With consent mode, a visitor who declines analytics storage is measured differently. In the basic implementation, Google tags do not load until the visitor responds, and if they decline, no data is sent to Google. In the advanced implementation, tags send cookieless pings while consent is denied, and Google uses them for modeling. Either way, a share of your orders never becomes an observed purchase in GA4, and that share follows your consent rate by country.

Blockers and browser protections

Ad blockers, privacy extensions, and some browsers stop requests to analytics domains. The order still happens. The purchase event never leaves the browser. The share depends on your audience, which is why it varies by device and browser more than by campaign.

Orders that never reach the confirmation page

A purchase tag that fires on the thank-you page misses every order that does not land there: a buyer who closes the tab during a payment redirect, a bank authentication step that times out on the way back, phone orders, orders created by the support team, subscription renewals, and marketplace orders. None of them are tracking failures. They are orders outside the web checkout, and they must be removed from the backend side before you compare.

Processing and late events

GA4 is not final the moment the day ends. Google’s data freshness page says processing can take 24 to 48 hours and that report data can change during that time. It also ignores events that arrive too late, more than a couple of days after they were triggered. Compare closed, processed days only.

Thresholds

GA4 can withhold rows from a report to protect user privacy. Google’s data thresholds page explains when that happens, for example in reports that include demographics, and recommends widening the date range. For reconciliation, read transactions from the BigQuery export or a report without demographic dimensions.

The gaps that are defects

These are differences that a working implementation should not have. Some make GA4 lower, some make it higher, and some leave the count right but the revenue wrong.

A two-column chart: expected gaps such as consent, blockers, and processing on the left, and defects such as duplicates, missing IDs, and wrong currency on the right.
Expected gaps only push GA4 down. Defects can push it down, push it up, or keep the count and break the value.

How much difference is normal

You will find percentages quoted as the normal GA4 gap. Do not adopt one. The gap depends on your consent rate, your visitors’ browsers and blockers, how much of your revenue comes from outside the web checkout, and how your payment flow returns buyers to the site. Two shops with perfect tracking can sit far apart.

Measure your own baseline instead. Define a capture rate:

capture rate = GA4 transactions ÷ web orders in the backend, for the same day

Compute it for every closed day over four to eight weeks, and split it by device, browser, country, and payment method. The range it moves in is your normal. A drop that stays inside the band is noise. A step outside it, especially one that starts on a specific day, is a defect until proven otherwise.

A line chart of daily capture rate inside a shaded baseline band, with a sudden step down after a release.
The baseline is a band, not a target. What matters is the day the line leaves it. Illustrative data.

Segments tell you more than the total. A capture rate that is stable on Android but falls on iOS after an app or browser update, or one that falls only for orders paid through a redirect-based method, points straight at the cause.

A step-by-step reconciliation

Run this once in depth, then repeat a lighter version every month.

1. Make both sides count the same orders

On the backend side, keep web orders only. Remove phone, support, marketplace, and renewal orders, plus test and internal orders. On the GA4 side, use the property and data stream that receive the production checkout, and nothing else.

2. Agree on revenue

Pick one definition, for example product revenue without tax and shipping, before refunds, and compute it on both sides. If the tag sends something else, recalculate the backend to match the tag for this exercise, then decide separately which one the business should see.

3. Align time

Choose one time zone and convert the other side to it. In the BigQuery export schema, event_timestamp is in UTC, while event_date follows the stream’s registered time zone, so join on the timestamp when your backend stores UTC. Compare only days that GA4 has finished processing.

4. Join by order ID

Export the transaction_id of every purchase from BigQuery or an exploration, and the order IDs from the backend. Join them, and you get three groups.

Three groups after joining by order ID: orders only in the backend, orders in both with matching or different values, and IDs only in GA4.
Each group after the join points to a different family of causes.

A query like this one finds the GA4 side, including duplicates:

SELECT
  ecommerce.transaction_id,
  COUNT(*) AS purchase_events,
  SUM(ecommerce.purchase_revenue) AS revenue
FROM `project.analytics_123456.events_*`
WHERE event_name = 'purchase'
  AND _TABLE_SUFFIX BETWEEN '20261001' AND '20261031'
GROUP BY 1

5. Read each group

6. Turn the result into a monitored number

Write down the baseline capture rate per segment, the revenue definition you agreed on, and the exclusions you applied. Next month, rerun the same query. The reconciliation is only useful if the next break is caught by the number, not by finance.

Where Trackingplan fits

Trackingplan, your digital analytics agent, does not make GA4 equal your backend, and it should not. It watches the part that breaks between the two: the purchase hit itself. It reads every hit your real users send to GA4, the ad platforms, and your server-side endpoints, learns a baseline per hour of day and day of week, and flags what moves outside it: missing properties, duplicate fires, broken triggers.

For revenue, the checks are the ones this guide runs by hand: the same purchase counted once, with the same value, across every platform, and wrong values, inconsistent tax or shipping, or the wrong currency flagged when they appear. When the agent finds one, it traces it from the dataLayer to the destination, with the tag, the trigger, or the variable that caused it. Which number finance reports is your call. Knowing the day the gap changed is the part monitoring takes off your plate.

Frequently asked questions

Why doesn’t GA4 revenue match my backend?

Because they count different things. The backend records every order it accepts. GA4 records the purchase events a browser managed to send, that consent allowed, that no blocker stopped, and that Google kept after deduplication and processing. On top of that, the two often disagree on what revenue means: tax, shipping, discounts, refunds, currency, and the time zone that decides which day an order belongs to.

What percentage of difference between GA4 and the backend is normal?

There is no single number that holds for every site, because the gap depends on your consent rate, your audience’s browsers and blockers, and your checkout flow. Measure your own baseline instead: divide GA4 transactions by web orders in the backend, day by day, for four to eight weeks, and per device, browser, country, and payment method. The band you see is your normal; a move outside it is the thing to investigate.

How do I reconcile GA4 transactions with orders?

Limit both sides to the same orders and the same definition of revenue, put them in the same time zone, wait until GA4 has finished processing, and join them by order ID, using transaction_id from the BigQuery export or an exploration. You get three groups: orders in both, orders only in the backend, and IDs only in GA4. Each group points to a different kind of cause.

Should GA4 ever report more transactions than the backend?

No. GA4 can legitimately miss orders, but it should not invent them. More purchases in GA4 than orders in the backend means duplicated purchase events, test or internal orders that the backend excludes, or events that never came from a real checkout.

David PombarSwiss army knife at Trackingplan

Read more from David, a Senior Product Strategist with 18+ years in digital product development and an atypical error detection knack.

Trackingplan

See everything. Miss nothing.

Your implementations audited around the clock with real-time, real-user data. Real-time alerts about errors or changes in your data, campaigns, pixels, privacy and consent. Let AI flag issues before they cost you.