Facebook pixel event deduplication is how Meta recognises that a Meta Pixel event and a Conversions API event describe the same action, so it counts one. Meta pairs them by name and ID: when the pixel’s eventID equals the server’s event_id and the event names match within 48 hours, it keeps the first copy and discards the rest.
Get it wrong and every lead reported by both channels is counted twice. Double-counted leads lower the cost per lead Meta shows you, so a broken setup looks like a good week. Meta’s help center article about deduplication warns that artificially high conversion numbers “can mislead optimization and budget allocation decisions”.
Why you need deduplication at all
Meta recommends what it calls a redundant setup: the Conversions API alongside the Meta Pixel, sending the same events through both (Meta’s guide to handling duplicate events). The pixel relies on the browser, and the same help center article notes that browser data can be blocked or lost to browser settings or connectivity issues. The server sends what the browser drops. Meta even sets a target in its article on server event details: the Conversions API should cover 75% of your pixel events.
Redundancy creates the problem this guide is about. Send the same event from both sides and you have to tell Meta which copies belong together. Send different events from each side (PageView from the browser and Lead from the server, for instance) and there’s nothing to deduplicate.
For lead gen, take a clear line: send the Lead from the server every time, keep the pixel’s Lead as the redundant copy, and give both the same ID. The server event is the one you control, which is the whole point of server-side tracking. The browser event is the one that may or may not arrive.
How Facebook pixel event deduplication works
The rule: event_name plus event_id
Meta treats two events as identical when both conditions hold:
- the pixel’s
eventIDmatches the Conversions APIevent_id; - the pixel’s event name matches the Conversions API
event_name.
On the pixel, eventID goes in the fourth argument of the fbq call. For a Lead with no value or currency:
fbq('track', 'Lead', {}, {eventID: 'lead_3f9c2a7e-1b4d-4c8e-9a51-0d6e2f7b8c14'});
The 48-hour window, and which copy Meta keeps
Events are deduplicated only if they arrive within 48 hours of the first event Meta received with that event_id. Inside the window, Meta generally prefers the event it received first when the two don’t differ meaningfully in content. Its server event reference adds that when a browser event and a server event arrive within 5 minutes of each other, the browser event is favoured.
The same logic covers repeats from a single source. According to Meta’s guide to using the API, when the same event_id and event_name arrive more than once, Meta keeps only the first copy, which makes retries safe.
What that means in common cases (example timings):
| Case | Result |
|---|---|
Pixel Lead and server Lead, same event_id, 10 seconds apart | counted once; the browser copy is favoured |
| Same pair, server event sent 3 hours later from a queue | counted once; the first copy received is kept |
| Same pair, server event sent 3 days later | counted twice: outside the 48-hour window |
Server retries a Lead with the same event_id | counted once |
| Pixel and server each generate their own ID | counted twice |
Pixel sends no eventID at all | counted twice, unless the fallback below catches it |
The fallback: fbp or external_id
Meta has a second method. If the browser and server events carry the same event_name plus the same fbp and/or external_id, Meta compares the server event with a browser event sent earlier. Its own guide lists the limits:
- it generally works only when the browser event is sent first and the server event follows;
- “Server events will not be discarded if a browser event has not been received in the past 48 hours, even if an identical browser event arrives after the server event”;
- it doesn’t deduplicate within one source: two identical browser events, or two identical server events, are both kept.
That last point is the opposite of the event_id method. Treat fbp and external_id as a safety net and event_id as the key; it’s also the method Meta marks as recommended.
| Method | Keys | Works when | Fails when |
|---|---|---|---|
event_name + event_id (recommended) | eventID on the pixel, event_id on the server | both copies carry the same name and ID within 48 hours | the IDs differ, or one side sends none |
event_name + fbp / external_id | the same fbp and/or external_id on both sides | the browser event arrives first | the server event arrives first, or only one source sends the event |
A server Lead event with event_id
The pixel half is the fbq call above. The server half is a POST to https://graph.facebook.com/<API_VERSION>/<PIXEL_ID>/events carrying the same ID. Example payload for a Lead from a landing page form:
{
"data": [
{
"event_name": "Lead",
"event_time": 1793787302,
"event_id": "lead_3f9c2a7e-1b4d-4c8e-9a51-0d6e2f7b8c14",
"action_source": "website",
"event_source_url": "https://offer.example.com/free-quote",
"user_data": {
"em": ["10928b45363b7d849e040470c3277657f8177ef5a953a092b4302f149757a02e"],
"ph": ["75565655f639ba28fbb0a88254e0d01fad0a351049bde96a286e336219a33e5f"],
"client_ip_address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"client_user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
"fbc": "fb.1.1793787151204.IwAR2F4-dbP0l7Mn1IawQQGCINEz7PYXQvwjNwB_qa2ofrHyiLjcbCRxTDMgk",
"fbp": "fb.1.1793787151880.1684203391"
}
}
]
}
What to check in it:
event_nameisLead, written exactly as on the pixel.event_idis the string the pixel sent aseventID.event_timeis in Unix seconds (here 4 November 2026, 10:15:02 UTC). Meta rejects the whole request if any event in it is more than 7 days old.action_sourceis required, andevent_source_urlis required for website events.emandphare SHA-256 hashes of the normalised email and phone, whilefbc(which carries the fbclid),fbp, the IP and the user agent go in clear. The Event Match Quality guide covers the hashing, and _fbc and _fbp cookies, explained covers the two cookie values.
DropCPL sends the Lead event to Meta from the server with an event_id for deduplication, plus fbc and fbp. Your CPL is computed on the leads your server received, so a deduplication slip on Meta’s side never inflates the number you decide on. Start free →
Where the event_id should come from
One ID per lead, generated once, used by both sides. For a form on a landing page the simplest flow is:
- When the visitor submits, the browser generates the ID. A UUID is fine: Meta accepts any unique string.
- The pixel fires
Leadwith that ID aseventID. - The same ID travels to your server inside the form POST, as a hidden field for example.
- Your server sends the Lead through the Conversions API with that ID as
event_id, and stores it with the lead.
If JavaScript didn’t run, the pixel never fired either, so the server can generate its own ID with no risk of a duplicate. The reverse flow works too: the server creates the lead, returns its ID, and the confirmation screen fires the pixel with it. Meta’s own examples of event_id are order numbers and transaction IDs, and for events without one, “a random number (so long as the same random number is sent between browser and server events)”.
Facebook pixel event deduplication mistakes
| Mistake | What happens | Fix |
|---|---|---|
Different event names (Lead on the pixel, lead or a custom FormSubmit on the server) | no pair is found; both events count | the same standard event name, formatted identically on both sides |
event_id generated separately in the browser and on the server | two random IDs never match | generate it once and pass it to the server |
Only one side sends the ID (a plugin or a hard-coded pixel without eventID) | those events can’t be paired | find every pixel source; each one passes the shared ID |
One event_id reused for different leads (a constant, the form’s ID) | real leads arriving within 48 hours are discarded as duplicates | one ID per lead |
| Browser and server send to different pixel IDs | no deduplication across the two datasets | the same pixel ID on both sides; easy to miss when an agency runs several clients |
| Server events held in a queue for days | outside the 48-hour window, both copies count | send events as they happen; Meta asks for “ideally within an hour” |
Relying on fbp alone while the server event goes first | the server copy is kept | add event_id |
Meta’s own troubleshooting list, in the same help center article, puts event_id consistency first and asks you to confirm that event_name and event_id are formatted identically across sources.
How to verify event deduplication in Events Manager
- Test one real submission. In Events Manager, open your dataset and the Test events tab, copy the test code and send it as
test_event_codeat the top level of your server request. Submit the form on your landing page and check that the browser and server Leads both arrive and that one of them is deduplicated; Meta’s help center points to this tool to see which events are received, deduplicated or dropped. Then remove the code. Meta notes that events sent withtest_event_codeare not dropped: they flow into Events Manager and are used for targeting and measurement, so a test lead is a real Lead as far as Meta is concerned. - Read the Event Deduplication tab. Under the event’s View details, the deduplication keys table shows the share of pixel and server events received with each key, and Overlap shows the share received from both sources. If both sources report
event_idand overlap stays low, the IDs aren’t the same strings. - Don’t read Events Received as an alarm. In the Event Overview tab, Events Received is the sum of pixel and server events before deduplication, so it’s supposed to look high.
- Compare with the leads you received. Meta’s reported leads and your own count never match exactly, for reasons covered in real CPL vs platform CPL. A Meta count close to double yours, though, points at deduplication first. It’s check number three in CPL: five things to keep in mind.