Pixel + CAPI Deduplication Explained (2026)
How event_id plus event_name deduplication stops your Meta Pixel and Conversions API double-counting, and the breakages that inflate or drop conversions.
Updated September 2026 · Likit Sae Lee, CTO
Meta deduplicates Pixel and Conversions API events on exactly two fields: a shared event_id and an identical event_name, matched within a 48 hour window (Meta for Developers, 2023). When both match, Meta collapses the pair and keeps the event it received first. The fbp and fbc browser cookies, hashed email, and phone are matching parameters, not deduplication keys, so they do not stop double-counting on their own. Get the event_id wrong, or let the event name drift in case, and Meta counts the same purchase twice.
You wired up the Conversions API, both the browser Pixel and the server are firing, and now Meta reports more purchases than your store actually took. The fix is not turning one source off. It is the deduplication contract: the small set of rules that lets Meta receive the same event twice and still count it once. Get the contract right and running both sources is free insurance. Get it wrong and your numbers inflate, or worse, real conversions quietly vanish.
Where deduplication fits: the third pillar after install and setup
There are three jobs in modern Meta tracking, and they are easy to blur together. The first is installing the Meta Pixel so the browser reports what visitors do. The second is standing up the Conversions API so your server reports the same events when the browser cannot. The third, the one this guide owns, is the deduplication contract: the rules that let those two sources send the same event without Meta counting it twice. If you have not finished the first two, start there. The Meta Pixel setup guide covers the dataset, the base code, and standard events, and the Conversions API setup guide covers the access token and connection methods. This page assumes both are live and goes deep on what happens when they overlap.
The reason you run both at all is signal loss. Browser tracking has been eroding for years. After Apple's App Tracking Transparency, AppsFlyer put the global opt-in rate at 50% three years post launch, so half your in-app audience declines tracking. Safari's Intelligent Tracking Prevention caps JavaScript-set first-party cookies at 7 days, and tightens that to a single day when a visitor lands on a URL carrying ad-click parameters like fbclid (Apple WebKit ITP 2.1 and 2.2, 2019). The server feed survives all of that, which is why Meta reports advertisers running a Conversions API setup for web events saw an average 17.8% lower cost per result, a directional figure from Meta itself reported through PPC Land in 2026. The scale is real in any market: DataReportal's Digital 2026 report counts 23.0 million people reachable by Facebook ads in Malaysia alone in late 2025, and every one of those sessions can fire a browser and a server event for the same action.
Redundancy is the goal. But redundancy without a deduplication contract is just double-counting with extra steps. The whole point of the next sections is to make running both sources safe, so your reports match your real order count and your optimization learns from one clean conversion per sale, not two phantom ones.
![]()
The exact contract: event_id plus event_name, and nothing else
Here is the load-bearing rule the entire post hangs on. Meta's recommended deduplication method checks exactly two fields: the event_id and the event_name. That is the whole key. Per Meta's developer documentation, "a Meta Pixel's eventID must match the Conversion API's event_id" and "a Meta Pixel's event must match the Conversion API's event_name" for an event to be deduplicated (Meta for Developers, 2023). Two fields. No third.
This is where most setups quietly go wrong, because practitioners assume the other identifiers carry the load. They do not. Hashed email, hashed phone, the fbp browser ID, and the fbc click ID are matching parameters. Their job is to tell Meta which person an event belongs to, which is what drives your Event Match Quality, a different number entirely. They play no role in the recommended deduplication method. You can send a perfectly matched email on both events and still double-count, because email is not a deduplication key. The only thing that collapses a duplicate is the event_id and event_name pair.
Hold that distinction firmly, because it is the cleanest way to separate two adjacent ideas that get tangled constantly. Matching is who: which account does this conversion belong to. Deduplication is how-many: is this one conversion or two. The fbp and fbc cookies and the hashed identifiers answer who. The event_id and event_name answer how-many. The EMQ guide owns the who in depth; this guide owns the how-many. Treat them as orthogonal jobs and the rest of this falls into place.
The event_id itself is simple. On the Pixel, it is the fourth argument in the fbq track call, passed inside an options object:
fbq('track', 'Purchase', {value: 12, currency: 'USD'}, {eventID: 'EVENT_ID'});
On the Conversions API, you send the same string in the event's event_id field, with the event_name set to the same value, here Purchase. When Meta sees that pair from both sources, it knows they are one event. When it does not, it cannot.
Meta runs two deduplication methods, not one
Almost every explanation stops at "send a matching event_id." Meta actually documents two methods, and knowing both tells you what happens when the first one is incomplete.
The primary method is event_id plus event_name, described above. It is the recommended approach because it is precise: an event_id is a unique string you control, so a match is unambiguous. When the IDs and names line up, Meta collapses the pair.
The fallback method matches on event_name plus the fbp browser ID or external_id. Per Meta's documentation, if you pass fbp or external_id consistently across browser and server, Meta will compare the event_name and fbp or external_id combination and remove the duplicate (Meta for Developers, 2023). This exists for teams who cannot easily generate a shared event_id across both sides. It is genuinely weaker, and the documentation spells out why in its limitations. The fallback "only works for deduplicating events sent first from the browser and then through the server." If the server event arrives and no matching browser event landed in the past 48 hours, the server event is not discarded, even if an identical browser event shows up later. The fallback also does nothing for browser-only or server-only duplicates: two consecutive browser events with the same information are both kept, and the same goes for two server events.
The takeaway is not to rely on the fallback. It is order-dependent and fragile. Use it only as a safety net behind a real event_id, and treat the event_id plus event_name contract as the thing you actually engineer. This is one of the under-covered mechanics: most posts present a single dedup method, so they never explain why a setup that looks like it has matching cookies still double-counts when the server event happens to arrive first.
| Method | Matches on | Reliability | When it applies |
|---|---|---|---|
| Primary (recommended) | event_id plus event_name | High, an exact unique-ID match | Any order, browser-first or server-first, within 48 hours |
| Fallback | event_name plus fbp or external_id | Lower, order-dependent | Only when the browser event arrives before the server event |
Which event Meta keeps, and why it can cost you data
When a pair deduplicates, one event survives and the other is discarded. The rule is more subtle than the common shorthand suggests. Meta's documentation states that when the server and browser events "do not differ meaningfully in their content, we generally prefer the event that is received first" (Meta for Developers, 2023). So the surviving event is the one Meta got first, not strictly the browser one. The other developer guidance confirms the collapse window directly: "If we find the same server key combination (event_id and event_name) and browser key combination (eventID and event) sent to the same Pixel ID within 48 hours, we discard the subsequent events."
That has a real consequence worth planning around. If your richest first-party data, the hashed email, phone, and address that lift your match quality, lives only on one of the two events, and that event is the one Meta discards as the duplicate, you lose the enrichment along with it. The fix is not to game the arrival order. It is to put your strong identifiers on both events so that whichever one survives still carries them. The EMQ guide hints at this when it warns that a botched contract can "suppress the server event that carries your richest data," and the mechanism is exactly this retention rule: the kept event is the first received when content is equivalent, so make both events equivalent in the data that matters.
In practice this means treating dedup and match quality as one design problem. Send the event_id on both. Send the hashed identifiers on both. Send fbp and fbc on both. Then it does not matter which copy Meta keeps, because either one is complete. The reader who optimizes only the server event for EMQ and leaves the browser event thin is the one most exposed to this, since a server event arriving second can be dropped in favour of a leaner browser event that landed first.
The breakages that inflate or drop your numbers
Three named failures cause most of the trouble, and they share a trait: the integration looks healthy on the surface while the data underneath is wrong.
The first is a missing or mismatched event_id. If one side omits the event_id, or each side mints its own independently, the pair never matches and every event double-counts. This is the single most common cause of conversions reported higher than real orders. The cure is to generate the ID once and share it, covered in the next section.
The second is case and whitespace drift, a silent dedup killer. Meta matches the event name and event_id as exact strings. Purchase and purchase are different events and will not deduplicate even with an identical event_id, because the name half of the key fails. A trailing space or a casing difference in the event_id itself breaks the match the same way. Because nothing errors, the symptom is just inflated numbers with no obvious cause. Keep the event name a single shared constant and trim and normalize the event_id so it is byte-for-byte identical on both sides.
The third is fbp and fbc not being forwarded, or being hashed. These two must travel in plain text, never through SHA-256. Email and phone are hashed before they leave your server; fbp and fbc are not. The fbp is set by the Pixel in the browser and the fbc derives from the fbclid on the landing URL, so a server event has to receive them forwarded from the browser, intact and unhashed. Hash them by mistake and Meta can no longer tie the server event to the browser session, which both lowers your match quality and disables the fbp fallback deduplication. Drop them entirely and you lose two of the strongest matching signals. Neither breakage shows a red error, which is why they survive in production for months.
![]()
There is a fourth, quieter failure that runs the opposite direction. Reusing the same event_id across different buyers, from a cache bug or a hardcoded value, makes Meta over-collapse. It sees the same ID on genuinely separate purchases, decides they are duplicates, and drops the real distinct conversions. That under-counts your sales, the mirror image of double-counting, and it is rarely diagnosed because everyone is looking for inflation, not deflation. Each unique event instance needs its own unique event_id. Generate it per action, never per page template or per product.
| Symptom | Likely cause | Fix |
|---|---|---|
| Conversions reported higher than real orders | event_id missing on one side, or each side mints its own | Generate one event_id per action and send it from both sources |
| Deduplication rate sits at 0% in Events Manager | No event_id is flowing from one or both sources | Confirm the Pixel and the server both attach the same event_id |
| Same purchase listed twice in Test Events | event_id or event name not matching across the two events | Align the event_id and use the identical event name and case |
| Dedup works for Purchase but not AddToCart | Partial coverage: event_id added to some events only | Attach a shared event_id to every event you send from both sources |
| Match quality drops after dedup is enabled | The surviving event is the leaner one; rich data only on the dropped copy | Send hashed identifiers and fbp and fbc on both events, not one |
| Fewer conversions than real sales | The same event_id reused across different buyers | Mint a fresh unique event_id per individual user action |
Generating the event_id the right way: mint once, share to both
The mechanism that prevents all of the inflation failures is a single discipline: the event_id is created exactly one time per user action and the same value is carried to both the Pixel and the server. The error rivals skip explaining is minting the ID independently on each side, which guarantees a mismatch and double-counts every event no matter how clean everything else looks.
The cleanest pattern is server-first. Generate the event_id server-side at the moment the order is confirmed, store it with the order, then pass it down to the browser so the Pixel fires with the same value. Server-first is preferred because the server is the source you most want to trust: it survives the browser signal loss, it holds your richest customer data, and it is the system of record for the sale. By originating the ID where the truth lives and handing it to the browser, you avoid the race where the browser invents an ID the server never sees.
A worked sketch makes it concrete. A skincare brand running a testimonial-led funnel, the kind of eight-week-results video such advertisers typically use, takes an order at checkout. The server creates an order and an event_id together, say a UUID. It renders the confirmation page with that event_id embedded, so the Pixel's Purchase event fires with that exact eventID as its fourth argument. The same server then sends the Conversions API Purchase event with the identical event_id, the same Purchase event name, the value and currency, and the hashed email and phone it already holds. Both events reach Meta carrying the same key. Meta collapses them, keeps one, and counts a single purchase. An aesthetics clinic confirming bookings, the sort of before-and-after result such advertisers typically run, does the same on its Lead event: one event_id created when the booking is confirmed, shared to both the browser and the server call.
Two guardrails keep this honest. Make the event_id genuinely unique per instance, not a value you can accidentally reuse, so you never trip the over-collapse failure. And forward fbp and fbc from the browser into the server event in plain text, so the server copy can match the person even though it originated server-side. With the ID minted once and shared, and the cookies forwarded unhashed, the contract holds whichever direction the events flow.
Reading the diagnostic and verifying before you trust the numbers
You do not have to guess whether dedup is working. Meta gives you a feedback loop, and you should read it before you let a campaign optimize toward an event.
Start with Test Events, a tab in Events Manager. Trigger a real action on your site, a product view or a test purchase, and watch the event arrive. A correct setup shows the event coming from both the browser and the server and collapsed into a single counted conversion. The failure mode is unmissable here: if the same purchase appears twice and is not merged, your event_id is not matching across the two sources. That is your cue to check for a missing ID, a casing difference, or independently minted IDs before anything goes live.
Then read the deduplication diagnostic in Events Manager over a few days of real traffic. The signal to anchor on is the clear failure: a deduplication rate of 0% means no event_id is flowing from one or both sides and every overlapping event is being double-counted. A healthy setup shows a sane, non-zero rate rather than zero. Treat any specific percentage target you see circulating as directional rather than a hard rule, because the right number depends on how much of your traffic genuinely fires both sources. The dependable reads are the extremes: zero is broken, a stable non-zero rate with reported conversions tracking close to your real order count is healthy.
A few habits keep the contract from rotting silently. Re-run the Test Events walk-through after any change to your store platform, tag manager, or server code, because an update can quietly reset a setting or drop a parameter. Watch that the event_id is attached to every event you deduplicate, not just Purchase, since partial coverage is why dedup works on one event and fails on another. Confirm fbp and fbc are still being forwarded unhashed after any tracking change. And reconcile your Meta-reported conversions against your backend order count on a schedule, the way you would reconcile a bank statement, because a broken dedup contract is exactly the kind of failure that goes unnoticed until a quarter of reporting is already wrong. A searchable archive of real ads, such as the free Meta Ad Library or a research tool like AdPlay.ai, helps you study what creative is working, but the numbers you judge that creative on are only trustworthy once this contract is clean.
The contract is small, which is why it is so often left half-built. Two fields, one shared ID, a 48-hour window, and identifiers forwarded in the right form. Get those right and the redundancy you set up for signal recovery costs you nothing in accuracy. Get them wrong and you are optimizing against numbers that describe a store you do not have.
By the numbers
Frequently asked questions
Why are my Meta conversions higher than my actual number of sales?
The most common cause is broken deduplication. When you run both the Pixel and the Conversions API and they send the same purchase without a shared event_id, Meta receives the event twice and has no way to tell it is one conversion, so it counts two. Deduplication relies on a matching event_id and event_name arriving within 48 hours. Confirm that the same event_id is genuinely flowing from both the browser and the server, that the event name is spelled identically in the same case, and that both events land inside the window.
What is the deduplication window for Pixel and Conversions API events?
48 hours. Meta collapses a matching event_id and event_name pair only when both events are received within 48 hours of the first one (Meta for Developers, 2023). Events that arrive further apart are treated as two separate conversions. This matters for delayed or batched server events such as offline, CRM, or late-firing server calls, which can land outside the window and get counted twice even when the event_id is correct.
Does the event name have to match exactly between the Pixel and CAPI?
Yes, including case. Purchase and purchase are different events to Meta and will not deduplicate even if the event_id is identical. Standard event names use specific capitalization, for example Purchase, AddToCart, and InitiateCheckout. A custom event with a casing or spelling difference between the two sources breaks the match silently, so keep the event name a single shared constant rather than typing it in two places.
Which event does Meta keep when the Pixel and the server both send the same purchase?
When a pair deduplicates and the two events do not differ meaningfully in content, Meta generally keeps the one it received first and discards the later duplicate (Meta for Developers, 2023). It is not strictly the browser event that wins, it is whichever arrived first. The practical risk is that if your richest first-party data lives only on the later event, a successful dedup can drop it, so put your strong identifiers on both events rather than relying on one to survive.
How do I generate an event_id that matches across the Pixel and the server?
Mint the event_id once per user action and pass the identical value to both senders. Do not generate it independently on each side, because two separately created IDs will never match and every event double-counts. The cleanest pattern is to create the ID server-side when the order is confirmed and pass it down to the browser Pixel event, since the server is the source you most want to trust. On the Pixel, the event_id is the fourth argument in the fbq track call.
Do fbp and fbc deduplicate events, and should they be hashed like email and phone?
No to both. The fbp browser ID and fbc click ID are matching parameters that tell Meta which person an event belongs to, feeding a weaker fallback method, but they are not the primary deduplication keys, which are event_id and event_name. Assume fbp handles dedup and you will leave event_id off and double-count every event that fires from both sources. They are also sent in plain text, never hashed: only personal identifiers such as email and phone are SHA-256 hashed before they leave your server. Hashing fbp or fbc breaks Meta's ability to match the server event to the browser session, which weakens both your match quality and the fallback deduplication that leans on fbp. Forward them from the browser to the server exactly as the Pixel set them.
Can the same event_id be reused for different purchases?
No. Each distinct user action needs its own unique event_id. Reusing one ID across different buyers, often from a cache bug or a hardcoded value, makes Meta over-collapse genuinely separate conversions and drop the ones it thinks are duplicates. That under-counts your real sales, the mirror image of double-counting. Generate a fresh ID per event instance, not per page template or per product.
How do I test that deduplication is working?
Walk your own funnel in the Test Events tab of Events Manager and watch a single purchase arrive. A healthy result shows the event labelled as coming from both the browser and the server, collapsed into one. If the same purchase appears twice and is not merged, your event_id is not matching across the two sources. Pair that with the deduplication diagnostic in Events Manager: a 0% rate means no event_id is flowing and everything is being double-counted.
Sources
- 1.Meta for Developers, Handling Duplicate Pixel and Conversions API Events (2023)
- 2.Meta for Developers, Conversions API Parameters (event_id and customer information) (2026)
- 3.PPC Land, Meta upgrades Pixel and Conversions API to close the gap for small advertisers (2026)
- 4.AppsFlyer newsroom, ATT data findings three years post-ATT (2024)
- 5.Apple WebKit, Intelligent Tracking Prevention 2.1 (2019)
- 6.Apple WebKit, Intelligent Tracking Prevention 2.2 (2019)
- 7.DataReportal, Digital 2026: Malaysia (2026)
Keep exploring
Turn ad research into winning ads
Research the ads that work, generate the creative on-brand, and launch to Meta, all in one tool.
7-day free trial · No credit card required
