Shopify Duplicate GA4 Events: Find and Fix Them

GA4 reports roughly double your real revenue, and the order count does not match Shopify admin.
This is almost always the same event fired by two different installations of the same tag. It matters beyond reporting: if you run paid ads, inflated conversions feed straight into bidding.
Symptom
- GA4 purchase count is an exact or near multiple of Shopify's order count.
- Revenue in GA4 is roughly 2x the Shopify total.
- The GA4 DebugView shows two
purchaseevents with the sametransaction_id. - Ads platforms report more conversions than orders exist.
Cause
Shopify stores accumulate tracking in several places, and each one is invisible from the others:
- Shopify's native GA4 integration (Online Store → Preferences) — and a manual
gtagsnippet intheme.liquid. - A Google Channel / Google & YouTube app installation, plus one of the above.
- Google Tag Manager containing a GA4 tag, while GA4 is also configured natively.
- Additional scripts (Settings → Checkout → Order status page) left over from a previous setup.
- A theme upgrade that reintroduced a snippet you had removed.
The duplicate usually appears after someone adds tracking without knowing tracking already existed.
Fix
1. Find every tag firing
Open a completed order confirmation page and run:
// How many GA4 measurement IDs are loaded?
const ids = [...document.querySelectorAll('script[src*="googletagmanager"]')]
.map((s) => new URL(s.src).searchParams.get('id'))
.filter(Boolean);
console.log('GA/GTM IDs on page:', ids);
// Any duplicates in the dataLayer?
const purchases = (window.dataLayer || []).filter(
(e) => e[1] === 'purchase' || e.event === 'purchase'
);
console.log('purchase events in dataLayer:', purchases.length);
Seeing the same G-XXXXXXX twice, or two purchase entries, confirms it.
2. Check each source in turn
| Where | What to look for |
|---|---|
| Online Store → Preferences | Google Analytics field populated |
| Apps | Google & YouTube channel installed |
| Settings → Customer events | Custom pixels firing GA4 |
| layout/theme.liquid | Hard-coded gtag or GTM snippet |
| Settings → Checkout → Additional scripts | Legacy order-status scripts |
3. Keep exactly one
The most reliable setup for most stores is Customer Events (Settings → Customer events → Add custom pixel), because it survives theme changes and runs on checkout pages where theme code does not.
Remove every other GA4 installation. If you keep GTM, remove the native integration; if you keep native, remove the GTM tag. Never both.
4. Make the event idempotent
Even with one tag, a page refresh on the thank-you page can re-fire purchase. Guard against it:
analytics.subscribe('checkout_completed', (event) => {
const orderId = event.data.checkout.order.id;
const key = `ga4_purchase_${orderId}`;
// A refresh on the thank-you page must not count a second purchase.
try {
if (localStorage.getItem(key)) return;
localStorage.setItem(key, '1');
} catch {
// Private mode: proceed rather than losing the event entirely.
}
gtag('event', 'purchase', {
transaction_id: orderId,
value: event.data.checkout.totalPrice.amount,
currency: event.data.checkout.currencyCode,
});
});
Always send transaction_id. GA4 deduplicates on it, which limits the damage even if something does fire twice.
Verify
- Open GA4 → Admin → DebugView, place a test order, and confirm exactly one
purchaseevent. - Refresh the thank-you page and confirm no second event.
- Compare GA4 revenue against Shopify admin for the same 24 hours — they should be within a percent or two.
- Check Realtime during the test to confirm only one measurement ID reports.
Prevent
- Document where tracking lives. Most duplicates come from someone adding a tag that already existed.
- Always send
transaction_idon purchase events. - Re-check after theme updates — snippets reappear.
- Reconcile GA4 against Shopify monthly. A drift is the earliest signal of a new duplicate.
Frequently asked questions
Does this affect Google Ads bidding?
Yes, and this is the expensive part. Smart Bidding optimises toward reported conversions, so inflated data makes it bid for traffic that converts worse than it appears.
Can I fix the historical data?
Not retroactively — GA4 data is immutable once collected. Annotate the date you fixed it and compare periods carefully.
Why does Shopify admin differ slightly even after fixing?
A small gap is normal: ad blockers, consent refusals and cross-device journeys all lose events. Within a few percent is healthy; double is a bug.
Related reading
- Bot traffic corrupting your analytics
- Traffic but no sales? Start here
- website SEO audit — our service page
Need a hand?
We audit and fix analytics on Shopify stores across the USA, UAE, Saudi Arabia, Qatar and the UK. If your numbers do not reconcile, get a free audit or see our website SEO audit service.
The Yaxify Team
Shopify & Web Development Agency
We build custom Shopify stores and high-performance websites for growing businesses across the USA, UK, and UAE. Everything we publish comes out of client work we have actually shipped.
More about Yaxify →