pixellint

Launch · marketers · engineers · PMs

GTM said fired. Network is empty.

Short answer

A tracking pixel that is not firing is almost never a vendor outage. It is a loader that never loaded, a collector request that was blocked, a consent gate that never unblocked, or a tag on the wrong page. Network is ground truth. Pixel Helper and GTM preview are opinions.

Validate this snippet Open the rulepack

Find the collector, not the loader

The GTM loader is googletagmanager.com/gtm.js?id=GTM-XXXX (or gtag/js?id=G- / AW- / DC-, or ns.html for the noscript iframe). That request is not the conversion. facebook.com/tr is the Meta Pixel. google-analytics.com/g/collect is the Google tag (v=2, tid G-, cid, en). ads.tiktok.com and analytics.tiktok.com are TikTok. ct.pinterest.com. px.ads.linkedin.com/collect. googleadservices.com /pagead/conversion. doubleclick.net activity for Floodlight. omtrdc.net and 2o7.net for Adobe.

A pixel not firing means the collector URL is absent, cancelled (red), blocked (chrome-extension), or never queued. A loader that 200s with an empty container is a different bug: missing id, unpublished workspace, or a GTM environment query that selected the wrong container. pixellint validate url on the loader, then again on the collector. vendor/google-tag-manager flags a missing container id. The Meta pack flags a missing numeric Pixel ID on /tr (vendor.meta.param.id.missing).

pixellint validate url 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXX' --rulepack vendor/google-tag-manager
pixellint validate url 'https://www.facebook.com/tr?ev=Purchse&em=buyer@example.com'

A diagnostic order for the empty Network tab

1. Publish, then a clean profile: no preview cookie, no GTM debug query, extensions off. 2. Preserve log. 3. Filter by the collector host, not by pixel. 4. If the loader is missing, the snippet never ran (theme, SPA, CSP). 5. If the loader is present and the collector is missing, the tag did not fire: trigger, consent, or the request was blocked. 6. If the collector is present, copy the URL and lint it. 7. If the collector is present and the UI is empty, wait, then check event name and Pixel ID.

Do this on the page where the event should fire, not on the homepage. Purchase debugging on / is how you conclude the pixel is dead while thank-you never loaded GTM. Hosted checkout on another origin is a different site. You need the thank-you host, or CAPI.

Preview fired is not Network sent

GTM preview can say a tag fired when the request was blocked, cancelled, or still queued. Preview attaches a debug cookie to a draft. Users without it get the published container. Shipping based on preview alone is how unpublished tags look fine. Submit. Publish. Then a clean browser.

Pixel Helper showing a PageView does not prove Purchase fired. Pixel Helper showing nothing does not prove the tag is unpublished; an extension may have eaten /tr. Disable the ad blocker on the QA profile or you will debug a list forever and call it a GTM bug. Confirm which version you published. Agencies leave old versions. Rollbacks reintroduce Purchse. See GTM loader and preview.

Ad blockers, CSP, and cancelled requests

EasyList and uBlock Origin match facebook.com/tr, google-analytics.com/g/collect, and ads.tiktok.com by host. The filter does not care whether the request came from fbq or from an img inside noscript. If the host is listed, both transports die. Noscript does not bypass a host block. Recovery is CAPI or a first-party collector you operate, not a second img to the same listed host. See Ad blockers.

CSP script-src must allow the tag host (connect.facebook.net, www.googletagmanager.com, analytics.tiktok.com) or the JS never runs. img-src must allow the image host (www.facebook.com, ad.doubleclick.net, px.ads.linkedin.com) or the 1x1 never loads. connect-src matters for fetch and sendBeacon; forgetting it looks like an ad blocker in the Network tab. A red request with a chrome-extension initiator is an extension. A request that never appears is often CSP or a tag that never queued.

Wrong page, SPA, hosted checkout

Purchase on the landing page is firing. It is firing in the wrong place. PageView belongs on landing, where click ids are stored. Purchase belongs on thank-you, once, after money is real. Firing Purchase on every landing inflates ROAS until the platform discounts you. Lead on page load of /contact is not a lead.

Hosted checkout (Shopify, Stripe Checkout, Adyen) often means thank-you is another host, so your GTM never runs. Cookies and click ids must travel or the conversion fires as a new user, or not at all. SPA routes do not reload the document: if you only fire on first load, checkout is invisible. Hook history. React 18 Strict Mode double-mounts and will double Purchase. See Landing page vs thank-you page and SPA virtual pageviews.

Two installs, two PageViews

Base code in the theme plus GTM's Meta tag is two PageViews. Network initiators will show both. Pixel Helper will too. Ads Manager will too, without event_id dedup. Remove one install. A History Change trigger plus All Pages is the other double. That is a pixel firing twice, which people also file as not tracking when ROAS looks insane.

Two GTM loaders (theme snippet plus a second GTM-XXXX) are two containers. pixellint validate url on each loader. Staging GTM-XXXX on production, or production GTM-XXXX on staging, is how QA purchases land in Ads Manager or how prod looks empty. GTM environments exist to swap those ids. Use them.

The request fired and the event is still wrong

ev=Purchse is a custom event. It 200s. Optimization will not treat it as Purchase. vendor.meta.param.ev.invalid warns because custom is legal. A Purchase /tr without cd[value] and cd[currency] is an incomplete conversion. A raw email in the query is vendor.meta.pii.unhashed_email. v=1 on /g/collect is Universal Analytics. A tid that is not G- is a stale UA property. Both still 200. These are pixel not tracking conversions tickets where Network is full of 200s.

Copy the collector URL. Redact em= and cookies. Paste it into the playground, or pixellint validate url --rulepack vendor/meta. Fix the findings. Then look at Events Manager. The UI lags. The URL does not. For Floodlight, src, type, cat, and ord ride on the path. For Google Ads conversion, the conversion ID is a path segment and label selects the action.

https://www.facebook.com/tr?id=1234567890123456&ev=Purchse&em=buyer@example.com

TikTok, Pinterest, LinkedIn, Floodlight

TikTok: analytics.tiktok.com loader and collection need sdkid. lib is expected to be ttq. The Events API is a different pack and a different debugger. Pinterest: ct.pinterest.com needs tid. Standard events are lowercase: pagevisit, checkout, addtocart. Checkout (PascalCase) is a custom event. LinkedIn Insight fallbacks look like px.ads.linkedin.com/collect?pid=...&fmt=gif. pid is the partner id.

Floodlight: /ddm/activity/src=...;type=...;cat=...;ord=... on doubleclick.net. A filter on pixel misses it. Unique counting also wants num alongside ord. Google Ads image conversion carries the ID in the path and needs label. Lint each URL with its pack. One unknown host is attribution, not a finding. See the pack catalog.

pixellint validate url 'https://analytics.tiktok.com/i18n/pixel/events.js?sdkid=CXXXX&lib=ttq'
pixellint validate url 'https://ad.doubleclick.net/ddm/activity/src=1234567;type=conv;cat=purch;ord=8675309?'

If the browser pipe is dark, use CAPI

Ad blockers and ITP punch holes in the pixel. The recommended setup is browser pixel plus conversion API events, deduplicated on event_id. If Network never shows /tr for a large share of users, the pixel is not going to start firing because you added another img. Send the order from the backend. Lint that JSON the same way. See Pixel plus CAPI and Conversions API not working.

Pixellint is not affiliated with Meta or Google. The playground is a pixel validator and a CAPI JSON checker. It does not replace Pixel Helper. It checks the contract the vendor documented, on the URL or body you paste. A weekly screenshot of preview is not a contract test. Keep one /tr fixture and one CAPI body in CI.

Check the artifact

Paste the pixel URL or JSON body into the playground. Same engine as pixellint validate. Artifacts you test may be stored; see privacy.