Fix guide · Any website · advanced · about 60 min

TikTok Events API deduplication: match the pixel's event_id

For the findings “TikTok server events have no event ID” and “TikTok server events carry a different event ID”.

What this means

Your site sends its TikTok events twice, on purpose: once from the browser through the TikTok Pixel, and once more to your own server, which forwards them to TikTok through the Events API. TikTok recommends the Events API as a second channel together with the pixel, as long as both copies of an event can be recognized as one.

The server copy we saw does not carry the browser event’s ID:

  • No event ID: the request to your server has the event name but no ID.
  • A different event ID: the request carries an ID, but not the one the browser event had.

TikTok deduplicates on the pixel code (event_source_id), the event name and the event_id. It keeps the first copy and discards later ones within 48 hours. If the server event carries a different ID, both copies count. If it carries no ID at all, TikTok can fall back to the _ttp cookie, but only within 5 minutes, only if first-party cookies are on and your server sends ttp; otherwise both count too.

We report this as possible: we can see the request from the browser to your server, but never the request from your server to TikTok, so check the server side yourself.

Why it costs you money:

  • Purchases and leads count twice in TikTok, so results and return on ad spend look better than they are.
  • Campaigns learn from inflated numbers and may spend on the wrong people.
  • When someone later “fixes” the double counting by switching one side off, you lose the reason you added the server copy.

How to confirm it yourself

  1. Open your page in a private (incognito) window, open developer tools (F12) and go to the Network tab. Accept cookies and trigger the conversion (for example, submit a test lead).
  2. Filter by analytics.tiktok.com/api/v2/pixel, open the request for that event and note event and event_id in the payload. An empty event_id means the browser event has no ID at all (see “TikTok events have no event ID”).
  3. Clear the filter and find the request your site sends to its own server at the same moment: a form submission, an /api/... call, or a request to your server-side tag manager address. Open its payload.
  4. Compare: does it contain the same ID? If there is no ID, or a different one, the copies cannot be matched.
  5. In TikTok Events Manager, the pixel’s detail page shows “Browser”, “Server” and “Server & Browser” figures. TikTok says that if the “Server & Browser” numbers are too low, you should check your deduplication.

How to fix

The rule: one action, one ID, created in one place and used by both copies.

  1. Pick where the ID is created.

    • For purchases, the order number is ideal: the confirmation page can use it in the browser, and your backend already has it. TikTok suggests order numbers or transaction IDs.
    • For leads, page views and clicks, create a random ID in the browser when the action happens (for example crypto.randomUUID()), or create it on your server when it renders the page or handles the form, and hand it to the browser.
  2. Pass it to the pixel in the third argument of ttq.track:

    const eventId = crypto.randomUUID();
    ttq.track('Lead', {}, { event_id: eventId });
  3. Send the same value to your server with the request it already receives: a hidden form field, a property in your /api/... call, or an event parameter in your server-side tag manager feed.

  4. Use it as event_id in the Events API event, with the same event name and your pixel code as event_source_id:

    {
      "event_source": "web",
      "event_source_id": "<PIXEL CODE>",
      "data": [{
        "event": "Lead",
        "event_time": 1792771200,
        "event_id": "4b1f0c6e-2f7d-4f6b-9a51-2d3c8e7f9a10",
        "user": { "ttclid": "<from the ttclid cookie>", "ttp": "<from the _ttp cookie>", "ip": "203.0.113.7", "user_agent": "Mozilla/5.0 ..." },
        "page": { "url": "https://example.com/contact" }
      }]
    }
  5. Do not create a second ID on the server. A server that makes its own random ID, prefixes it, or sends a different value (a database row ID while the browser used the order number) produces a mismatch even though both copies “have an ID”.

  6. Keep the names identical. Event names are case-sensitive. TikTok renamed CompletePayment to Purchase and SubmitForm to Lead in 2025 and converts the old names automatically, but use the same name on both sides to be safe.

  7. With server-side Google Tag Manager: use one variable for the ID in the web TikTok Pixel tag and pass it to the server container, where TikTok’s TikTok Events API (Official) tag sends it as event_id. If you set GTM up through TikTok’s interactive flow in Events Manager, TikTok configures an event_id variable for you. Check both containers in Preview mode: they must show the same value for the same event.

  8. With a partner integration or the Events API Gateway: these handle IDs for events they collect themselves. A separate, older pixel that sends its own browser events will not match; remove it.

  9. Publish and run a test conversion.

Common causes

  • The browser events have an empty event_id, so there is nothing for the server to copy.
  • The server-side code was added later and never received the ID from the browser.
  • Browser and server each generate their own random ID.
  • The server sends a different identifier or a different event name.
  • In server-side GTM, the web tag and the server tag read different variables.

How to verify the fix

  1. For one test conversion, the pixel’s event_id, the ID in the request to your server and the event_id your server sends to TikTok are identical, and so are the event names.
  2. Send a server test event with the test_event_code from the Test Events tab (Test Server Events) and check that browser and server copies arrive; remove the code afterwards.
  3. Over the following days, the “Server & Browser” figures for the event grow.
  4. Re-run the check on IsPixelWorking. The finding should be gone.

Sources

More TikTok guides