ULTRO NOTES Published Updated

Shopify Custom Pixels: Setup, GA4 Events, and Google Tag Manager

How Shopify custom pixels and their sandbox work, how to add and test one, how standard events map to GA4, how to run Google Tag Manager inside a custom pixel, where heatmap tools like Hotjar and Microsoft Clarity fit, consent, and capturing GA4 IDs for the Measurement Protocol.

In shortA Shopify custom pixel is JavaScript you add yourself under Settings > Customer events. It subscribes to customer events such as page views, add to cart, and completed checkout, and forwards them to GA4, Meta, and other tools. It runs on checkout and the Thank you page without editing your theme, but it runs in an isolated sandbox, so it can't read the page, and Google Tag Assistant can't see tags inside it. Purchases come from the checkout_completed event, and purchases where the Thank you page never loads, or that never touch the store at all, can't be captured by any custom pixel.

On Shopify, you can’t add tags to checkout or the Thank you page from your theme. The way to carry GA4, Meta, TikTok, and other tracking through checkout is a pixel added under Customer events in the admin. When no app covers what you need to track, or you want to manage tags together in Google Tag Manager, you add your own code as a custom pixel.

This guide is for anyone who handles GA4 or GTM tracking on a Shopify store. It covers how custom pixels work and what they can’t do, how to add one, how standard events map to GA4, where GTM tends to trip people up, where heatmap tools fit, consent, and how to capture (read and store) the GA4 client ID for the Measurement Protocol. Specs follow Shopify’s help center and developer docs as of October 2026.

What is a Shopify custom pixel?

A custom pixel is JavaScript you register in the Shopify admin to send store activity to outside tracking tools. Shopify emits page views, product views, add to cart, completed checkout, and other activity as customer events, and a pixel subscribes to them and forwards them to GA4, Meta, and so on.

Diagram of how pixels work. When a customer views pages and products, adds to cart, or completes checkout, Shopify emits these as customer events on the storefront, checkout, Thank you page, and Order status page, and pixels receive them and forward them to GA4, Meta, and others. An app pixel is installed with an app, runs in a strict sandbox (Web Worker), and is maintained by the app developer. A custom pixel is code pasted into Customer events, runs in a lax sandbox (iframe), and is maintained by you, without Shopify support. Neither kind of pixel can read the page's elements or display anything on the page.
Two kinds of pixel receiving and forwarding customer events (As of October 2026. Created by ULTRO Inc. based on Shopify Help Center and developer docs. Click the image to enlarge.)

Pixels load automatically on the storefront, checkout, the Thank you page, and the Order status page. Tags used to go in theme.liquid or the checkout’s Additional scripts, but Additional scripts is now read-only, and non-Plus stores reached their Thank you page upgrade deadline on August 26, 2026. Pixels are now the only way to track through checkout. GA4 Not Tracking All Shopify Purchases? explains the background.

There are two kinds of pixel: app pixels installed by apps, and custom pixels you add by hand. Compared with a tag pasted into the theme:

App pixelCustom pixelTag in the theme
How it’s addedInstall an appPaste code into Customer eventsPaste into theme.liquid or similar
Where it runsStorefront, checkout, Thank you, and Order status pagesSameStorefront pages only
EnvironmentStricter sandbox (strict, Web Worker)Looser sandbox (lax, iframe)The page itself
Can read page elementsNoNoYes
Shopify’s consent settings applyYesYesNo
Maintained byThe app developerYou (not supported by Shopify)You

Sandbox limits (lax vs. strict)

Pixels run in a sandbox cut off from the store’s pages, so they can’t read page elements or display anything on the page. This keeps pixels from collecting data outside what they’re given or interfering with checkout. Shopify’s developer docs say app pixels run in a strict sandbox using a Web Worker, and custom pixels in a lax sandbox: an iframe with the sandbox attribute set to allow-scripts and allow-forms.

The lax sandbox makes ordinary JavaScript tags (gtag.js, GTM, the Meta pixel code) easier to drop in, with these limits:

What doesn’t work or behaves differentlyWorkaround
Reading button clicks, form values, outbound link clicks, and other page elementsUse the data in standard and DOM events. If that’s not enough, edit theme code to publish your own events (Shopify.analytics.publish(), developer work)
Page URLs are wrong (the iframe returns the sandbox URL instead of the store page)Use the page information in events such as page_viewed (event.context)
Showing buttons, banners, or anything else on the pageNot possible
Tracking scrolling or mouse movementNot possible
Reading the page’s cookies with document.cookieUse browser.cookie.get(), which runs asynchronously in the top frame
HTML <script src=...> tagsCreate the script element in JavaScript

Heatmap tools like Hotjar and Microsoft Clarity don’t belong in a custom pixel

A custom pixel fires inside an iframe, so tags that read the page directly, or depend on its URL or elements, may not work. Shopify’s help center lists “clicks and mouse movement to create heatmaps from DOM scraping” among what pixels can’t collect, along with outbound link clicks and page scrolling. Heatmaps and session recordings are built on exactly that data, so pasting their script into a custom pixel doesn’t give you usable heatmaps.

Here’s what the two most common tools document for Shopify:

ToolWhere it’s installedCheckout and Thank you page
HotjarIts help center says to paste the tracking code in theme.liquid just before </head>, and that this covers the storefront onlyHotjar says Shopify’s built-in checkout doesn’t support third-party scripts such as its tracking code, and that code installed in a Shopify sandbox collects no data
Microsoft ClarityThe Microsoft Clarity Shopify app adds the Clarity script to the storefront (existing theme.liquid installs are migrated)Clarity’s docs say checkout and post-checkout pages are tracked through Shopify’s pixel for Shopify Plus stores. Its July 2025 announcement mentions checkout funnel analysis and heatmaps and recordings for post-purchase pages

In practice:

  • Storefront heatmaps and recordings: install through the theme or the vendor’s own Shopify app, not a custom pixel.
  • Checkout: unless the vendor documents checkout coverage through its own Shopify integration (Clarity does, for Shopify Plus), plan on no heatmaps there. Read behavior inside checkout from standard events such as checkout_started and payment_info_submitted in your analytics tool instead.

Inside custom pixel code, you can use analytics (event subscriptions), browser (cookies and storage), and init (the page, cart, and consent state at load time) directly. To receive consent changes, use api.customerPrivacy.

Custom pixel or app pixel: which to use

If an app covers what you need, use its app pixel. Shopify’s help center recommends choosing an app pixel integrated with the third-party service for higher security and automatic updates, and using a custom pixel only when no app pixel meets your requirements.

What you wantBest fit
Basic GA4 and Google Ads trackingThe Google & YouTube app. See how to connect Shopify to GA4
Meta ads trackingThe Facebook & Instagram app
A tracking tool with no official appCustom pixel
Managing several tags in Google Tag ManagerGTM inside a custom pixel
Custom events or parameters beyond the standard eventsCustom pixel (published from the theme)
Heatmaps and session recordings (Hotjar, Microsoft Clarity)Theme code or the vendor’s Shopify app. Checkout coverage depends on the vendor

Our view is that a custom pixel fills gaps the apps leave. A store that sends GA4 purchases through the Google & YouTube app and also sends them from a custom pixel will double-count. Before adding a custom pixel, list which routes currently send what. It makes checking everything later much easier.

Shopify’s help center also includes a Meta pixel example for custom pixels (fbq('init', ...), then fbq('track', 'Purchase', ...) on checkout_completed). But if the store also runs the Facebook & Instagram app, the same purchase arrives twice. If you also send the Conversions API from a server, browser and server events need the same event ID so Meta can deduplicate them.

How to add a custom pixel

In the admin, go to Settings > Customer events, name the pixel, paste the code, save, and then connect it to start tracking.

  1. First, remove old code that tracks the same things. Leaving it in place double-counts. Check:
    • Online Store > Themes: open the theme’s ”…” menu, choose Edit code, and search theme.liquid and other files for gtag(, GTM-, and fbq(.
    • Additional scripts in your checkout settings (now read-only, so anything left there doesn’t run on upgraded pages). Plus stores should also check checkout.liquid.
    • Apps: whether an app such as Google & YouTube or Facebook & Instagram already tracks the same thing.
  2. In the admin, open Settings > Customer events.
  3. Click Add custom pixel in the top-right and enter a pixel name. It can’t match another custom pixel’s name.
  4. Click Add pixel to open the event editor.
  5. If needed, set Permission and Data sale under Customer privacy (covered in the consent section below).
  6. Paste your JavaScript into the Code window. Remove HTML tags such as <script>.
  7. Click Save. To start tracking now, click Connect pixel.
Settings > Customer events in the Shopify admin. The Pixels list shows each pixel with its Status (Web, Server) and Data (Optimized, Always on), and the Add custom pixel button is at the top right
① The Status column ② Add custom pixel (Screen as of October 2026. Click the image to enlarge.)
The Add custom pixel dialog with a Pixel name field (up to 30 characters) and Cancel and Add pixel buttons
① Enter a pixel name, then click Add pixel (Screen as of October 2026. Click the image to enlarge.)

To connect later, open the Custom pixels tab in Customer events, find the pixel under Disconnected pixels, and click Connect. To stop it, click Disconnect; the pixel isn’t deleted. To delete it, use the row’s ”…” menu, but deletion can’t be undone.

Test with Shopify Pixel Helper

After saving, click Test on the pixel’s page to open Shopify Pixel Helper on your storefront. As you use the store, received events appear in a list. A green dot means the event was subscribed to and the callback ran successfully; a red dot means the callback failed (expand the event to see the error).

You can test only one custom pixel at a time, and you can’t edit it while testing (save your changes, then close and reopen the Pixel Helper tab). Event logs clear when you move to another page. If you see “Pixel is awaiting consent,” accept tracking in your cookie banner or click “Give consent to continue test.” Pixel Helper doesn’t work on headless stores, and on a password-protected store, Shopify suggests turning off the password page first. Google Tag Assistant is limited here; see Google Tag Assistant for what it can and can’t show.

To test the purchase event, place a test order. If you use Shopify Payments, go to Settings > Payments, click Manage in the Shopify Payments section, select Enable test mode, and click Save. Orders placed this way aren’t charged. In our tests, pixels still fired on test orders, so they reached GA4 as purchases too. After checking them in DebugView, decide how you’ll handle test purchases mixed into production data. To see which data each event carries, temporarily add this code and watch the browser console:

analytics.subscribe("all_standard_events", (event) => {
  console.log(event.name, event.data);
});

Shopify standard events and GA4 events

Shopify’s standard events need to be mapped to GA4 recommended events: checkout_completed to purchase, product_added_to_cart to add_to_cart, and so on. Without renaming the events and parameters, they won’t appear in GA4’s ecommerce reports. GA4 events covers GA4 event types and ecommerce parameters in full.

Shopify standard eventWhen it firesGA4 recommended event
page_viewedA page is viewedpage_view
collection_viewedA collection page is viewedview_item_list
search_submittedA store search is submittedsearch
product_viewedA product page is viewedview_item
product_added_to_cartAn item is added to the cartadd_to_cart
product_removed_from_cartAn item is removed from the cartremove_from_cart
cart_viewedThe cart page is viewedview_cart
checkout_startedCheckout startsbegin_checkout
checkout_contact_info_submittedContact information is submitted(no equivalent)
checkout_address_info_submittedThe address is submittedadd_shipping_info
checkout_shipping_info_submittedA shipping method is chosen(sometimes mapped to add_shipping_info)
payment_info_submittedPayment information is submittedadd_payment_info
checkout_completedCheckout is completedpurchase
alert_displayed / ui_extension_erroredAn alert is shown, an extension errors(no equivalent)

Shopify’s GTM tutorial maps add_shipping_info to the address step (checkout_address_info_submitted). GA4’s add_shipping_info can carry the shipping method (shipping_tier), so if you want to analyze shipping methods, mapping it to the shipping method step (checkout_shipping_info_submitted) fits better. Pick one and don’t send it from both.

Besides standard events, there are DOM events for clicks and form submissions (clicked, form_submitted, and others). They work on the storefront, checkout, and Thank you page, but not on the Order status page.

Parameters for purchase

Take the purchase parameters from checkout_completed’s event.data.checkout. Based on Shopify’s mapping:

GA4 parametercheckout_completed value
transaction_idcheckout.order.id
valuecheckout.totalPrice.amount
currencycheckout.currencyCode
taxcheckout.totalTax.amount
shippingcheckout.shippingLine.price.amount
couponThe discount code, pulled from the checkout’s discount data (checkout.discountApplications)
itemscheckout.lineItems (reshaped into GA4’s items format)

Four things to align:

  • Match the transaction ID format. checkout.order holds the order ID and buyer details, not the order number shown in the admin (such as #1001). If another tag sends order numbers to the same GA4 property (the Google & YouTube app, for example), sending order IDs here makes the two impossible to match. Check the actual values in Pixel Helper and match the other route’s format.
  • Match item_id with other routes. The example code uses SKU, but if another route sending to the same property uses a different format, the same product shows up as separate items in GA4’s product reports.
  • Decide what value means. Shopify’s developer docs describe totalPrice as the sum of all item prices “including duties, taxes, and discounts,” and subtotalPrice as the price “before duties, shipping, and taxes.” They don’t say whether totalPrice includes shipping, so compare a test order with the admin total and match whatever your other routes use.
  • The order ID exists only on checkout_completed. Per the developer docs, order.id is null on every other event.

checkout_completed fires once per checkout, typically on the Thank you page. If you have post-purchase upsells, it fires on the first upsell page instead and doesn’t fire again on the Thank you page. And if that page fails to load, it isn’t triggered at all. This matters in the “purchases a pixel can’t capture” section below. Standard events don’t include refunds either; send refunds to GA4 from a server, not a pixel.

How to set up Google Tag Manager in a Shopify custom pixel (with code)

First, here’s how purchase data reaches GA4.

Diagram of how Shopify's checkout_completed event becomes a GA4 purchase when GTM runs inside a custom pixel. 1. The custom pixel code subscribes to the event and pushes event checkout_completed, ecommerce, and page_location to the dataLayer. 2. A GTM Custom Event trigger listens for checkout_completed; the event name must match the pushed event value exactly. 3. A GA4 Event tag sends it as purchase, with Send Ecommerce data checked and Data Source left as Data Layer. GA4 receives a purchase. Paste the code as one block: consent settings first, then the GTM loader, then the event subscriptions.
From checkout_completed through GTM to a GA4 purchase (As of October 2026. Created by ULTRO Inc. based on Shopify Help Center and developer docs. Click the image to enlarge.)

To use GTM, paste its install code into a custom pixel, subscribe to customer events, and push them to the dataLayer. The steps:

  1. In GTM, click Admin, then Install Google Tag Manager, and copy the code for the head.
  2. Remove <script> and </script> and paste it into the custom pixel’s Code window.
  3. Add code that subscribes to standard events and pushes them to the dataLayer.
  4. In GTM, create the triggers and GA4 event tags (see the minimal setup below).

Paste the code as one block in this order: consent settings, the GTM loader, then event subscriptions. The consent code must come before GTM loads. Replace the container ID with your own.

window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

// 1. Pass the consent state to Google consent mode (before GTM loads)
const toConsent = (p) => ({
  analytics_storage: p.analyticsProcessingAllowed ? "granted" : "denied",
  ad_storage: p.marketingAllowed ? "granted" : "denied",
  ad_user_data: p.marketingAllowed ? "granted" : "denied",
  ad_personalization: p.marketingAllowed ? "granted" : "denied",
});
gtag("consent", "default", toConsent(init.customerPrivacy));
api.customerPrivacy.subscribe("visitorConsentCollected", (event) => {
  gtag("consent", "update", toConsent(event.customerPrivacy));
});

// 2. Load GTM (the install code without its script tags)
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');

// 3. Subscribe to events
analytics.subscribe("page_viewed", (event) => {
  window.dataLayer.push({
    event: "page_viewed",
    page_location: event.context.window.location.href,
    page_referrer: event.context.document.referrer,
    page_title: event.context.document.title,
  });
});

analytics.subscribe("checkout_completed", (event) => {
  const c = event.data.checkout;
  window.dataLayer.push({ ecommerce: null });
  window.dataLayer.push({
    event: "checkout_completed",
    page_location: event.context.window.location.href,
    ecommerce: {
      transaction_id: c.order?.id,
      value: c.totalPrice?.amount,
      currency: c.currencyCode,
      tax: c.totalTax?.amount,
      shipping: c.shippingLine?.price?.amount,
      items: c.lineItems.map((l) => ({
        item_id: l.variant?.sku || l.variant?.id,
        item_name: l.title,
        item_variant: l.variant?.title,
        price: l.variant?.price?.amount,
        quantity: l.quantity,
      })),
    },
  });
});

Shopify’s GTM example sets every consent mode value to granted, and notes that this might not match what your region’s privacy laws require. Pasted as is, it tells Google “consent granted” regardless of what the visitor chose, so build the values from the actual consent state as above (more in the consent section).

On the GTM side, this minimal setup receives purchase:

  • Trigger: type Custom Event, event name checkout_completed (it must match the pushed event value exactly).
  • Tag: tag type Google Analytics: GA4 Event, event name purchase. Under More Settings > Ecommerce, check Send Ecommerce data and leave Data Source set to Data Layer.
  • page_location: add a Data Layer Variable for page_location and pass it as a parameter on the GA4 tag.
Creating a Google Analytics: GA4 Event tag in Google Tag Manager. The event name is purchase, and under More Settings > Ecommerce, Send Ecommerce data is checked with Data source set to Data Layer
① Send Ecommerce data, with Data source set to Data Layer (Screen as of October 2026. Click the image to enlarge.)

Where GTM in a custom pixel trips people up

The common sticking points are testing, page URLs, events that are no longer collected automatically, double counting, and confusing client IDs.

Sticking pointWhat happensWhat to do
Preview mode and Tag AssistantShopify’s help center says Tag Assistant’s Troubleshoot tag feature doesn’t detect Google tags in a custom pixel or tags loaded by GTM there. Many people also report that GTM Preview mode doesn’t work wellCheck events in Pixel Helper, payloads in GA4’s Realtime report and DebugView, and requests in the browser’s Network tab (filter on collect?v=2)
Page URL (page_location)The URL GA4 collects automatically includes sandbox detailsTurn off GA4’s automatic page views and pass the page_location from page_viewed to your page_view tag. Pass event.context.document.referrer as page_referrer too, so it matches the store page
Enhanced measurementEvents based on page elements, such as outbound clicks and scrolls, aren’t collected automaticallyPublish only the ones you need from the theme with Shopify.analytics.publish(). Turn off the enhanced measurement events you won’t use
Double countingThe same event arrives from the Google & YouTube app or a theme Google tag and from the custom pixelSend through one route only
Client ID mix-upsevent.clientId is Shopify’s own ID, not GA4’s client IDWhen you need GA4’s ID, read the _ga cookie (see below)

page_location matters for more than page reports: GA4 reads UTM parameters and gclid from the URL to determine the source. Attach the same page_location to purchase and your other event tags so the page doesn’t drift between events. If (not set) rises after a change, (not set) in Google Analytics 4 explains how to tell the causes apart.

Double counting is the most common problem after adding a custom pixel. A basic check is whether each action is recorded exactly once. The quickest way: in a GA4 exploration, look for transaction IDs with 2 or more transactions. Choose the “Transaction ID” dimension and put it next to the “Transactions” metric. GA4 duplicate purchases walks through it step by step.

The Select dimensions screen in a GA4 exploration, with transaction typed in the search box. Transaction ID under the Ecommerce category is checked, and the Confirm button is at the top right
① Check Transaction ID ② Click Confirm (Screen as of October 2026. Click the image to enlarge.)

Consent for a custom pixel is handled in two places: the Permission settings in the admin, and code that passes the consent state to Google and others. The first decides whether the pixel runs; the second decides whether a running pixel sends data in line with consent.

Diagram showing that custom pixel consent is handled in two places. When a visitor opens the store, first the Permission and Data sale settings under Customer privacy in Customer events > Custom pixels decide whether the pixel runs at all: in markets that require consent (usually the EEA and the UK), it runs only with consent for the chosen purposes, and it doesn't run for visitors who opted out of data sale. Second, consent handling in the code decides whether the running pixel sends in line with consent, passing the consent state from the Customer Privacy API's init.customerPrivacy and visitorConsentCollected to Google consent mode before GTM loads. Data then goes to GA4 and others in line with consent. After setup, decline consent and check in the Network tab that sending stops or switches to cookieless pings.
The two places custom pixel consent is handled (As of October 2026. Created by ULTRO Inc. based on Shopify Help Center and developer docs. Click the image to enlarge.)

Pixel Permission settings

Open a pixel under Customer events > Custom pixels, and Customer privacy has two settings:

SettingOptionsDefaultEffect
PermissionRequired (choose purposes) / Not requiredMarketing and Analytics requiredIn markets that require consent, the pixel runs only with consent for the chosen purposes
Data saleWhether the pixel’s data collection qualifies as data saleQualifies as data saleThe pixel doesn’t run for visitors who opted out of data sale

Markets that require consent are usually the EEA and the UK. Unless you’ve set a market to require consent, visitors in that market are tracked without a banner. The Data sale setting is what matters for US visitors who opt out of the sale or sharing of their data under state privacy laws.

If you set Permission to Not required, the pixel runs before consent even in markets that require it. In that case, you have to handle consent in your code with the Customer Privacy API below.

Inside a custom pixel, init.customerPrivacy gives the consent state at load, and api.customerPrivacy.subscribe with visitorConsentCollected gives you changes made in the banner. There are four values: analyticsProcessingAllowed, marketingAllowed, preferencesProcessingAllowed, and saleOfDataAllowed. Step 1 in the GTM code above maps these to Google consent mode and passes them before GTM loads.

If your store accepts data sale and sharing opt-outs under US state laws, you can also grant ad_user_data and ad_personalization only when marketingAllowed && saleOfDataAllowed.

Shopify’s help center says the Marketing and Analytics purposes map to Google’s consent mode, but it’s up to you to check each service’s documentation for which purpose maps to which value, so treat the code above as one example. After setting it up, decline consent and check in the Network tab that sending actually stops (or switches to cookieless pings). Treat “the banner appears” and “declining actually changes what’s sent” as two separate checks.

Capture the GA4 client ID and session ID in a custom pixel

If a custom pixel captures the GA4 client ID and session ID, you can later send purchases that never reached GA4 with the Measurement Protocol, joined to the original visit. A purchase sent without those IDs shows up in GA4 as a new user’s purchase with no visit and no source.

GA4’s IDs live in two cookies on the store’s domain. A custom pixel can read the top frame’s cookies with browser.cookie.get().

CookieContainsExample value
_gaClient IDGA1.1.123456789.1700000000 (the last two numbers are the client ID)
_ga_ + your measurement ID without “G-”Session IDThe value includes the session start timestamp

Per Google’s Measurement Protocol changelog, since February 17, 2026 the Measurement Protocol accepts a client ID cookie for client_id and a session ID cookie for session_id. Google still recommends sending non-cookie values. See the GA4 Measurement Protocol guide for how to send them.

Here’s one way to capture them: when checkout completes, send them to your own server along with the order ID.

const MEASUREMENT_ID_SUFFIX = "XXXXXXXXXX"; // the part after G- in G-XXXXXXXXXX

let privacy = init.customerPrivacy;
api.customerPrivacy.subscribe("visitorConsentCollected", (e) => {
  privacy = e.customerPrivacy; // update if consent is given later on the same page
});

analytics.subscribe("checkout_completed", async (event) => {
  if (!privacy.analyticsProcessingAllowed) return; // no analytics consent, don't capture

  const gaCookie = await browser.cookie.get("_ga");
  const gaSessionCookie = await browser.cookie.get("_ga_" + MEASUREMENT_ID_SUFFIX);

  fetch("https://example.com/ga-ids", {
    method: "POST",
    keepalive: true,
    body: JSON.stringify({
      order_id: event.data.checkout.order?.id,
      checkout_token: event.data.checkout.token,
      ga_cookie: gaCookie,
      ga_session_cookie: gaSessionCookie,
    }),
  });
});

Things to watch when you build this:

  • Is _ga on the store’s domain? If you run the Google & YouTube app or a Google tag in the theme, GA4 runs on the store’s pages and _ga is stored on the store’s domain. If neither is present and GTM runs only inside the custom pixel, GA4 runs only inside the pixel. Either way, open your browser’s developer tools and check that the cookie actually has a value before relying on it.
  • Capture before checkout completes, too. Purchases missing from GA4 include those where the Thank you page never loaded, and then code that captures on checkout_completed never runs. You can capture at checkout_started or payment_info_submitted keyed on the checkout token (checkout.token), then match it with the order once it’s placed.
  • Make the endpoint accept CORS. Requests come from the sandbox, so your server has to allow cross-origin POSTs.
  • Send IDs only. Don’t send email addresses, addresses, or other buyer details.
  • Respect consent. Don’t capture IDs for visitors without analytics consent, and don’t send their purchases later.

We turned this whole flow, from capturing to sending, into a Shopify app, GapFill. It’s our own product, so read with that in mind. Its app pixel captures the client ID and session ID at checkout automatically, and it recovers missing GA4 purchase events server-side. It works alongside your existing tags, so there’s no custom pixel to write, and the missing purchases report is free.

Our Shopify appGapFill: Recover missing GA4 purchase events server-sideWorks alongside your existing tags. Free missing purchases report.See GapFill →

Purchases a custom pixel can’t capture

A custom pixel only runs when a Shopify page opens in the buyer’s browser. Purchases that never reach the Thank you page, or that never touch the store at all, can’t be captured no matter how you change the code.

Type of purchaseCaptured by a pixel?Why
Buyer reaches the Thank you page normallyYescheckout_completed fires
Buyer doesn’t return from an off-site payment providerNoThe Thank you page never opens
Buyer closes the Thank you page while it loadsNoThe event doesn’t fire unless the page loads
Sending is blocked by an ad blocker or browser tracking preventionThe pixel may run, but the request to GA4 can be blockedRequests to the destination are blocked
Checkout completed inside an AI assistant or social appNoNo store page that loads pixels opens in the buyer’s browser
Subscription renewalsNoThe order is created automatically and the buyer never visits

In one of our clients’ Shopify stores (GA4 connected through the Google & YouTube app), we matched orders against GA4 purchases one by one from August to mid-September 2026, and 21% of orders were missing from GA4. Even with an app integration, purchases a tag can’t capture remain. The only way to know how many your store has is to match your own orders against GA4.

Those purchases have to be sent from the server, starting from Shopify order data, not from a pixel. Server-side GTM still needs the first event to happen in a browser, so it can’t recover purchases where no pixel ran. GA4 Not Tracking All Shopify Purchases? compares the ways to fill that gap.

Custom pixel not working? What to check

When a custom pixel doesn’t work, check in this order: is the pixel running, is it receiving events, and is the data reaching the destination?

  1. Is it connected? Check the Status column in the pixel list under Customer events. Saving alone doesn’t start it; it has to be connected.
  2. Is it waiting for consent? Look for “Pixel is awaiting consent” in Pixel Helper, and check the Permission and Data sale settings and the per-market settings under Settings > Customer privacy.
  3. Code errors: a red dot in Pixel Helper means that event’s callback threw an error. An error at the top level of the code (outside a subscription) can leave the pixel only partly working.
  4. Event and trigger names: make sure the GTM trigger’s event name matches the event you pushed to the dataLayer.
  5. Is it reaching GA4? Check event names and parameters (transaction_id, value, currency, items) in GA4’s Realtime report or DebugView. Check for duplicates here too.

Sources

FAQ

What is a Shopify custom pixel?

It's JavaScript you register under Settings > Customer events in the Shopify admin to send store activity to outside tracking tools. It runs on checkout and the Thank you page without editing your theme code. Pixels installed by apps are app pixels; pixels you add by hand are custom pixels.

What's the difference between a custom pixel and an app pixel?

How they're installed and where they run. An app pixel comes with an app and runs in a stricter sandbox. A custom pixel is code you paste into the admin; it runs in a looser sandbox where ordinary JavaScript tags work more easily, but you maintain it yourself. Shopify recommends an app pixel when one meets your needs, for higher security and automatic updates.

Can I use Google Tag Manager in a Shopify custom pixel? Does Preview mode work?

Yes. Paste the GTM install code without its HTML tags, then add code that subscribes to customer events and pushes them to the dataLayer. Because GTM also runs inside the sandbox, Shopify's help center says Google Tag Assistant's Troubleshoot tag feature doesn't detect tags in a custom pixel. Test with Shopify Pixel Helper and GA4's Realtime report and DebugView instead.

Why isn't my custom pixel firing?

The usual reasons: it was saved but never connected, it's waiting for consent in a market that requires it, it's blocked for visitors who opted out of data sale, there's an error in the code, or the GTM trigger's event name doesn't match. Check in Shopify Pixel Helper first whether events arrive and whether any show errors.

Can I run Hotjar or Microsoft Clarity in a custom pixel?

Not usefully. A custom pixel runs in an iframe and can't read the page's elements, clicks, or scrolling, which is what heatmaps and session recordings need. Hotjar's help center says to install its code in theme.liquid and that it covers the storefront only. Microsoft Clarity's Shopify app adds Clarity to the storefront, and Clarity says checkout and post-checkout pages are tracked through Shopify's pixel for Shopify Plus stores.

Why doesn't checkout_completed fire for some purchases?

It fires only when the Thank you page (or the first upsell page, if you have one) loads. It doesn't fire for buyers who don't return from an off-site payment provider, buyers who close the page while it loads, checkouts completed inside AI assistants or social apps, or subscription renewals. Those have to be sent from order data with the Measurement Protocol.

Does a custom pixel run without a cookie banner?

In markets set to require consent (usually the EEA and the UK), it runs only when the visitor has given the consent its Permission setting requires. In markets that don't require consent, it runs without a banner. New custom pixels qualify as data sale by default, though, so they don't run for visitors who opted out of data sale.

Key takeaways
  • A custom pixel is tracking JavaScript added under Settings > Customer events. It runs on checkout and the Thank you page too.
  • It runs in a lax iframe sandbox, so it can't read the page. Page URLs include sandbox details, so use the page_viewed event's values.
  • To send to GA4, map Shopify standard events to GA4 recommended events. Purchase is checkout_completed to purchase.
  • Google Tag Assistant can't see tags in a custom pixel. Test with Shopify Pixel Helper and GA4's Realtime report and DebugView.
  • Heatmap tools such as Hotjar and Microsoft Clarity go in the theme or the vendor's Shopify app, not in a custom pixel.
  • Handle consent in two places: the pixel's Permission settings, and code that passes Customer Privacy API values to Google consent mode.
  • Capturing the GA4 client ID and session ID lets you send missing purchases with the Measurement Protocol, joined to the original visit. Purchases where no pixel ran can't be captured by the pixel itself.

Written byRyushi Nagata

CEO, ULTRO Inc. (Tokyo)

Shopify Partner. Ryushi handles Shopify store builds, maintenance, advertising and CRM end to end, and has managed more than ¥4 billion in ad spend over his career. He holds Google Ads certifications (Search, Display, Video, Shopping, Apps, Performance Max, Measurement) and the Google Analytics certification.

How many purchases is GA4 missing in your store?

GapFill matches your Shopify orders against GA4 purchases, shows what GA4 missed, and recovers those purchase events server-side. It works alongside your existing tags, and the missing purchases report is free.

See GapFill →