Zernio
APIFor you
Zernio
APIFor you
Platforms

Meta Ads

Overview

Set up

Connect

Build

CampaignsAd setsCreativesCreative libraryPreviewsBoost a postLead Gen Forms

Target

TargetingCustom Audiences

Ad types

Creative testingConversion campaignsCatalog adsMessaging & call adsClick-to-WhatsApp adsReach & frequency

Measure

InsightsMeta PixelsConversions APIAd URL tracking tags

Operate

Duplicate & lifecycleAccount & ops readsAd LibraryAd comments

Reference

Enums, limits & error codes
Dashboard
OverviewPlatformsAPI ReferenceResources
Meta Ads

Conversions API

Send server-side conversion events to a Meta pixel with POST /v1/ads/conversions, dedupe them against the browser pixel, and read Event Match Quality back.


Send conversions that happen off the website (a deal closed, a lead qualified, a trial converted) to a Meta pixel with POST /v1/ads/conversions.

They reach Meta's Graph API events endpoint. Campaigns can then optimize and report on them alongside the events the browser pixel captures.

Before you start

You need a Meta ads account connected and a pixel to send to (pixels).

There is no pixel-scoped access token to paste. Zernio uses the ads connection you already have.

Send plaintext identifiers. Every piece of PII is normalized and hashed with SHA-256 server-side to Meta's spec before anything leaves Zernio.

Find the pixel to send to

The Conversions API is Meta's server-side twin of the browser pixel. Zernio proxies it at POST /v1/ads/conversions, with destinationId set to the pixel id.

GET /v1/accounts/{accountId}/conversion-destinations lists the pixels the connected ad accounts can reach.

import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: destinations } = await zernio.conversions.listConversionDestinations({
  path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' }
});

const destinationId = destinations.destinations[0].id;

Response (200):

{
  "platform": "metaads",
  "destinations": [
    { "id": "1729525464415281", "name": "Website pixel", "status": "active" }
  ]
}

On Meta a destination is a pixel (dataset), so destinations[].id is the same value as promotedObject.pixelId on a conversion campaign.

Send a conversion event

Each event needs eventName, eventTime (Unix seconds), eventId and user.

actionSource says where it happened: web, app, offline, crm, phone_call or system_generated.

const { data: sent } = await zernio.conversions.sendConversions({
  body: {
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    destinationId: '1729525464415281',
    events: [
      {
        eventName: 'Lead',
        eventTime: 1804064400,
        eventId: 'order_abc_123',
        value: 42.5,
        currency: 'USD',
        actionSource: 'crm',
        user: {
          email: 'customer@example.com',
          phone: '+14155551234',
          firstName: 'Jane',
          lastName: 'Doe',
          country: 'US'
        }
      }
    ]
  }
});

Response (200):

{
  "platform": "metaads",
  "eventsReceived": 1,
  "eventsFailed": 0,
  "failures": [],
  "traceId": "AbCdEfGhIjKlMnOp"
}

Read eventsFailed and failures[] to catch a partial failure. Each entry carries eventIndex, the eventId it echoes back, a message and a code.

traceId is Meta's fbtrace_id, which Meta support can look up.

eventName takes a Meta standard event (Purchase, Lead, CompleteRegistration, AddToCart, InitiateCheckout, AddPaymentInfo, Subscribe, StartTrial, ViewContent, Search, Contact, SubmitApplication, Schedule) or a custom name of your own.

A commerce event carries items[] and sourceUrl as well. Zernio maps them to Meta's fields:

  • items[] becomes contents (the id, price and quantity of each row) plus num_items
  • sourceUrl becomes event_source_url
{
  "eventName": "Purchase",
  "eventTime": 1804064400,
  "eventId": "order_abc_125",
  "value": 129.5,
  "currency": "USD",
  "sourceUrl": "https://shop.example.com/checkout/complete",
  "user": { "email": "customer@example.com" },
  "items": [{ "id": "SKU-991", "name": "Trail runner", "price": 129.5, "quantity": 1, "category": "shoes" }]
}

platformData carries anything Zernio has not normalized. Its keys are shallow-merged into Meta's custom_data.

A field Zernio already builds (value, currency, contents, num_items) wins the collision.

platformData never reaches user_data, so match keys belong on user.

Match keys

The more identifiers on user, the higher Meta's match rate. Send plaintext:

GroupFields
Contactemail, phone, firstName, lastName
Location and demographicscountry, city, state, zip, dob (YYYYMMDD), gender (f or m)
IdentifiersexternalId (your stable user or device id), ipAddress, userAgent, leadId
Click ids, under user.clickIdsfbc (from the fbclid URL parameter), fbp (the _fbp cookie)

ipAddress, userAgent, fbc and fbp go to Meta unhashed, which is what Meta requires.

The highest-signal web keys are fbc, fbp, externalId, ipAddress and userAgent. Capture them at first touch and send them on every event.

Deduplication

Send a stable eventId on every event and the same value from the browser pixel. Meta dedupes the pair within a 48-hour window.

A missing or inconsistent eventId between pixel and server double-counts the conversion. This is the usual cause of an inflated report.

Batching and test mode

Send up to 1,000 events per request. Zernio chunks a larger batch.

On Meta a chunk is all or nothing. One malformed event rejects the chunk it is in, and every rejection shows up in failures[].

Test mode. Put testCode: "TEST12345" at the root of the request. The batch goes to the Test Events tab in Meta Events Manager without touching production pixel data.

It is Meta's test_event_code passed straight through.

Consent and limited data use

The batch-level consent object drives Meta's Limited Data Use handling:

{
  "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
  "destinationId": "1729525464415281",
  "consent": { "adUserData": "DENIED" },
  "events": [
    { "eventName": "Purchase", "eventTime": 1804064400, "eventId": "order_abc_124", "user": { "email": "customer@example.com" } }
  ]
}

Set either flag (adUserData or adPersonalization) to DENIED and every event in the batch is stamped with data_processing_options: ["LDU"] plus geolocation mode (country: 0, state: 0).

Geolocation mode tells Meta to work out from the event's own geo signals which US privacy law applies.

GRANTED, or no consent at all, sends the events under Meta's default processing.

The same object also drives Google's EEA and UK consent mapping on this endpoint. Split batches by consent state rather than by platform.

Event match quality

GET /v1/ads/conversions/quality reads Meta's Event Match Quality and pixel-to-server coverage back, without opening Events Manager.

Pass the pixel as destinationId.

const { data: quality } = await zernio.conversions.getConversionsQuality({
  query: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', destinationId: '1729525464415281' }
});

Response (200):

{
  "platform": "metaads",
  "rows": [
    {
      "eventName": "Purchase",
      "compositeScore": 6.2,
      "matchKeys": [{ "identifier": "email", "coveragePercentage": 80 }],
      "eventCoveragePercentage": 75
    }
  ]
}

There is one row per event name:

FieldMeaning
compositeScoreThe EMQ score from 0 to 10
matchKeys[]Per-identifier coverage
eventCoveragePercentageHow well the server events dedupe against the pixel

Web events only, because Meta's API exposes web EMQ and not app or offline.

If it fails

A rejected batch is not an error status. Meta returns 200 and reports the rejection in the body.

Branch on eventsFailed, never on the status code.

Partial failure

Response (200):

{
  "platform": "metaads",
  "eventsReceived": 0,
  "eventsFailed": 1,
  "failures": [
    { "eventIndex": 0, "eventId": "order_abc_123", "message": "Invalid event_time", "code": 100 }
  ]
}

Fix the event at eventIndex and resend the chunk. eventId makes the retry safe.

A 400 means the request body never reached Meta. accountId, destinationId or events is missing, or an event has a malformed shape.

A 403 means the connection has no ads access. On a legacy plan without ads this is expected. Ads are included with usage-based billing.

Related

  • Pixels: create the pixel these events land on.
  • Conversion campaigns: optimize an ad set toward the events you send.
  • WhatsApp: the messaging-specific conversions path.
  • Send conversions and Conversions quality: every field.
Was this page helpful?

Meta Pixels

Create, read, rename and share a Meta Pixel, manage its users and custom conversions, read stats and diagnostics, and install it on Shopify or WordPress.

Ad URL tracking tags

Read and set the click-URL parameters on a Meta ad with GET and PATCH /v1/ads/{adId}/tracking-tags, without rebuilding the creative yourself.

On this page

Before you startFind the pixel to send toSend a conversion eventMatch keysDeduplicationBatching and test modeConsent and limited data useEvent match qualityIf it failsPartial failureRelated