How to Set Up the Meta Conversions API on Next.js

The Meta Conversions API (CAPI) sends conversions to Meta from your server instead of the browser, so the events ad blockers and browser privacy controls strip from the client-side Pixel still reach Meta. On a Next.js app the clean setup is to keep the Pixel firing in the browser, send the same event from a Route Handler, and tie the two together with a shared event_id so Meta deduplicates them into one. Done right, you recover lost conversions without double-counting.

Why server-side at all

The browser Pixel is fragile. Around 30% of internet users run ad blockers at least some of the time (GWI via Backlinko), and those tools — along with Safari's tracking prevention and consent tooling — routinely block the Pixel before it fires. The Conversions API runs on your server, where none of that applies, so it captures conversions the Pixel misses. The recommended setup is not Pixel or CAPI; it's both, sending the same events through two paths for redundancy.

How deduplication actually works

This is the part teams get wrong. When the Pixel and CAPI both report the same purchase, Meta keeps one — but deduplication only checks event_name and event_id (Meta for Developers). It does not use email, phone, fbp, or fbc to dedupe; those identifiers are for matching the event to a Facebook profile, a separate job.

So the rule is simple: generate one unique event_id per user action and pass the identical string to both the Pixel and the Conversions API. A UUID v4 works well. Any mismatch — even different casing or a stray space — breaks deduplication and the conversion gets counted twice.

Step 1: fire the Pixel with an event ID

In the browser, generate the ID and pass it to the Pixel as eventID:

const eventId = crypto.randomUUID();
fbq("track", "Purchase", { value: 49.0, currency: "USD" }, { eventID: eventId });

Keep that eventId so your server can send the same value — for example by posting it to your own endpoint alongside the order.

Step 2: send the same event from a Route Handler

Create a server route (e.g. app/api/meta/route.ts) that calls Meta's Graph API with the matching event_id:

await fetch(`https://graph.facebook.com/v21.0/${PIXEL_ID}/events?access_token=${TOKEN}`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    data: [{
      event_name: "Purchase",
      event_time: Math.floor(Date.now() / 1000),
      event_id: eventId,                 // identical to the Pixel's eventID
      action_source: "website",
      user_data: { /* see step 3 */ },
      custom_data: { value: 49.0, currency: "USD" },
    }],
  }),
});

Run this server-side only — the access token is a secret and must never reach the browser.

Step 3: get user_data right (hashing rules)

The user_data object is what lets Meta match the event to a user, and it has strict, easy-to-miss rules (Meta for Developers):

  • Hash all PII with SHA-256 before sending — email, phone, name. Send these only with real consent.
  • Do not hash fbp and fbc. Hashing them breaks matching entirely. fbp comes from the _fbp first-party cookie; fbc is derived from the fbclid query parameter when a user arrives from a Meta ad.
  • Only set fbc when a real fbclid exists in the landing URL. Never fabricate one — invented values degrade match quality.
  • Include client_ip_address and client_user_agent from the incoming request when you can.

On Next.js, read _fbp/fbc from cookies on the server (or pass them from the client with the order), and pull IP and user-agent from the request headers in the Route Handler.

Step 4: verify in Events Manager

Open Events Manager → your Pixel → Test Events, complete a purchase, and click into the event. You should see it arriving from both the browser and the server, and a deduplication status confirming the two were merged. If you see the same purchase twice, your event_id values don't match — fix that before going live.

Common mistakes that break it

Most CAPI problems come from a short list of avoidable errors:

  • Mismatched event_id. A difference in casing or whitespace between the Pixel's eventID and the server's event_id produces two events instead of one. Generate the ID once and pass the exact string to both.
  • Hashing fbp or fbc. These are not PII and must be sent raw; hashing them silently destroys matching.
  • Fabricating fbc. Only attach it when a real fbclid is in the URL. A made-up value degrades match quality rather than improving it.
  • Leaking the access token. The token is a server secret. Keep it in an environment variable read only inside the Route Handler — never expose it to the browser bundle.
  • Sending PII without consent. Email and phone go through SHA-256, but you should only collect and transmit them where the user has actually consented.

Key things to remember

CAPI is a redundancy layer, not a replacement for the Pixel — and it still won't make Meta and GA4 agree, because the two platforms count conversions over different windows regardless of how the data arrives. Treat owned, first-party events as your source of truth, use CAPI to make Meta's own optimization signal more complete, and lean on the event_id discipline so completeness never turns into double-counting.

FAQ

Frequently asked questions

What is the Meta Conversions API?

The Meta Conversions API (CAPI) sends conversion events to Meta directly from your server rather than the browser. It runs alongside the Pixel so conversions blocked on the client side still reach Meta for measurement and ad optimization.

How does Meta deduplicate Pixel and Conversions API events?

Meta deduplication keys only on event_name and event_id. You generate one unique event_id per user action and send the identical string to both the Pixel (as eventID) and the Conversions API; any mismatch causes the event to be double-counted.

Should I hash fbp and fbc for the Conversions API?

No. fbp and fbc must be sent unhashed — hashing them breaks user matching. Only personally identifiable information like email and phone is hashed with SHA-256. Set fbc only when a real fbclid exists in the landing URL.

Will the Conversions API make my Meta and GA4 numbers match?

No. CAPI makes Meta's data more complete, but Meta and GA4 still count conversions over different attribution windows and in different ways, so a gap remains. Use owned first-party events as your source of truth.