Business & Revenue OpsFree
Reconcile Google Ads and GA4 Numbers
Three exports in, one sheet out splitting the gap into the part you can fix and the part you have to label.
River's reconciliation takes all three exports and joins them on the campaign and, where you have it, the order id. What comes back is one row per campaign carrying each platform's claim beside the analytics figure and the order count, with the difference split into four named classes. Two of the four close when somebody changes a setting. The other two are how the platforms are built, so they get a size and a label rather than a fix, and the sheet says which is which.
Every answer published for this query is a list of reasons, and the reasons are correct. What none of them does is add up. A reason list cannot tell you that one of your causes is worth 612 conversions and another is worth 41 in the opposite direction, or that a third of your gap will still be there after you fix everything fixable. It also stops before the only question that matters on the day, which is which number to put in the budget deck.
Written for whoever owns paid reporting and has to sit in the budget meeting, and for the finance lead who noticed the platforms claim more orders than the company shipped. Reach for it when three systems disagree about one campaign. Where the disagreement is between two reports rather than three platforms, the two-report reconciliation closes to a residual instead. A recurring version belongs in the cross-platform paid report, and its definitions in a metric register.
Two platforms claimed more orders than the company took
Halden Supply sells outdoor gear and ran Google and Meta through August. The order system recorded 2,105 paid orders. Google Ads claimed 1,304 conversions, Meta claimed 1,508 purchases, and the two together claim 2,812, which is 707 more orders than the business took. GA4 assigned 1,631 of the real orders to paid channels and 474 to everything else. So the number under dispute is the 1,181 by which the two platforms outrun the analytics figure.
Six causes account for all 1,181, and the sheet signs each one. Both platforms claim the same order 612 times. Meta credits 178 purchases to a view with no click. Google credits 241 to clicks eight to thirty days old. Another 118 are September orders stamped on August click dates, 41 are August orders from July clicks the column drops, and 73 lost their click id. Four of the six are removable and come to 762. The other two are structural and come to 419, a third of the gap no setting change touches.
Reach behaves worse, and this is where a reconciliation should stop trying. One Meta campaign, one month, four ways of asking. Campaign level returns 412,006. The two publisher rows sum to 588,314. An hourly breakdown returns zero, because reach returns 0 when hourly breakdowns are in use. A sampled DMA split comes back at 397,120. Only the first answers how many people saw the campaign, and the other three are what the API returns to a question about something else.
How it works
Hand over three exports
The ad platform reports, the analytics export, and the order count for the same month.
Line them up
River joins on campaign and order id, then finds every order more than one platform claims.
Split the gap
Each difference gets a class, a sign and a count, then a removable or structural verdict.
Take one rule in
One number per decision, the settings worth changing, and the gap you should stop arguing about.
What you get
- One row per campaign with each platform's claim, the analytics figure and the order count
- The gap split four ways, each class signed and sized rather than merely named
- A removable column and a structural column, so the fixable share is a number
- Every reach figure marked with the breakdown it came from and whether it is summable
- One number named per decision, with the question it answers written beside it
- The settings to change, and the ones no change will fix, listed separately
Common questions
Which number goes in the budget deck?
Depends on the decision, and the output says so in one line each. For what you paid per order, the platform's spend against the order system's count, because the invoice matches the spend. For which channel is growing, one analytics tool on one model, which is what a channel and CAC review needs. Platform conversion totals answer neither.
Why can I not just add Facebook and Instagram reach together?
Because a person reached on both is one person and two rows. In the worked month the publisher split summed to 588,314 against 412,006 at campaign level, implying 176,308 people counted twice. The sheet marks every reach figure with the breakdown behind it and a summable column, so nobody adds the wrong ones next quarter.
Can I fix this by matching the attribution windows?
Partly, and only going forward. Google's default click window is thirty days, and widening it is not retroactive: a conversion outside the old window won't be retroactively counted. So aligning windows makes next month comparable and leaves the months either side of the change permanently on different bases. The sheet records the change date for that reason.
Why does the ad platform report conversions on days they did not happen?
Because the default column stamps a conversion on the click that earned it. Google says plainly that comparing against third-party platforms works better with the column that reports conversions by the time they occurred. Switching it moved 118 conversions out of the worked month and pulled 41 back in.
Our GA4 numbers are modeled. Does that break the comparison?
It changes what the comparison means, and the sheet labels it. Behavioral modeling only turns on above published thresholds, including 1,000 events per day with consent denied for a week. A property under them reports observed data only, so its paid figure is short by however much traffic declined consent.
Should the three numbers ever reconcile to zero?
No, and a page promising that is selling you something. Two of the six causes in the worked month were structural and worth 419 of the 1,181 gap. The useful target is a stated expected range per campaign, so next month's variance gets checked against it rather than investigated from scratch. A known conflict log is where that lives.
Does this work without a shared order id?
Yes, at a coarser grain. With order ids the double-claim count is exact, because each order is named. Without them River bounds it from the overlap between the two platforms' click populations and reports a range, then says which part of the split that leaves unresolvable. A dashboard spec keeps the coarser version honest.
Reconcile Google Ads and GA4 Numbers
Fill in the form and your workspace opens with the work already underway.