Ecommerce Conversion Tracking: Why the Numbers Disagree
GA4 says forty purchases yesterday. Google Ads says fifty-two conversions for the same conversion action. Meta Events Manager says sixty-one. Shopify’s own orders screen, the only one of the four actually counting money changing hands, says forty-four. None of the other three is lying. Each one is answering a slightly different question, and one of them is also double-counting.
This is the audit I run on an inherited account before touching a budget, bid or audience, because every other number in the account – ROAS, cost per acquisition, which campaign gets more spend – is downstream of whichever one of these four counts is wrong. What follows is the order to check them in, the one bug that explains most of the gap, and the fix.
The three numbers that never match
Three platforms, three different jobs. GA4 is trying to describe a session on your website. Google Ads is trying to decide which of its own ads deserves credit for a sale. Meta is trying to decide the same thing, on its own clock, inside its own attribution window. None of the three was built to agree with the other two, and none of the three was built to match Shopify’s orders screen, which is the only one of the four that is not making an attribution decision at all. It is just counting orders.
That is the baseline worth sitting with before chasing a discrepancy: some gap between these numbers is the design working as intended. The question that actually matters is not “why don’t these match” but “is any of these wrong, as opposed to merely different”, and that is a question with a real, checkable answer.
Why would my numbers disagree if nothing is broken?
Three structural reasons, and all three are true at once in most accounts.
- They measure on different clocks. Google Ads can credit a conversion to the day of the click rather than the day of the sale, inside a lookback window of up to ninety days. GA4 and Meta generally record the event on the day it happened. Pull a single day in Google Ads and you are seeing sales that trace back to clicks spread across weeks.
- They use different attribution models. Google Ads runs data-driven attribution by default, splitting fractional credit across every ad interaction in the path. GA4’s ecommerce purchase count is not an attribution decision at all – it is a count of purchase events in sessions, full stop. Meta attributes within its own click and view windows, set in Events Manager, independent of what Google Ads or GA4 are doing.
- They lose different amounts of signal. iOS privacy limits, ad blockers and cookie consent banners each suppress a different slice of events on a different platform, so under-counting is not evenly spread across the three either.
None of that explains a platform counting more purchases than Shopify actually took that day. Attribution and timing differences move credit around between ads. They do not invent an order that never happened. If a platform is higher than Shopify’s own count on a single day, something is firing twice.
The ten-minute reconciliation check
Pick one day – yesterday is fine, and a quiet day is more readable than a sale day. Then pull four numbers for that single calendar day and nothing else.
- Shopify’s orders screen for that one day: order count and total revenue. This is the ground truth the other three get checked against.
- GA4’s ecommerce purchases for the same day, from Reports, Monetization, Ecommerce purchases. Filter to the single date before reading the count.
- Google Ads conversions for your purchase conversion action, same day, switched to report by conversion time rather than the click-date default, so you are comparing sales that happened that day rather than sales attributed back to a click that day.
- Meta’s Purchase event count in Events Manager for the same calendar day, reading the raw event count rather than the attributed-results column, which applies Meta’s own window on top.
Now compare all three to Shopify. A platform running a little under Shopify is consistent with lost signal and is rarely worth chasing on a single day. A platform running over Shopify’s own order count, on a day you have isolated like this, is not an attribution quirk. It is counting the same order more than once.
The comparison, in one table
What each platform is actually measuring, and the key each one uses to recognize a duplicate before it double-counts a sale.
| Platform | What it counts | Attributed to | Its dedup key |
|---|---|---|---|
| Shopify orders | An order that was placed | The day it was placed | Order ID – this is the ground truth |
| GA4 | A purchase event in a session | The day of the event, by default last-click channel | transaction_id, scoped to one user, web streams only |
| Google Ads | A conversion for the action you configured | The click, inside your lookback window, by data-driven attribution | transaction_id, exact string match |
| Meta | A Purchase event from the Pixel and/or the Conversions API | Meta’s own click and view attribution window | event_id plus event_name must match between Pixel and Conversions API |
Every dedup key in that table depends on something actually being sent consistently. That dependency is where the duplicate-pixel case comes from.
What is the duplicate-pixel case?
The most common cause of a platform over-counting Shopify’s own orders is not a reporting quirk. It is the same purchase event firing more than once, and the platform having no consistent key to recognize the second firing as a repeat of the first.
On Shopify specifically, it shows up in one of three shapes. A theme ships its own GA4 or Meta tag on the order status page, and a Google Tag Manager container is added later with the same tag inside it, so the purchase event fires twice from two independent sources that have never heard of each other. A customer reloads or revisits the order confirmation page – normal behaviour, not a bug on their end – and the tag fires again with no transaction ID to recognize the repeat. Or a store runs the Meta Pixel and the Conversions API side by side without wiring them to agree on an event ID, so Meta receives what looks like two separate purchases for one order.
Check for the first shape in five minutes: open your theme code or the order status page settings for a native GA4 or Meta tag, then open your Google Tag Manager container and search its tags for the word purchase. Finding both is the single most common root cause I see in an inherited account.
How do I stop the same purchase from being counted twice?
Each platform’s own documentation is specific about what makes two events count as one, and all three come down to a consistent, unique identifier rather than anything clever.
Google’s own guidance on using a transaction ID to minimize duplicate conversions is that a second conversion for the same conversion action with the same transaction ID is treated as a repeat and is not counted a second time. A static or reused ID breaks this; the ID has to be unique and generated per order.
GA4 works the same way through a transaction ID, with two limits worth knowing before you rely on it: Google’s own documentation on minimizing duplicate key events with transaction IDs states that deduplication only works for data collected through web streams, not app streams, and that an empty transaction ID causes every purchase missing one to be collapsed into a single event rather than counted correctly.
Meta requires both sides to agree. Meta’s documentation on deduplicating Pixel and Conversions API events is explicit that the Pixel’s event ID has to match the Conversions API’s event_id, and the Pixel’s event name has to match the Conversions API’s event_name, before the two are treated as the same purchase. If your Conversions API setup was added without wiring that match, the Pixel and the server event are two purchases as far as Meta is concerned.
The fix in every case is the same shape: pick the Shopify order number as the single transaction ID or event ID, send it identically from every source that fires that tag, and remove whichever source is the duplicate rather than trying to make two conflicting sources agree.
What to fix, in order
| Step | Why it is here |
|---|---|
| Confirm one tracking source per platform | Find and remove the second GA4 or Meta tag before anything else. A dedup key cannot fix two genuinely separate tags firing the same event with different code. |
| Make the transaction ID the real Shopify order number, everywhere | Not a session ID, not a timestamp, not a value that can be stripped or re-generated on reload. The same string, sent by every tag that touches that order. |
| Match Meta’s event ID and event name across Pixel and Conversions API | If you run both, this is the one Meta will not deduplicate without. |
| Reconcile against Shopify weekly, not once | The five-minute check above catches today’s bug. A weekly habit catches the next app install that adds a tag nobody asked for. |
| Only then trust the ROAS you are bidding against | A target you are optimizing toward is only as good as the count underneath it. Fix the count before you touch the target. |
Reconciling conversion counts is one piece of a wider habit worth having before trusting any report out of these platforms. What to check in GA4 before trusting a report covers the same discipline applied to GA4 specifically, for the weeks between the reconciliations above.
Questions people ask
Why don’t Google Ads, GA4 and Meta show the same number of purchases?
Is some mismatch between ad platforms normal?
What is the duplicate-pixel problem in ecommerce conversion tracking?
How often should I reconcile ad platform numbers against my store?
Can I trust my ROAS if conversions are being double-counted?
Want this run properly?
I am Greg Asuncion, an ecommerce paid media consultant. I run Google Ads, Meta, Amazon and Microsoft for direct‑to‑consumer brands on Shopify — one person, published pricing, and a free audit after a call. Here is how I run a measurement audit before touching a budget.
