Conversions API deduplication, done properly.
Running the Meta Pixel and the Conversions API together is now standard advice, and it is correct advice. What gets skipped is the part that makes the pair work: deduplication. Send both without it and Meta counts one conversion twice. Get the mechanism subtly wrong and you can inflate a single real conversion into dozens of recorded events, which does more damage to optimization than having no server-side tracking at all.
This is the detail that separates a CAPI integration that improves performance from one that quietly degrades it. If you are still setting up the basics, start with our Meta Pixel setup guide and come back here.
Why deduplication exists
The browser Pixel and the Conversions API describe the same real-world event from two vantage points. When someone buys, the Pixel fires Purchase from their browser and your server fires Purchase from the backend. Both are reporting one purchase. Meta needs a way to recognise that, or your Events Manager fills with phantom volume.
The reason you send both anyway is coverage. Browser events are lost to ad blockers, privacy settings and closed tabs, commonly 20 to 40 percent of them. Server events are lost when the user journey never reaches your backend, or when the identifiers are too thin to match. Together they cover each other. Deduplication is what stops that redundancy turning into double counting.
How Meta actually matches the two events
Meta deduplicates on the combination of event_name and event_id. If two events arrive with the same event name and the same event ID inside the deduplication window, Meta treats them as one event and keeps the first one it received, usually preferring the richer server-side payload for matching.
That means the entire mechanism rests on one thing: both sends must carry an identical, stable event ID derived from the conversion itself. Not from the page load, not from a timestamp, not from a random number generated at send time.
The three things that must line up
First, the event ID must be identical on both sides, character for character. Derive it from something intrinsic to the conversion: an order ID, a booking UUID, a transaction reference. Anything the browser and the server can independently compute and arrive at the same answer.
Second, the event name must match exactly. Purchase and purchase are not the same event to the matching logic. A browser Purchase and a server CompletePayment will never deduplicate, no matter how well the IDs line up.
Third, the two events must arrive close enough together. Meta's deduplication window is generous, but it is not infinite, and events replayed days later will not match a browser event that has aged out.
Failure mode 1: regenerated event IDs
The most common break. The browser generates a random event ID at fire time, the server generates its own, and the two never match. Every conversion is counted twice. Ads Manager looks like performance doubled overnight, which is exactly the kind of good news nobody investigates.
The tell is a conversion count that is close to a round multiple of your true number, and a Events Manager deduplication rate near zero. Meta reports this in the Events Manager overlap diagnostics, and it is worth checking the moment CAPI goes live rather than a quarter later.
Failure mode 2: stateless re-sending
This one is worth describing in detail, because we hit it ourselves and it is not obvious from the documentation.
We run a scheduled job that polls our booking system and forwards each booking to Meta as a Lead, so that bookings made outside the embedded widget are not lost. The first version of it re-sent every booking from the last 48 hours on every run, on the assumption that Meta's own deduplication would sort it out. The event IDs were correct and stable, so this seemed safe.
It was not. Meta deduplicated correctly for attribution, and Ads Manager showed the right number of leads. But Events Manager counts every event it receives, so one real booking arrived roughly 120 times over two days. Match quality fell to 3.3 out of 10, and the algorithm was optimizing against a signal that looked like a flood of identical low-quality leads.
The fix is not cleverer IDs, it is state. Keep a ledger of what you have already sent, look it up before every send, and fail closed by skipping when the lookup itself errors. Deduplication on Meta's side is a safety net for genuine browser and server overlap, not a licence to re-send the same event on a schedule.
Failure mode 3: identifiers that are too thin
Deduplication can work perfectly and your setup can still underperform, because matching and deduplication are different problems. A server event with nothing but an IP address and a user agent will deduplicate fine and match almost nobody.
Send hashed email as a minimum, and add hashed phone, first and last name, city, country, and the _fbp and _fbc cookies where you have them. Hash with SHA-256, lowercase and trim first, and never send raw personal data. Each additional parameter measurably lifts match quality, and match quality is what the algorithm is actually optimizing against.
How to verify it works
Four checks, in order. Use the Test Events tool in Events Manager and complete one real conversion: you should see both the browser and server event arrive, flagged as deduplicated. Check the event overlap diagnostics for a deduplication rate that is high and stable rather than near zero or near 100 percent. Compare your Events Manager conversion count against the ground truth in your own database for a fixed day, and expect them to be close. Finally, watch the event match quality score per event, and treat anything below about 6 as a parameter problem rather than a deduplication problem.
Then run the whole test twice in a row. If your server-side job is stateful, the second run should send nothing at all.
The takeaway
Deduplication is not a checkbox in a CAPI setup, it is the thing that makes the setup worth having. Derive the event ID from the conversion, keep the event names identical, keep state on anything that runs on a schedule, and enrich the server payload with as many hashed identifiers as you legitimately hold. Clean signal is worth more than more signal. If your reported conversions and your actual conversions have quietly drifted apart, that gap is usually the first thing worth fixing, and it is normally cheaper than another round of creative. A free strategy call will tell you which one you are dealing with.
Frequently asked questions
How does Meta deduplicate Pixel and Conversions API events?
Meta matches on the combination of event_name and event_id. When a browser event and a server event arrive with the same name and the same ID within the deduplication window, Meta treats them as one event.
What should the event_id be?
Something intrinsic to the conversion that both the browser and the server can compute independently, such as an order ID, a booking UUID or a transaction reference. Never a random number, a timestamp or an ID generated at send time, because the two sides will not match.
Why are my conversions counted twice?
Usually because the browser and the server each generate their own event ID, so Meta never matches them. The tell is a conversion count close to a round multiple of your real number and a deduplication rate near zero in the Events Manager diagnostics. Event names that differ in spelling or capitalisation cause the same problem.
How do you test that deduplication works?
Complete one real conversion in Test Events and check that both the browser and the server event arrive, flagged as deduplicated. Then compare the Events Manager count with your own database for a fixed day. If you send events from a scheduled job, run it twice in a row: the second run should send nothing.
What is a good event match quality score?
Treat anything below about 6 out of 10 as a parameter problem. Send hashed email at minimum, and add hashed phone, name, city, country and the _fbp and _fbc cookies where you have them, lowercased, trimmed and hashed with SHA-256.
