Skip to main content
← BlogSERVER-SIDE11 MIN READ

TikTok Ads Conversion Tracking: Pixel + Events API Setup That Actually Matches

Run TikTok Pixel + Events API together: event_id dedup, ttclid, Advanced Matching, and why the in-app browser makes server-side essential.

Ariful IslamAriful IslamFounder · computer engineer ·
inX
ACCURACY = REPORTED ÷ REALILLUSTRATIVE · 30 DAYS
Your store100%
GA482%
Meta68%
Google Ads91%
95%+ healthy85–95 leaking<85 broken

Here's the detail almost every TikTok advertiser misses: when someone taps your ad, they don't open Safari or Chrome. They land in TikTok's own in-app browser. That one fact wrecks more TikTok tracking setups than everything else combined, and it's why the Events API matters more on TikTok than on any other platform I set up.

Side-by-side mapping of Meta&#x27;s tracking components to TikTok&#x27;s equivalents, from pixel and server API to click IDs, cookies and the purchase event name.
Same architecture, different names. Meta CAPI experience transfers almost directly.
MetaTikTok
Meta Pixel (browser)TikTok Pixel (browser)
Conversions API (server)Events API (server)
fbclid in the landing URLttclid in the landing URL
_fbp / _fbc cookies_ttp cookie
PurchaseCompletePayment

How does TikTok's tracking stack compare to Meta's?

TikTok copied Meta's homework, and that's good news for you: the Pixel is the browser side, the Events API is the server side, and deduplication runs on a shared event_id. Same architecture, different names. If you've ever set up Meta Pixel + CAPI dedup, you already understand 90% of this.

ConceptMetaTikTok
Browser tagMeta PixelTikTok Pixel
Server channelConversions API (CAPI)Events API
Click ID in the landing URLfbclidttclid
First-party pixel cookie_fbp / _fbc_ttp
Dedup keyevent name + event_idevent name + event_id
Identity signalsAdvanced Matching (hashed email/phone)Advanced Matching (hashed email/phone)
Purchase eventPurchaseCompletePayment

The standard web events you'll actually use: ViewContent, AddToCart, InitiateCheckout, and CompletePayment. That last one is the money event, and it needs value and currency every single time, from both browser and server.

Flow of a TikTok shopper whose identity is lost between the in-app webview and a later Safari purchase, and how a stored ttclid on a server event restores it.
Nothing is broken at the tag level. The identity is simply gone by the time they buy.
  1. Tap the ad — Opens in TikTok's webview, _ttp set there
  2. Webview is isolated — Cookies hidden from Safari, under ATT/ITP
  3. Buys days later — Pixel sees an anonymous new visitor
  4. Events API event — Stored ttclid and hashed email restore it

Why does TikTok's in-app browser break normal tracking?

Because the in-app browser is an isolated environment: cookies set inside TikTok's webview don't exist in the user's real browser, and on iOS the whole thing sits under Apple's ATT and WebKit's ITP cookie limits. The _ttp cookie your pixel sets lives in a walled garden. The user reads a review, comes back two days later through Safari, buys. To a pixel-only setup that's a brand-new anonymous visitor: TikTok gets no credit, and the algorithm optimizes on a fraction of your real buyers.

Add ad blockers and iOS privacy features that go after tracking scripts, and browser-only TikTok tracking undercounts badly. I've audited accounts where the pixel fired perfectly on test orders and still missed a large share of real purchases, because real customers don't behave like test orders.

The Events API doesn't have this problem. It sends the conversion from your server (or your store's backend) straight to TikTok. No webview, no cookie, no blocker in the path. The catch: server events arrive without browser context, so you have to attach identity yourself. That's what ttclid and Advanced Matching are for.

How do I set up the TikTok Pixel through GTM?

Use TikTok's official Pixel template from the GTM community gallery and feed it from your existing dataLayer. Here's the path I use on client builds:

  1. In TikTok Ads Manager, open Events Manager and create a web pixel (the path moves around in Ads Manager updates). Copy the pixel ID.
  2. In your GTM web container, add the TikTok Pixel template from the community template gallery and create the base tag firing on all pages with that pixel ID.
  3. Turn on Automatic Advanced Matching so the pixel picks up email and phone from forms, and pass them explicitly on checkout pages where you have them. TikTok hashes with SHA-256, same as Meta.
  4. Create event tags for ViewContent, AddToCart, InitiateCheckout, and CompletePayment, mapped from your dataLayer with value, currency, and content IDs.
  5. Generate an event_id for every event and send it with the pixel event. This exact string is what your server event will reuse for dedup.

A simple order-based ID works best for purchases, because both sides can derive it independently:

window.dataLayer.push({
  event: "purchase",
  transaction_id: "ORD-10482",
  value: 129.00,
  currency: "USD",
  event_id: "ORD-10482" // reuse the order ID; server sends the same string
});

How do I set up the TikTok Events API?

Two paths worth taking. Which one depends on your stack.

Path 1: server-side GTM (my default)

You run a GTM server container on your own subdomain (something like track.yourstore.com), your website sends events to it, and TikTok's Events API tag template forwards them to TikTok. In the tag you map: the event name, event_id (same one the pixel sent), ttclid, the _ttp cookie value, hashed email and phone, IP, user agent, value, and currency. The access token comes from your pixel's settings in Events Manager. Bonus: the same server event stream can feed Meta CAPI, Google, and GA4 with consistent numbers.

Path 2: platform-native integrations

On Shopify, the TikTok app sends server events natively once you raise the data-sharing level to Maximum. Fine for a standard store, and it takes minutes. But you give up control: you can't filter what's shared, can't fix its matching when it underperforms, and subscription or upsell flows outside standard checkout fall through the cracks. I trust the server GTM route more because I can see and shape every payload.

Flow showing an order ID reused as the event_id by both the TikTok Pixel and the Events API so TikTok counts a single CompletePayment.
Random IDs generated separately on each side is the classic reason TikTok double-counts.
  1. Checkout completes — dataLayer push carries ORD-10482
  2. TikTok Pixel — CompletePayment, event_id ORD-10482
  3. Server GTM container — Same event_id, plus ttclid and hashes
  4. Events API — CompletePayment, event_id ORD-10482
  5. One CompletePayment — TikTok dedupes on name plus event_id

How do I verify deduplication is actually working?

Place a real test order, then watch it in TikTok Events Manager: the event should arrive from both browser and server and be counted once. Use the test events tool in Events Manager to watch events land live while you check out, then confirm in the event details that browser and API events are deduplicated rather than counted separately. The check that matters: one order, one counted CompletePayment. Two purchases from a single checkout means your event_id values don't match.

What are the most common TikTok tracking failures?

Four problems cover nearly every broken account I've opened:

  1. Duplicate purchases. Pixel and Events API both firing, no shared event_id. Revenue inflates, ROAS looks great until you compare it to your bank account, and TikTok optimizes on doubled data.
  2. Missing value or currency. Purchases count, but TikTok can't do value-based optimization and your ROAS column is garbage. This one hides for months because "conversions" still show up.
  3. No Advanced Matching. Server events arrive anonymous, so attribution drops and the events barely help delivery. Hashed email and phone plus ttclid is the floor, not a bonus.
  4. Pixel-only setups. No Events API at all. On most platforms that's a leak. On TikTok, with nearly every click starting in the in-app browser, it's a flood.

Why is first-party server-side tracking the real fix here?

Because every failure above traces back to the same root: you're relying on a script in someone else's browser — worse, in TikTok's browser inside someone else's phone. First-party server-side tracking flips that. In plain terms: instead of only having a TikTok script report from the visitor's device, your site sends each event to a small tagging server you own, on your own subdomain, and that server reports to TikTok, Meta, Google, and GA4. Built on Google Tag Manager's server container, so it's standard tooling, not a black box.

For TikTok specifically, that changes four things. Conversions lost to the in-app webview, ad blockers, and iOS privacy limits get through, because the server request doesn't depend on a fragile script surviving in a cookie-hostile environment. Cookies set from your own subdomain are first-party and live longer, so the ttclid captured on click day is still there when the customer buys later. Match rates rise because the server attaches hashed email, phone, and click IDs to every event instead of hoping the pixel caught them — and better-matched events are what TikTok optimizes on, which is where cheaper CPAs actually come from. And dedup stops being fragile, because one server owns the event stream: same event_id logic for TikTok and Meta, consistent numbers everywhere, full control over what's shared per the user's consent choice.

Without it, TikTok keeps spending your budget while learning from the wrong picture.

Frequently asked questions

Does TikTok have an equivalent of Meta's Conversions API?

Yes. The TikTok Events API is the direct equivalent: a server-to-server channel that sends conversions straight to TikTok, deduplicated against the pixel through a shared event_id.

What is ttclid and where do I find it?

It's the click ID TikTok appends to your landing page URL when someone clicks an ad. Capture it on landing, store it with the session and order, and pass it in every Events API payload for attribution.

Why is TikTok counting my purchases twice?

Your pixel and Events API are both sending the purchase without a matching event_id, so TikTok treats them as two separate events. Send the same ID string (the order ID works well) with the same event name from both sides.

Do I need both the TikTok Pixel and the Events API?

Run both. The pixel captures browser context and feeds Advanced Matching; the Events API guarantees delivery when the in-app browser, ad blockers, or iOS privacy features stop the pixel. Deduplication makes running both safe.

Which TikTok event should I use for purchases?

CompletePayment is the standard web purchase event. Always send value and currency with it, from both the browser and the server, or TikTok can't optimize toward revenue.

Does the Shopify TikTok app replace a custom Events API setup?

For a standard Shopify store with data sharing set to Maximum, it's a reasonable start. A server-side GTM build gives you control over payloads, consent, dedup logic, and non-standard flows like subscriptions and upsells.

Why is my TikTok event match rate low?

Almost always missing Advanced Matching data. Send SHA-256 hashed email and phone plus ttclid on your server events, and turn on Automatic Advanced Matching in the pixel.

  • tiktok
  • events api
  • dedup

How accurate is your conversion tracking?

PLATFORM-REPORTEDconversionsdivided byBACKENDreal orders or leadsequalsaccuracy
TRY IT · YOUR NUMBERS
68%Broken. Something is missing entirely. Worth a free audit.
Reading guides because something is broken?Book 30 minutes with Ariful. We'll find it on the call and send a written fix list within 24 hours.
Get my free audit →
Ariful Islam
WRITTEN BYAriful IslamFounder of Tracking Consulting and a computer engineer (Diploma, BSc, MSc in CSE). 300+ clients and 1,000+ tracking projects since 2019.
About Ariful →
FREE · 30-MIN CALL · WRITTEN AUDIT IN 24H

Want us to check your accuracy for you?

We look at your setup together on the call. Within 24 hours you get a written audit and a prioritized fix list you can hand to any developer. Not a fit? We'll say so.