Shopify and Google Ads conversion tracking without double counting
Most Shopify stores quietly count every purchase two or three times: the Google & YouTube app, a leftover manual tag, and a GA4 import all firing at once. Here's how to pick one to trust, silence the rest, and stop Smart Bidding from chasing conversions that never happened.

Open Google Ads and Shopify side by side and the purchase counts rarely match: Ads shows more, sometimes two or three times more. The usual cause isn't attribution. It's that two or three tags are each reporting the same order. The fix is one decision, not a rebuild: pick one primary source of purchase truth (for most stores, the Google & YouTube app's purchase conversion), set every other purchase tracker to secondary in Google Ads, and reconcile conversions against Shopify orders every week.
Nobody sets out to count purchases three times. Stores get there honestly: a freelancer added a manual tag in 2023, the Google & YouTube app arrived with the checkout-extensibility migration, and someone imported the GA4 purchase event "for reporting." Each step was reasonable. The sum is a Google Ads account claiming two to three conversions per real order, with Smart Bidding scaling budget into the fiction. One primary source of truth per business event, everything else demoted: that's the whole fix, and the rest of this page is how to apply it.
#The double-counting trap
Three trackers can each report the same Shopify purchase to Google Ads, and Ads will happily count all three if they're all primary conversion actions:
The trap is silent because every individual tag is working. Diagnostics show green; the only broken thing is the sum. Detection takes two minutes: Google Ads → Goals → Conversions → Summary. If more than one purchase-shaped conversion action is set to primary, you're double-counting right now.
#Option A: the Google & YouTube app (the default that earned it)
For most stores the app is the right primary. Install the Google & YouTube channel from the Shopify app store, connect the Ads account, and it creates and maintains the purchase conversion: dynamic values, currency, transaction IDs for dedup, enhanced conversions support, and the part that matters operationally: it survives Shopify's checkout changes, because Shopify ships it (setup reference: Google's Shopify integration doc).
What it hides in return: no custom value rules (margin-based values, excluding subscription renewals, LTV-adjusted values), no visibility into dropped events, and its conversion settings live partly behind the app rather than in your tag stack. That opacity is tolerable exactly because you'll reconcile against orders below.
#Option B: a manual tag (control, at maintenance cost)
A hand-built Google Ads purchase tag is the right primary in the cases the
app can't express: margin-based or rules-adjusted conversion values,
subscription-aware counting, or a stack that already runs server-side. Two
requirements the app was handling for you now land on your desk: fire from
a checkout-extensibility web pixel (legacy checkout.liquid
customizations are gone), and send transaction_id on every purchase so
refresh-double-fires dedup. Wire values dynamically from the order object.
A hardcoded average defeats the point, per Value-based bidding needs values you can trust. If
you're going this far, evaluate going server-side properly:
Server-side tracking for Shopify: the sandbox, CAPI, and the three real routes covers that variant.
#Option C: the GA4 import (usually the duplicate)
The GA4 purchase import is the tag most stores should demote. It arrives last ("let's see GA4 data in Ads"), duplicates whichever purchase source already exists, and adds GA4's own attribution and processing quirks on top. That seam is explained in GA4 conversion tracking: setting up key events. Keep the import as a secondary conversion action: visible in columns, excluded from "Conversions", harmless to bidding.
#The recommended stack
- Primary: Google & YouTube app purchase conversion (Option A), unless you have a concrete value-rules or server-side reason for Option B.
- Secondary: everything else purchase-shaped: the GA4 import, any legacy manual tag you can't delete yet. Better: delete the legacy tag.
- Non-purchase actions (add-to-cart, begin-checkout): secondary, always. They're diagnostics, not things to bid toward.
- Enhanced conversions: on. Hashed customer data lifts match rates; details in Enhanced conversions: what they fix, what they leak, how to turn them on.
One primary source of truth per business event is the whole doctrine. The same one-primary rule is what fixes Meta-side stacks, and it's why this page and Why Facebook Ads conversions don't match Shopify describe the same disease in two dashboards.
#How accurate is Shopify's Google channel, really?
Accurate enough to bid on, not accurate enough to skip reconciliation. Shopify's own conversion reporting and Google's conversion counts measure different objects (orders vs attributed conversions; Shopify's conversion summary doc covers its side), so exact agreement was never on the table. The honest questions are: what share of real orders does the integration report to Ads, and does that share hold steady? Running an order feed side-by-side with the native integration, healthy looks like this (illustrative magnitudes, since stability is the real test): the integration reporting on the order of 85% to 95% of ad-attributable orders, week over week. A stable 0.9 ratio is workable; a 0.9 that becomes 0.6 after a checkout update is the incident.
#Verify: reconcile orders against conversions
The reconciliation is one weekly ritual: Shopify orders (by day, ad-sourced if you can segment) next to Google Ads conversions (by day, remembering Ads books to click date). Healthy looks like a stable ratio; three tags looks like two to three times over; a decaying ratio means a tag broke in a checkout update. The full test procedure, including firing a test purchase without polluting data, is The conversion tracking QA checklist: test it like you'd test code.
That ratio earns its keep after the cleanup, not just during it. Once it sits stable, it becomes an early-warning system: the day a checkout update or a re-added tag breaks tracking, the ratio moves before your reporting does. The catch is that it only warns you if someone looks, and a weekly reconciliation is the first ritual to lapse when the quarter gets busy.
That's the part worth handing off. Buron's Shopify order feed and attribution datasets watch orders against claimed conversions continuously, so a broken ratio arrives as a dated finding in your inbox instead of a "why did our cost-per-conversion crater?" three weeks later. *[Connect your store →]
Frequently asked questions
How accurate is Shopify's Google channel integration?
Good enough to be the primary purchase source for most stores: it survives checkout updates, sends dynamic values with transaction IDs, and supports enhanced conversions. What it doesn't give you is control: custom value rules, subscription handling, or visibility into what it dropped. That's why you still reconcile orders against conversions.
Can I run the Google & YouTube app and my own conversion tag together?
Yes, but only one may be a primary conversion action in Google Ads. The other must be secondary, or every purchase counts twice. Ads doesn't warn you: both tags are 'working correctly', and the account happily reports double. Pick one source of truth and demote the rest.
Why does Google Ads show more purchases than Shopify?
Usually stacked tags: the Google & YouTube app, a leftover manual tag, and a GA4 import all reporting the same order as primary conversions. Attribution windows and click-date reporting add smaller gaps, but a consistent 2x or 3x means duplicate tags, not attribution.
Related
- Server-side tracking for Shopify: the sandbox, CAPI, and the three real routes
- Shopify pixel setup: native web pixels and the Meta Pixel, done right
- Enhanced conversions: what they fix, what they leak, how to turn them on
- Why Facebook Ads conversions don't match Shopify
- The conversion tracking QA checklist: test it like you'd test code