Deduplication
When a single user converts on your site, both your Snap Pixel SDK and your Conversions API (CAPI) integration will typically fire; each independently reporting the same event. Without deduplication, Snap must pick only one source, thereby not enriching your signals data. In other words you lose the benefits from a properly setup CAPI implementation.
This page describes exactly how Snap matches a Pixel SDK event to its corresponding CAPI event, the implementation steps you need to follow on your side, and the special handling that applies to the PURCHASE event.
We recommend pairing both integrations. The Pixel SDK gives Snap a browser-side signal (cookies, IP, user agent) that a server-only integration sometimes can't replicate. CAPI gives Snap a durable server-side signal that survives ad blockers, ITP, ATT, and other client-side losses. Running both — with proper deduplication — gives you the best of both worlds.
How Snap matches Pixel SDK events to CAPI events
For Snap to recognize two events as the same conversion, both events must carry the same unique identifier on a corresponding field, for the same pixel, and within a window.
The matching pair is:
| Source | Field name | Notes |
|---|---|---|
| Pixel SDK | client_dedup_id | Set on the snaptr('track', ...) call. |
| CAPI | event_id | Set on each object in the data array of the payload. |
When Snap sees a Pixel SDK event and a CAPI event for the same pixel that share the same value, it merges them into a single conversion by taking the union of the two events. Whichever event arrived first is retained as the surviving conversion; any exclusive information present only on the second event (for example, identifiers the first event didn't carry) is merged into that surviving conversion. The second event is then dropped so reporting and optimization see exactly one conversion.
The matching window
| Event type | Fields used for matching (Pixel SDK ⇄ CAPI) | Window |
|---|---|---|
| Non-purchase events | client_dedup_id ⇄ event_id | 48 hours |
PURCHASE | client_dedup_id ⇄ event_id, and transaction_id ⇄ order_id | up to 30 days |
For non-purchase events (e.g. SIGN_UP, PAGE_VIEW, VIEW_CONTENT, ADD_BILLING), Snap will dedupe two events that share the same client_dedup_id / event_id on the same pixel within roughly 48 hours. If they fall outside that window, they are treated as separate conversions.
PURCHASE is a special case — see Purchase event nuances below.
Every cross-channel dedup pair on this page is the same conceptual value sent through two integrations under two different names. Make sure you're writing the value into the right field on each side:
| Concept | Pixel SDK parameter | CAPI v3 parameter |
|---|---|---|
| Cross-channel dedup id | client_dedup_id | event_id (top-level) |
| Order / transaction reference | transaction_id | order_id (inside custom_data) |
| Category for custom conversions | item_category | content_category (inside custom_data) |
If you're migrating from CAPI v2 you may see transaction_id referenced on the CAPI side too — that's the legacy v2 name. In CAPI v3 the field is order_id; see the Migration Guide for the full v2-to-v3 rename list.
What must match
For a Pixel SDK event and a CAPI event to be recognized as the same conversion, all of the following must agree:
- The pixel — the asset id in the CAPI endpoint URL must be the same Pixel ID that fired the Pixel SDK event.
- The event name —
event_namein CAPI (e.g.SIGN_UP) must match the event name passed tosnaptr('track', ...). - The identifier —
client_dedup_idin Pixel SDK andevent_idin CAPI must be byte-for-byte identical. We don't lowercase, trim, or normalize this value — it must match exactly.
Setting up deduplication
These are the four implementation steps you need to ship on your side. The example uses Google Tag Manager (GTM), but the same pattern applies to any tag manager or hand-rolled tagging.
1. Generate a unique id per conversion
A system you control — typically whichever system is already responsible for creating order numbers, transaction ids, or session identifiers — needs to mint a single unique id for each conversion event.
Good sources for this id, in order of preference:
- An existing transaction id or order id (if your conversion has one).
- An existing session id or lead id.
- A freshly generated
UUIDv4.
The id should be unique to that single conversion. If you re-use the same id for multiple distinct conversions, Snap will dedupe them and you will under-count.
If your system already produces one (not sure? ask your technical team), recycle it as both your Pixel client_dedup_id and your CAPI event_id — you don't need to invent a new identifier.
2. Expose the id to your tag manager
Make the unique id accessible to your tag manager as a variable. In GTM, this typically means pushing the value into the dataLayer when the conversion happens, and reading it back via a Data Layer Variable or Custom JavaScript Variable.
3. Inject the id into the Pixel SDK tag
Pass the variable through as client_dedup_id on the snaptr('track', ...) call:
snaptr('track', 'SIGN_UP', {
client_dedup_id: '{{ dlv - dedup_id }}',
user_email: '{{ dlv - user_email }}',
});
If you don't pass client_dedup_id explicitly, the Pixel SDK will auto-generate one client-side (prefixed with @-). That auto-generated id is only known to the browser — it cannot match anything your server sends to CAPI. You must set client_dedup_id explicitly for cross-channel deduplication to work.
4. Pass the same id as event_id in your CAPI payload
Whichever service fires your CAPI event needs the same id and must pass it as event_id:
{
"data": [
{
"event_name": "SIGN_UP",
"event_time": 1746048000,
"event_source_url": "https://example.com/signup",
"action_source": "WEB",
"event_id": "txn_abc123",
"user_data": {
"em": "..."
}
}
]
}
See the full list of fields on the Parameters page.
Field mapping recap & Custom Conversion Setup
The table below summarizes the Pixel SDK and CAPI fields that must agree for deduplication and for accurate custom-conversion reporting:
| Source | Field name | Purpose |
|---|---|---|
| Pixel SDK | client_dedup_id | Helps match against events sent through CAPI (event_id) and prevents duplicate calls by the same user. |
| CAPI | event_id | Helps match against events sent through Pixel SDK (client_dedup_id) and prevents duplicate calls. |
| Pixel SDK | transaction_id | PURCHASE-only secondary dedup key. Must match its CAPI counterpart order_id. Sent on the same snaptr('track', 'PURCHASE', { ... }) call. |
| CAPI | order_id | PURCHASE-only secondary dedup key. Must match its Pixel SDK counterpart transaction_id. Sent inside custom_data. |
| Pixel SDK | item_category | Lets you set up custom conversions on any line-of-business using the same event. Must match its CAPI counterpart content_category. |
| CAPI | content_category | Lets you set up custom conversions on any line-of-business using the same event. Must match its Pixel SDK counterpart item_category. Sent inside custom_data. |
Purchase event nuances
PURCHASE deduplication is more aggressive than the other events because (a) purchases are the most consequential conversion and (b) advertisers have historically had the hardest time deduplicating them.
A second dedup key just for PURCHASE
For PURCHASE events, Snap dedupes on two keys, not one:
client_dedup_id⇄event_id— the same cross-channel key used for every other event (up to a 48-hour window).transaction_id(Pixel SDK) ⇄order_id(CAPI) — aPURCHASE-only server-side dedup key with a window of up to 30 days.
The Pixel SDK accepts this value under the parameter name transaction_id. CAPI v3 accepts the same value under the parameter name order_id (inside the custom_data object). They are not separate fields — they are the same field with different names on either side of the integration. You must send the exact same value on both, just under each integration's local parameter name.
This means that for PURCHASE events you have two ways to get correct deduplication:
- Preferred — Set the same value on
client_dedup_id(Pixel SDK),event_id(CAPI),transaction_id(Pixel SDK), andorder_id(CAPI). This gives you a 30-day dedup window on the transaction and full cross-channel matching. This is what you should do. - Acceptable fallback — If you can only set the order/transaction id on both sides (e.g. you literally cannot inject a value into the
client_dedup_idslot of your Pixel SDK tag), Snap will still dedupe twoPURCHASEevents that share the sametransaction_id(Pixel SDK) /order_id(CAPI) on the same pixel, within 30 days. Optimization quality is lower because we won't merge user identifiers across the two events, but you won't double-count.
In your CAPI payload, the order/transaction reference goes under order_id inside the custom_data object:
{
"event_name": "PURCHASE",
"event_id": "1001-2025-05",
"event_time": 1746048000,
"event_source_url": "https://example.com/checkout/complete",
"action_source": "WEB",
"user_data": { "em": "..." },
"custom_data": {
"value": 89.99,
"currency": "USD",
"order_id": "1001-2025-05"
}
}
On the Pixel SDK side, the same value goes under the parameter name transaction_id:
snaptr('track', 'PURCHASE', {
client_dedup_id: '1001-2025-05',
transaction_id: '1001-2025-05',
price: '89.99',
currency: 'USD',
});
Notice how 1001-2025-05 appears four times across the two integrations — twice on each side, once for each dedup key.
Invalid order/transaction values are ignored
If we detect that your transaction_id (Pixel SDK) or order_id (CAPI) is a placeholder rather than a real value, we will not use it for dedup at all.
If you see your purchase counts looking too high in reporting, your dedup diagnostics in Events Manager will tell you whether your order/transaction value is being skipped because it matches one of these placeholder patterns. Fix the templating on your end and dedup will resume working within 24 hours.
One-minute price-based fallback
If a PURCHASE event has none of client_dedup_id / event_id, transaction_id, or order_id set to a valid value, Snap will fall back to deduping on the combination of user, pixel, and total price — but only within a one-minute window. This catches accidental double-fires from the same checkout page, but is far less reliable than explicit ids. Treat it as a last-resort safety net, not a deduplication strategy.
Verifying your setup
After you ship the changes:
- Wait at least 24 hours for traffic to flow through.
- In Events Manager, open your Pixel.
- Navigate to the event in question (e.g.
SIGN_UPorPURCHASE) and open Diagnostics. - Confirm:
- Deduplication rate between Pixel SDK and CAPI is high (closer to 100% is better).
- The
event_idfield is present on your CAPI events. - The
client_dedup_idfield is present on your Pixel SDK events. - For
PURCHASE,transaction_id(Pixel) andorder_id(CAPI) are present.
Anyone on your team with access to Events Manager can self-serve these checks at any time.
You can also use Snap's Request Check Tool and the test event endpoints to validate your payloads before deploying to production.
Common pitfalls
- Auto-generated Pixel SDK
cdid. If you don't passclient_dedup_idexplicitly, the Pixel SDK will mint a UUID prefixed with@-. That id is local to the browser session and cannot match what your server sends to CAPI. Always setclient_dedup_idyourself. - Different ids on Pixel and CAPI. A common mistake is to generate a fresh UUID on the server when firing the CAPI event, rather than re-using the same id that was injected into the Pixel SDK tag. They must be the same value, not two independently-minted ids.
- Mismatched
event_name. SendingSIGN_UPfrom the Pixel SDK andsignup(orSIGNUP) from CAPI will prevent matching. Use the canonical event names listed in Parameters. - Mismatched pixel id. The Pixel ID baked into your Pixel SDK init call must be the same asset id used in your CAPI endpoint URL.
- Sending the order/transaction id under the wrong CAPI field. This is a common mistake when a developer is reading the Pixel SDK docs and the CAPI docs side by side: the Pixel SDK calls this field
transaction_id, but CAPI v3 calls itorder_id(insidecustom_data). They hold the same value but have different parameter names. Sendingtransaction_idat the top level of your CAPI payload will be silently ignored, and you'll lose the 30-dayPURCHASEdedup window. - Placeholder order/transaction values. A
transaction_id(ororder_id) of{{order.order_number}}is no id at all. See the blocklist above. - Re-using one id across many conversions. Each conversion needs a unique id. Re-using the same
event_idvalue across many real conversions will collapse them all into one.
Further reading
- Conversions API Parameters — full schema including
event_id,order_id, andcontent_category. - Best Practices — short version of this guidance plus general data-hygiene rules.
- Verify Setup — test event endpoints and the Request Check Tool.
- Snap Business Help Center: