Skip to main content

First Party Data

Meta Event ID Deduplication: How the Pixel's eventID and the Conversions API's event_id Count One Purchase Once

Event ID deduplication is how Meta recognizes that a Meta Pixel event from the browser and a Conversions API event from your server describe the same action. When the pixel's eventID matches the server's event_id, the event names match and both reach the same pixel within 48 hours, Meta keeps one copy and discards the other. For a Shopify purchase, the ID both sides already know is the order ID.

For how the Conversions API itself works, start with our Meta CAPI and first-party data guide. This post goes one level down: the mechanism step by step with code, Meta's fallback method and its limits, which Shopify ID to use, how to check the result in Events Manager, sending Shopify orders through Zapier, and what deduplication can't fix. We checked every Meta, Shopify and Zapier detail against their own documentation on October 24, 2026.

Why does Meta need deduplication?

Meta recommends sending the same events from the browser pixel and from the Conversions API, a setup it calls redundant. The server copy covers events the browser loses to ad blockers, privacy settings and pages that never finish loading. The price is that every purchase reaches Meta twice.

Meta's guide to Conversions API deduplication says that advertisers with a redundant setup must give Meta a way to tell distinct events from overlapping ones. Without one, both copies count. An event you send from only one place, such as a Purchase sent only from the server, needs no deduplication.

The guide offers two methods: a shared event ID, which Meta recommends, and a fallback that compares the browser ID (fbp) or your own ID for the visitor (external_id).


How does event ID deduplication work, step by step?

1. Create one ID when the action happens. One purchase, one ID. Meta's server event parameters describe event_id as any unique string you choose and name an order number or transaction ID as good candidates.

2. Send it from the browser as eventID. It goes in the fourth argument of the pixel's track call:

fbq('track', 'Purchase', {value: 129.00, currency: 'USD'}, {eventID: '5871234567890'});

With several pixels on a page, fbq('trackSingle', 'PIXEL_ID', 'Purchase', {...}, {eventID: '...'}) sends to one of them with the same option, and the image pixel takes the ID as an eid URL parameter.

3. Send the same value from the server as event_id, with an event_name spelled exactly like the browser event. The body is a POST to https://graph.facebook.com/{API_VERSION}/{PIXEL_ID}/events, per Meta's Using the API page:

{
  "data": [{
    "event_name": "Purchase",
    "event_time": 1792843200,
    "event_id": "5871234567890",
    "action_source": "website",
    "event_source_url": "https://yourstore.com/",
    "user_data": {
      "em": ["62a14e44f765419d10fea99367361a727c12365e2520f32218d505ed9aa0f62f"],
      "client_ip_address": "203.0.113.7",
      "client_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
      "fbp": "fb.1.1792842000000.1098115397"
    },
    "custom_data": {"value": 129.00, "currency": "USD"}
  }]
}

4. Meta pairs the two. The pixel's eventID must equal the server's event_id, and the pixel's event name must equal event_name, for events sent to the same pixel ID.

5. The first copy wins for 48 hours. If the same ID and name reach the same pixel again within 48 hours of the first event with that ID, Meta discards the later copies. Meta's guide says it generally keeps the event it received first when the two don't differ meaningfully, and its parameter reference adds that when the browser and server copies arrive within five minutes of each other, it favors the browser copy.

What has to matchBrowser (Meta Pixel)Server (Conversions API)
Deduplication keyeventID, the fourth fbq argumentevent_id
Event name'Purchase' in the track call"event_name": "Purchase"
PixelThe ID in fbq('init')The ID in the /events URL
TimingEither copy can arrive firstWithin 48 hours of the first copy

Two consequences follow. Retries are safe: Meta's Using the API page says that when the same event_id and event_name arrive more than once, it keeps only the first copy, so resending a failed batch with the original IDs doesn't double count. Delays aren't: a server event's event_time can be up to seven days old, but if it reaches Meta more than 48 hours after the browser copy, the two aren't paired.


What is the fbp and external_id fallback, and where does it fail?

Meta's second method compares event_name plus fbp and/or external_id across the browser and server copies. fbp is the browser ID from the _fbp cookie; external_id is your own ID for the visitor, such as a user or loyalty ID, which Meta's customer information parameters page says to send in the same format everywhere. In the pixel, it goes into the init call:

fbq('init', 'PIXEL_ID', {external_id: 'CUSTOMER_ID'});

Meta's guide lists two limits:

  • Browser first, then server. It generally works only when the browser event arrives first. A server event isn't discarded if no browser event arrived in the previous 48 hours, even when an identical browser event turns up afterwards.
  • No deduplication within one source. Two consecutive server events with the same information are both kept, and so are two browser events.
Event ID and event namefbp or external_id
Meta's statusRecommendedFallback
Arrival orderEitherBrowser first, then server
Two server copies of one eventLater copy discarded if the ID matchesBoth kept
What you have to sendThe same ID from both sidesThe same fbp or external_id from both sides

Our take. Give every event you send twice an event ID. Send fbp and external_id too, since they help matching, but don't count on them to catch duplicates.


Which event ID should a Shopify store use?

A good event ID passes three tests:

  • Unique per action. Meta's example: two purchases with order numbers 123 and 456 each need their own ID, and the browser copies must carry the same numbers, so Meta sees two purchases, not four.
  • Known to both sides at the moment each one sends.
  • Identical as a string. 5871234567890 and gid://shopify/Order/5871234567890 don't match.

That rules out a customer ID or email (a customer who orders twice in two days would lose the second purchase, because Meta discards a repeated key within 48 hours), a timestamp (the browser and the server record different times) and any random value each side generates on its own.

For Purchase, use the order ID. In the browser, Shopify's checkout_completed event exposes it as event.data.checkout.order.id, and Shopify says that ID is null for every other event. On the server, the same order arrives through an order webhook or the Admin API. Shopify's REST Order resource shows a numeric id alongside admin_graphql_api_id: "gid://shopify/Order/…", and the GraphQL Order object has the number as legacyResourceId. Log what your browser code receives on a test order and make the server send exactly that string. The browser event also carries a checkout token, but the REST resource marks the order's checkout_token as deprecated, so the order ID is the safer key.

A Shopify custom pixel can send the browser copy. Shopify's custom pixel guide puts them under Settings > Customer events, says they're unsupported and need JavaScript knowledge, and tells you to remove existing pixel code first so events aren't counted twice. With Meta's base code loaded in the pixel:

analytics.subscribe('checkout_completed', (event) => {
  const checkout = event.data.checkout;
  fbq('track', 'Purchase', {
    value: checkout.totalPrice?.amount,
    currency: checkout.currencyCode,
  }, {
    eventID: String(checkout.order?.id),
  });
});

Shopify says checkout_completed fires once per checkout, typically on the Thank you page, or on the first upsell page when you run post-purchase offers. If that page fails to load, it doesn't fire at all, which is the case the server copy exists for.

Events without an order have no natural ID. Meta's parameter reference says a random number works as long as the browser and server send the same one, so generate it in the browser, fire the pixel with it and pass it to your server with the event.

EventID both sides can shareBrowser gets it fromServer gets it from
PurchaseOrder IDcheckout_completed: checkout.order.idOrder webhook or Admin API
AddToCart, InitiateCheckout, ViewContent, LeadA random ID made in the browserGenerated when the event firesSent to your server with the event

How do you check that deduplication is working?

  1. Place a real test order. Adding a test_event_code lets you watch server events arrive in Meta's Test Events tool, but Meta says those events aren't dropped: they flow into Events Manager and are used for targeting and measurement. Either way, the order counts as real data, so cancel it in Shopify afterwards.
  2. Read the browser copy's ID. In your browser's developer tools, filter network requests for facebook.com/tr and read the eid value; our Meta Pixel Helper guide shows where. Compare it with the event_id in your server logs. They should be the same string.
  3. Open the pixel's Overview in Events Manager. Meta's verification guide says it counts events before deduplication and shows the connection method for each event; Purchase should show both Browser and Server. Meta says events should appear within 20 minutes.
  4. Open Purchase, then Event Details, then the Event Deduplication tab. It shows the rate of events deduplicated from each source, with a warning when the rate is too low, and the rate of deduplication key usage. Overlap is the share of events whose key arrived from both sources, measured against the source with fewer events. Meta says low overlap means one or both sources send keys that aren't unique, or only one source sends a key.
  5. Compare with Shopify. Put Meta's purchases next to Shopify's orders for the same day, time zone and attribution window. If Meta still shows more, our guide to Meta reporting more purchases than Shopify covers the causes that aren't duplicates.

Because overlap is measured against the smaller source, a store whose server sends more purchases than the browser (as it should) can still show high overlap, as long as every browser purchase has a server copy with the same ID.


Can you send Shopify orders to the Conversions API through Zapier with an event ID?

Yes, if both sides can carry the same ID. Zapier's Facebook Conversions app has four actions (Send Purchase Event, Send Lead Event, Send Funnel Event and Send Other Event), and its Shopify and Facebook Conversions page includes a template that sends new paid Shopify orders as purchase events. Zapier's setup guide says you need full control of the business assets and must add Zapier as a partner to record events for a Meta Pixel.

Zapier's public pages don't list the action's input fields, so check them as you build the Zap:

  1. Map the Shopify order ID into the Send Purchase Event step's event ID field.
  2. Send the same order ID from the browser as eventID, for example with the custom pixel above. If the browser Purchase comes from a pixel whose event ID you can't set, the Zap's events can't be paired with it, and Meta will usually count each order twice.
  3. No event ID field? Use Webhooks by Zapier to POST the JSON body above to Meta's /events endpoint yourself.
  4. Watch the clock. Zapier labels each Shopify trigger as instant or polling, and polling triggers check for new data every 15 minutes on the Free plan. That fits inside 48 hours; a Zap that stalls for days doesn't.

Our take. A Zap is a workable stopgap for a store with no other server-side sender. If Shopify's Facebook & Instagram app is set to Enhanced or Maximum, it already sends purchases from Shopify's servers (what each level sends), and the Zap becomes a second server-side sender.


What can't event ID deduplication fix?

Deduplication can't reconcile senders that never agreed on an ID.

ProblemWhy Meta counts it wrongFix
Two server-side senders on one pixel, such as Shopify's app plus another tool, or a Conversions API Gateway left connectedEach tool creates its own IDs, and the fallback never pairs two server eventsOne server-side sender per pixel
Copies more than 48 hours apartThe window has closedSend server events promptly and clear stalled queues
Different event namesPurchase and a custom name don't matchUse the same name on both sides
Copies sent to different pixelsKeys are matched per pixel IDSend both copies to the same pixel
One ID reused across real actionsMeta discards a real later purchaseUse an ID unique to each action
Extra Purchase events that aren't copies, such as renewalsThey aren't duplicatesDecide which purchases Meta should receive

The first row is the one we find most often in audits. Our guide to Meta attribution pitfalls and event duplication walks through finding the extra source.


How does Upstack set event IDs?

With Upstack's default Browser + Server tracking method, Upstack loads Meta's pixel on your store and sends the same events from its servers, and both copies carry the same Upstack event ID, so Meta keeps one (Upstack's tracking method docs). For purchases, Upstack's JavaScript SDK derives the event ID from the order ID when you don't pass one (SDK reference). It can't pair events from another sender, so if Shopify's app or another tool also sends server events to the same pixel, turn that one off.


Frequently asked questions

Is event_id required in the Conversions API?

No. Meta marks it optional but recommends it for deduplication. Without it, or with different IDs on each side, a purchase sent from both the pixel and the server counts twice unless the fbp or external_id fallback catches it, and that only works when the browser copy arrives first.

Does Meta deduplicate two server events with the same event_id?

Yes. Meta's Using the API page says that when it receives the same event_id and event_name more than once, it keeps only the first copy. Two different tools rarely generate the same IDs, though, so this protects retries from one sender, not a second sender.

How long does Meta remember an event ID?

For deduplication, 48 hours from the first event it receives with that ID. A copy that arrives later is counted as a new event.

Can I use the Shopify order number as the event ID?

Meta suggests order numbers, and any order identifier works if the browser and server send it as the same string. On Shopify, the order ID is the one both the checkout_completed event and your server have.


See how Upstack Signal sends browser and server events with one shared event ID.

About the author

Michael Alt

Founder & CEO, Upstack Data

Michael Alt is the founder and CEO of Upstack Data, the server-side tracking, identity resolution and attribution platform for Shopify and DTC brands.

Want similar results?

Start free for 21 days and see what clean signal does for your performance. No card, no contract.