← All posts

Server-side CAPI deduplication, explained

How Meta keeps one copy of a purchase: event_id matching, the 48-hour window, the fallback when IDs are missing, and how to verify it in Test Events.

Meta keeps exactly one copy of a conversion when your browser pixel and your server events agree on two things: the same event name and the same event_id, arriving within 48 hours of each other. Everything else — timestamps, page URLs, user parameters — helps Meta match events but does not, by itself, merge them. When the two sides disagree, Meta does not pick a winner: it counts both.

What has to line up on both sides

Four fields, and each one has a different job. The first is the merge key; the rest must simply be consistent.

What the browser and server copies must share
What the browser and server copies must share
FieldBrowser copyServer copyMerge role
event_idGenerated in the browserMust reuse the browser's valuePrimary key — identical values count once
event_namepurchasepurchaseMust match on both sides
action_sourcewebsitewebsiteMust match on both sides
event_source_urllocation.hrefReconstructed from the payloadSend the same value — Meta uses it when pairing

The server side never invents its own event_id. It reads the one the browser generated and stamps it on the server copy — that single decision is what makes deduplication work.

When the two layers disagree

If the IDs match, Meta keeps one purchase and discards the duplicate. If they don't, both events count: a store doing 200 purchases a week suddenly reports 400, ROAS reads double, and Meta's optimization trains on revenue that never happened. When the model corrects for the phantom volume, performance drops and the dashboard still looks green.

The usual suspects: a server library minting fresh IDs, an old integration still running next to a new one, or a theme snippet and an app both firing purchases with different IDs. See Meta pixel not firing on Shopify for the full list of ways a pixel setup rots.

The 48-hour window, and who wins a tie

Both copies must reach Meta within 48 hours of each other. Server retries after an outage, a queue that drains slowly, or a backfilled import can push the server copy past the window — each event then stands on its own and the purchase counts twice.

When both arrive at the same moment, Meta's tie-break favors the browser event. In practice that matters less than it sounds: the outcome is one counted conversion either way, as long as the IDs match.

If you don't send an event_id

Deduplication doesn't stop — it gets vaguer. Meta falls back to pairing events on shared identifiers (fbp, fbc) and consistent event parameters. The catch: the fallback only catches browser events that arrived before the server event, and any event sent twice from the server side can slip through and count again.

Two paths to deduplication
Two paths to deduplication
Dedup pathWith event_idWithout event_id
Matching ruleShared event_name + event_idfbp/fbc plus shared parameters
Window48 hoursBest effort
Server event arriving firstStill deduplicatedMay count twice
VerdictDefault — always send oneOnly when an ID is genuinely impossible

How to verify it's working

Open Events Manager → Test Events, fire a test purchase, and look at the result row: if the browser and server copies show as one deduplicated event, you're done. If you see two separate events, the IDs don't match — fix that before the next dollar of ad spend, because right now every conversion is counting twice.

That verification step matters most right after you enable a new server-side tool. Meta's own guidance and every credible setup guide say the same thing: don't assume the pairing works — watch the first events arrive.

For what a broken setup costs you in practice, see Meta's free 1-click CAPI vs Headless.

Frequently asked questions

How do browser and server events deduplicate?

Meta keeps one copy when both events carry the same event name and the same event_id, and the two copies arrive within 48 hours of each other. When they land at almost the same moment, Meta prefers the browser event.

Can I deduplicate without an event_id?

Partially. Meta documents a fallback that pairs events on shared identifiers like fbp and fbc, but it only catches browser events that arrived before the server event. Without an event_id, delayed events and re-sends can count twice. Always send one.

What breaks deduplication?

Three things: the server side generating its own event_id instead of reusing the browser's, two tracking tools sending the same purchase with different IDs, and the two copies landing more than 48 hours apart. The symptom is doubled purchases in Events Manager and a ROAS that reads too high.