Meta Pixel and Conversions API Deduplication: Fix Double Counts

Quick fix: Send the same unique ID as eventID in the browser pixel call and as event_id in the Conversions API event, and use the exact same event name in both. Meta only deduplicates events received within 48 hours of the first event with that ID.
Meta recommends sending important events through both the browser pixel and the Conversions API. The browser covers what it can, the server covers events lost to ad blockers or page-load problems, and Meta merges the pairs. When that merge fails, every conversion seen by both channels is counted twice.
This guide explains exactly what Meta matches on, the most common reasons matching fails, and how to confirm deduplication is working in Events Manager.
How deduplication works
According to Meta's deduplication documentation, Meta decides whether a browser event and a server event are the same based on their ID and name:
| Browser pixel | Conversions API | Must be |
|---|---|---|
eventID (in the fourth argument of fbq) | event_id | Identical string |
Event name, such as 'Purchase' | event_name | Identical, including case |
Two more rules matter in practice:
- The 48-hour window. Events are only deduplicated if they arrive within 48 hours of when Meta received the first event with that
event_id. - Which copy is kept. When matching events don't meaningfully differ, Meta generally prefers the one received first.
Deduplication also has an alternative method: sending fbp and/or external_id consistently from both sides. Meta's docs note that this method only works when the browser event arrives first and the server event follows. The event_id method doesn't have that limitation, so treat it as your primary key.
A correct implementation
The browser call takes the event ID in a separate options object, not inside the custom data:
fbq('track', 'Purchase',
{ value: 129.00, currency: 'USD' },
{ eventID: 'order_10482' }
);
The matching server event uses event_id with the same value:
{
"data": [
{
"event_name": "Purchase",
"event_time": 1793092800,
"event_id": "order_10482",
"action_source": "website",
"event_source_url": "https://shop.example.com/thank-you",
"user_data": {
"client_ip_address": "203.0.113.10",
"client_user_agent": "Mozilla/5.0 ...",
"fbp": "fb.1.1793092000000.1234567890"
},
"custom_data": {
"value": 129.00,
"currency": "USD"
}
}
]
}
The event_time above is only a placeholder Unix timestamp in seconds. Use the real time of the event.
Why deduplication fails, most likely first
1. The event ID is missing on one side
The most common cause is simply that only one channel sends an ID. Platform integrations often add event_id to server events, while a hand-coded or GTM pixel fires the same event without eventID. With no ID to match, both copies count.
Check: in DevTools, filter the Network tab by facebook.com/tr, click the event request, and look for an eid= parameter. That's the browser event ID. If it's absent, the pixel call isn't passing eventID.
2. The IDs are generated separately
If the browser generates a random ID and the server generates its own random ID, they will never match. The ID must be created once and shared. Good options:
- For Purchase: the order ID or order number, which both sides already know.
- For other events: generate an ID in the browser, send it with the pixel event, and pass the same value to your server (in a form field, the request body, or a dataLayer value your server-side container reads).
Meta's server event reference describes event_id as any unique string you choose, so the format is up to you as long as both sides send the same one.
3. Event names don't match
Purchase and purchase are different names. So are a standard Purchase on the browser and a custom OrderCompleted on the server. Use Meta's standard event names on both sides with identical spelling and capitalization.
4. The ID is in the wrong place in the pixel call
A frequent bug is putting the ID inside the custom data object:
// Wrong: eventID is treated as a custom parameter
fbq('track', 'Purchase', { value: 129.00, currency: 'USD', eventID: 'order_10482' });
// Right: eventID goes in the fourth argument
fbq('track', 'Purchase', { value: 129.00, currency: 'USD' }, { eventID: 'order_10482' });
In GTM templates, look for a dedicated "Event ID" field rather than adding it as an object property.
5. The server event arrives too late
Server events sent in a nightly batch, or retried days later, can fall outside the 48-hour window. Meta also limits event_time to at most seven days in the past, and if any event in a request is older than that, the whole request returns an error. Send server events as close to real time as you can.
6. One side sends the event more than once
Deduplication pairs a browser event with its server twin. It doesn't fix a pixel that already fires Purchase twice in the browser, or a server that sends two events with different IDs for one order. Fix those duplicates at the source first. See Meta Pixel duplicate events.
7. Different pixel IDs
Deduplication works within one pixel (dataset). If the browser sends to pixel A and the server sends to pixel B, Meta treats them as unrelated events. Confirm the id= on the browser request matches the pixel ID in your Conversions API endpoint.
8. Two integrations send the same server event
It's common to find both a platform integration and a separate server-side setup (for example a server-side tag manager container or a custom script) sending Conversions API events for the same pixel. Each may generate its own ID, so the browser event can only pair with one of them and the other counts as an extra conversion. Pick one source for server events per pixel and switch the other off.
Sharing one ID between browser and server
For events without a natural ID, such as Lead or AddToCart, the simplest reliable pattern is to create the ID in the browser, use it for the pixel call, and send it along with the request your server already receives:
var eventId = 'lead_' + Date.now() + '_' + Math.random().toString(36).slice(2, 10);
fbq('track', 'Lead', {}, { eventID: eventId });
// Include the same ID in the form submission so the server
// can reuse it as event_id in its Conversions API call.
document.querySelector('#lead-form input[name="event_id"]').value = eventId;
Your server then reads that field and sends it unchanged as event_id. The exact format doesn't matter. What matters is that the value is unique per event and identical on both sides.
How to check deduplication in Events Manager
Meta's setup verification guide describes where to look:
- In Events Manager, open your pixel and go to Overview. Meta says server events should appear within 20 minutes. The Connection Method information shows whether an event arrives from the browser, the server, or both.
- Click Event Details for the event you're checking, such as Purchase, and open the Event Deduplication tab.
- Review Rate of Events Deduplicated. Higher is better, and Meta shows a warning when it's too low.
- Review Rate of Deduplication Key Usage. It shows how often each source sends each key. Low Overlap suggests keys that aren't unique or are only sent by one side.
For a faster check before waiting on aggregate numbers, use the Test Events tab: trigger an event in the browser while your server sends the same event with a test_event_code, and confirm both arrive with the same event ID. Our Test Events walkthrough covers the steps.
Deduplication problems also show up as warnings in the Diagnostics tab. Our guide to Events Manager Diagnostics warnings explains how to work through them.
How to verify the fix
- Place a test order. In DevTools, confirm the browser Purchase request carries
eid=with your order-based ID. - In your server logs, confirm the Conversions API request sent the same
event_idandevent_name, and got a2xxresponse. - Over the next days, watch the Event Deduplication tab for that event. The deduplicated rate should rise and the key overlap warning should clear.
- Compare Purchase counts in Events Manager to your actual order count for the same period. They won't match perfectly, but a gap near double points to a remaining problem.
FAQ
Do I need deduplication if I only use the Conversions API?
No. Deduplication only applies when the same event comes from both browser and server. Meta's docs note it doesn't apply to browser-only or server-only streams. Do still send event_id, since it's harmless and ready if you add the pixel later.
Can I use the same event_id for different events?
It must be unique per event occurrence. Reusing one ID across separate purchases can cause real conversions to be dropped as duplicates.
Is fbp enough to deduplicate?
It can work, but only when the browser event arrives before the server event. Sending event_id on both sides is more reliable.
Why are server events marked as received but not deduplicated?
Usually because the browser call has no eventID, the IDs are generated separately, or the event names differ. Compare one real event from each side.
