Every agency has sat in this meeting. You share the monthly report, and before you reach the strategy slide the client is already squinting at their own ad dashboard, comparing your conversion count to the one glowing on their screen. The two don’t match. In that instant you’re no longer presenting results, you’re defending arithmetic.
The uncomfortable truth is that those numbers were never meant to be identical. Each platform answers a slightly different question, about a slightly different event, measured over a different window, in a different time zone, and de-duplicated by different rules. The job isn’t to force them to agree. It’s to understand why they disagree, and to hand the client one number you can stand behind.
Why your numbers never match between platforms
This is the broad platform-mismatch problem behind most marketing reports. HubSpot, GA4, Google Ads, Meta, Looker Studio, and spreadsheets can all be technically correct while still creating a disputed executive number. The report needs a reconciliation layer, not another exported chart.
The five places a number can diverge
Before you debug any specific discrepancy, it helps to know where they hide. In practice, nearly every mismatch traces back to one of five culprits:
- Event definition. A “conversion” in Google Ads might be a form view; in your CRM it’s a closed-won deal. Same word, different moment.
- Attribution window. 7-day-click versus 28-day-click, with or without view-through, changes the count dramatically.
- Time zone. The ad account reports in its own zone; your warehouse stores UTC. The boundary days never line up.
- De-duplication. One person, three touchpoints. Counted once per platform, or once globally?
- Filtering. Bots, internal traffic, and test events stripped out in one place but not another.
The goal isn’t to make the platforms agree. It’s to understand why they disagree, and to publish one number you can defend.
Attribution windows are the usual suspect
If you only check one thing, check the windows. Meta defaults to a 7-day click, 1-day view model; Google leans on data-driven attribution across a 30-day path; your CRM, quietly, credits whoever owns the contact. None of these is wrong, but stacked together they guarantee three different totals. Write the window down next to every metric in the report, it’s the single cheapest way to pre-empt the “why don’t these match” question. (More on this in attribution models, explained.)
Time zones and the midnight problem
The second most common issue is invisible until the end of the month. A conversion that fires at 11:40pm Pacific lands on the 1st of next month in UTC. Run a monthly report and a handful of events quietly hop the boundary in each direction. Over a quarter it washes out; on a single month’s invoice-driving number, it’s the difference between hitting target and missing it.
De-duplicate once, at the source
When the same lead shows up in Meta, Google, and your CRM, summing the platforms double- and triple-counts a single human. The fix is to resolve identity in one place, a unified events table keyed on a canonical identifier, and let every downstream report read from that, rather than adding up dashboards that were never designed to be added.
| Source | Reported | After reconciliation |
|---|---|---|
| Google Ads | 412 | 318 |
| Meta | 380 | 261 |
| CRM (closed-won) | 291 | 291 |
| Unified (de-duplicated) | Not reported | 372 |
Build one reconciliation layer
Spreadsheet reconciliation works exactly until it doesn’t, usually the month a teammate is on holiday. The durable answer is a thin modeling layer that maps each platform’s raw events to a shared schema, applies one set of rules, and exposes a single metric. It looks unglamorous, but it’s the difference between answering “where did this number come from?” in five seconds versus five hours.
-- one canonical event, regardless of source select coalesce(meta.lead_id, gads.lead_id, crm.contact_id) as canonical_id, min(event_at_utc) as first_seen, max(is_closed_won) as converted from unified_events group by 1
A checklist before you send
Run these six checks and most “the numbers are wrong” emails never get written:
- State the attribution window beside every conversion metric.
- Confirm the date range uses one time zone, named explicitly.
- Reconcile to the de-duplicated total, not the sum of platforms.
- Strip internal and test traffic, and say that you did.
- Show last month beside this month, so movement has context.
- Add one sentence explaining any figure that differs from the native dashboard.
None of this makes the platforms agree. It does something more useful: it lets you walk into the meeting with one number, a clear story about where it came from, and the quiet confidence that you’ve already answered the question before it’s asked.
FAQ
Why do marketing numbers never match between platforms?
Marketing numbers diverge because platforms use different event definitions, attribution windows, time zones, filters, and deduplication rules.
Should I force every platform to match?
No. The goal is to reconcile differences, name the source of truth for each metric, and publish one number the team can defend.
What is the fastest way to reduce reconciliation hell?
Start by documenting attribution windows, date ranges, time zones, exclusions, and the owner for every KPI before the report is shared.
When should a team build a reporting layer?
Build a reporting layer when recurring reviews are spent defending the numbers instead of deciding what changed and why.
If this is the dispute behind your next report, run the Report Trust Checklist, then map the rules into CRM reporting, governed dashboards, and the Maven agent. Go deeper on HubSpot and GA4 mismatches, the monthly marketing report workflow, and Looker Studio vs HubSpot vs Sheets, then review pricing. Bring the disputed report, run the checklist, map the first trusted workflow.
Sources and references

Jamie Isabel
Founder at Maven
