Meta Conversions API (CAPI)
A server-to-server channel that sends conversion events directly from your website's backend to Meta, so purchases and leads still register when the browser pixel is blocked or the cookie is gone.
The Meta Pixel is JavaScript. It runs in the visitor's browser, and a great deal of what modern browsers and devices do is specifically designed to stop scripts like it from working. Content blockers strip it before it loads. Safari's Intelligent Tracking Prevention caps how long its cookie survives. Apple's App Tracking Transparency prompt removes a large share of iOS users from browser-side attribution entirely. And on a poor mobile connection the request simply never completes.
Every one of those is an event that really happened — a real form fill, a real purchase — which Meta never hears about. The consequences are not merely reporting cosmetics:
- Optimisation degrades. Campaign delivery is trained on the conversions it receives. Feed it a systematically incomplete sample and it learns to find the kind of person whose browser lets the pixel through, not the kind of person who buys.
- Reported cost per lead is overstated, because spend is counted in full and conversions are not.
- Retargeting and lookalike audiences shrink and skew towards the least privacy-protected segment of your market.
The Conversions API closes that gap by moving the event server-side. When a lead form is submitted, your server — which no content blocker can reach — posts the event to Meta's Graph API directly, carrying the same event name, value and customer parameters the pixel would have sent.
Deduplication is the part that goes wrong
In practice you run both channels at once: the pixel for its rich browser signals, and
the API for reliability. That means most conversions are reported twice, and Meta has to
recognise the two reports as one event. It does that by matching the
event_name together with an event_id that you generate and send
identically on both sides.
Get the event_id wrong — regenerate it server-side instead of passing
through the one the browser used — and nothing errors. The events simply both count.
Your dashboard shows conversions roughly doubling, cost per acquisition appears to halve,
and the optimiser is being trained on inflated data. It is a silent failure, which is why
it is worth testing deliberately rather than assuming.
Match quality
A server event arrives without the browser context that identifies a person, so you
supply that yourself: email, phone number, name, city and postcode, each hashed with
SHA-256 before it leaves your server, plus the fbc and fbp
cookie values and the event_source_url where you have them. Meta reports the
result as an Event Match Quality score in Events Manager. More correctly-normalised
parameters mean more events attributed to a real person; fewer means the event is received
but cannot be credited to anyone.
Hashing is done by you, not by Meta — raw email addresses should never be transmitted. Normalisation matters as much as hashing: values trimmed and lowercased, and phone numbers in international format, or the hash will not match.
Worked example
A visitor arrives from a Facebook ad and submits an enquiry form.
- The browser fires
fbq('track', 'Lead', {...}, {eventID: 'lead_8f31c2'}). - The same request posts the form to your server, carrying that same
lead_8f31c2value. - Your server saves the lead, hashes the submitted email and phone, and posts a
Leadevent to the Conversions API withevent_id: 'lead_8f31c2'. - Meta receives two reports of the same event, sees a matching name and id, and counts one.
If the visitor had an ad blocker, step 1 never happened — and step 3 still reported the lead. That is the entire point of running both.
Common mistakes
- Generating the event ID on the server. It has to originate in the browser and be passed through, or the two copies never match and every conversion is double-counted.
- Sending raw email addresses or phone numbers. Customer parameters must be SHA-256 hashed before transmission, and normalised first.
- Firing the event from the form's success handler instead of the server. That is still browser-side. It is blocked by the same things the pixel is, and gains nothing.
- Leaving the test event code in production. Events keep flowing to the test stream and never reach the live dataset.
- Assuming a rise in reported conversions is real. After switching CAPI on, verify deduplication in Events Manager before reading the numbers as growth. A broken setup looks exactly like a successful one.
Need this handled properly? See how we run it →