Direct answer: Run both. Meta calls the Pixel plus the Conversions API a redundant setup and recommends it. Send the same key events from the browser and the server, and give each pair the same event name and event ID so Meta counts it once. Most “my numbers doubled” or “my sales vanished” problems come from a mismatch in one of those two fields, which the checklist below walks through.
What a matching pair looks like
One purchase, order 1042, sent twice. Example values only.
Browser (Pixel):
fbq('track', 'Purchase',
{ value: 129.00, currency: 'USD' },
{ eventID: 'order-1042' }
);
Server (Conversions API event):
{
"event_name": "Purchase",
"event_id": "order-1042",
"event_time": 1793750400,
"action_source": "website",
"user_data": { "em": ["<sha256 of email>"] },
"custom_data": { "value": 129.00, "currency": "USD" }
}
Same name, same ID, so Meta keeps one. Note the Pixel field is eventID and the server field is event_id. Different spelling, same value.
Troubleshooting checklist
Work top to bottom. Rows 1, 2 and 5 follow Meta’s deduplication docs; row 7 follows Meta’s best practices. Rows marked likely cause (our experience) are our checklist, not Meta statements.
| # | Check | Bad example | What you’ll see | Fix |
|---|---|---|---|---|
| 1 | Event names match exactly | Pixel Purchase, server purchase or order_complete | Two separate events, double counting | Use one exact name on both sides |
| 2 | IDs are the same value | Pixel order-1042, server 1042 | Double counting | Build both from the same source (order ID) |
| 3 | Likely cause (our experience): ID isn’t empty or a placeholder | eventID: '' or eventID: '{{order_id}}' (unfilled) | Dedup fails or many events collapse into one | Check the rendered page, not the template |
| 4 | Likely cause (our experience): ID is unique per event | Every purchase sends eventID: 'purchase' | Sales undercount | Use order ID or a random ID per event |
| 5 | Pixel ID passed in the right spot | ID inside the data object instead of the options object | Pixel event has no ID | Put eventID in the 4th argument as above |
| 6 | Likely cause (our experience): Both sides actually fire | Server fires, Pixel blocked on the thank-you page | Fine for counting, but check the browser side is still live | Test in Events Manager |
| 7 | Server event has customer info | user_data only has IP | Low match quality in Events Manager | Add hashed email or phone where you have consent |
| 8 | Likely cause (our experience): Checked after the last site change | New checkout theme went live last week | Events quietly stopped | Re-test after every checkout or theme change |
How to test
- In Events Manager, open your data source and use Test Events.
- Place a test order and note the order ID.
- Look for the event from both browser and server, and confirm both carry the same event name and ID.
- If you see two separate Purchase events, go back to rows 1 to 5.
Event match quality
Meta’s Conversions API best practices point to event match quality in Events Manager and say better match quality can increase additional conversions reported. The usual fix is sending more customer information parameters with server events.
Setting it up
If your site runs on a common commerce or website platform, check for a ready-made connection first. Meta says it “partners with leading technology providers” to connect websites, apps, and offline systems to its business tools, and lists these as partner integrations in Events Manager. A custom build makes sense when you need control over which fields are sent.
If tracking is right and sales are still low
Look at the funnel: page speed, shipping costs that show up late, missing reviews or return policies, and ads that don’t match the landing page.
FAQ
Do I need both the Pixel and the Conversions API? Meta recommends both, as a redundant setup.
Will running both double count sales? Not if each pair shares the same event name and event ID.
What should I use as the event ID? Something unique to that event that both sides can read, such as the order ID.
Why is it eventID in one place and event_id in the other? That’s how the Pixel and the API name the field. The value must match.




