Back to Blog
meta-adsconversionstracking

Meta Ads Conversion Tracking: Pixel and CAPI Guide

A developer's guide to Meta conversion tracking. Covers Meta Pixel setup, Conversions API (CAPI) implementation, event deduplication, and how to track conversions through Xylo.

Xylo Team|March 15, 2026|6 min read

Why Conversion Tracking Matters

Without conversion tracking, you are flying blind. You can see impressions, clicks, and spend -- but you cannot tie those to actual business outcomes like purchases, sign-ups, or leads. Meta's ad optimization algorithms also rely on conversion data to find more people likely to convert. Better tracking data leads to better targeting, which leads to lower costs.

Meta provides two primary mechanisms for tracking conversions: the Meta Pixel (client-side) and the Conversions API (server-side). This guide covers both, how they work together, and how to pull conversion data through Xylo's API.

The Meta Pixel

The Meta Pixel is a JavaScript snippet that runs in the user's browser. When a user visits your website and takes an action (views a page, adds to cart, completes a purchase), the pixel fires an event to Meta's servers.

Basic Setup

Add the pixel base code to your site's <head>:

<script>
  !function(f,b,e,v,n,t,s)
  {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
  n.callMethod.apply(n,arguments):n.queue.push(arguments)};
  if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
  n.queue=[];t=b.createElement(e);t.async=!0;
  t.src=v;s=b.getElementsByTagName(e)[0];
  s.parentNode.insertBefore(t,s)}(window, document,'script',
  'https://connect.facebook.net/en_US/fbevents.js');
  fbq('init', 'YOUR_PIXEL_ID');
  fbq('track', 'PageView');
</script>

Standard Events

Meta defines standard events that their optimization algorithms understand:

// Purchase event
fbq('track', 'Purchase', {
  value: 49.99,
  currency: 'USD',
  content_ids: ['SKU-123'],
  content_type: 'product',
  num_items: 1
});

// Add to cart
fbq('track', 'AddToCart', {
  value: 49.99,
  currency: 'USD',
  content_ids: ['SKU-123'],
  content_type: 'product'
});

// Lead form submission
fbq('track', 'Lead', {
  value: 10.00,
  currency: 'USD'
});

The Problem with Client-Side Only

The pixel has a significant limitation: it runs in the browser. This means it is affected by:

  • Ad blockers -- 30-40% of users block tracking scripts
  • Browser privacy features -- Safari ITP, Firefox ETP, Chrome's Privacy Sandbox
  • Cookie restrictions -- third-party cookie blocking reduces attribution accuracy
  • Page load failures -- if the page does not fully load, the pixel does not fire

These issues mean the pixel alone can miss 20-50% of actual conversions. This is where the Conversions API comes in.

The Conversions API (CAPI)

The Conversions API sends event data server-to-server. Your backend sends events directly to Meta's servers, bypassing the browser entirely. This is unaffected by ad blockers, cookie policies, or client-side failures.

Server-Side Implementation

const PIXEL_ID = process.env.META_PIXEL_ID;
const ACCESS_TOKEN = process.env.META_ACCESS_TOKEN;

async function sendConversionEvent(event: {
  event_name: string;
  event_time: number;
  user_data: {
    em?: string;    // hashed email
    ph?: string;    // hashed phone
    client_ip_address?: string;
    client_user_agent?: string;
    fbc?: string;   // click ID from _fbc cookie
    fbp?: string;   // browser ID from _fbp cookie
  };
  custom_data?: {
    value?: number;
    currency?: string;
    content_ids?: string[];
  };
  event_id?: string;  // for deduplication
}) {
  const response = await fetch(
    `https://graph.facebook.com/v22.0/${PIXEL_ID}/events`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        data: [event],
        access_token: ACCESS_TOKEN,
      }),
    }
  );

  return response.json();
}

// Example: track a purchase from your checkout handler
await sendConversionEvent({
  event_name: "Purchase",
  event_time: Math.floor(Date.now() / 1000),
  user_data: {
    em: hashSHA256(customer.email),
    ph: hashSHA256(customer.phone),
    client_ip_address: req.ip,
    client_user_agent: req.headers["user-agent"],
    fbc: req.cookies._fbc,
    fbp: req.cookies._fbp,
  },
  custom_data: {
    value: 49.99,
    currency: "USD",
    content_ids: ["SKU-123"],
  },
  event_id: `purchase_${order.id}`,  // unique ID for dedup
});

User Data Matching

The Conversions API uses customer information to match server events to Meta users. The more data you provide, the better the match rate. All personally identifiable information must be SHA-256 hashed before sending:

Parameter Description Impact on Match Rate
em Email (hashed) High
ph Phone (hashed) High
fbc Click ID cookie Very High
fbp Browser ID cookie High
client_ip_address User's IP Medium
client_user_agent User's browser Medium
external_id Your user ID (hashed) Medium

The fbc parameter (from the _fbc cookie) is the most valuable for matching because it directly ties the conversion to a specific ad click.

Deduplication: Pixel + CAPI Together

The best approach is to run both the Pixel and CAPI simultaneously. The Pixel catches client-side events quickly, while CAPI catches events the Pixel misses. But this creates a problem: if both systems fire for the same event, Meta counts it twice.

The solution is deduplication using event_id. Assign the same unique ID to both the Pixel event and the CAPI event:

// Client-side: Pixel
const eventId = generateUniqueId(); // e.g., UUID
fbq('track', 'Purchase', {
  value: 49.99,
  currency: 'USD'
}, { eventID: eventId });

// Pass eventId to your server for the CAPI call
fetch('/api/track-purchase', {
  method: 'POST',
  body: JSON.stringify({
    event_id: eventId,
    value: 49.99,
    currency: 'USD'
  })
});
// Server-side: CAPI
app.post('/api/track-purchase', async (req, res) => {
  await sendConversionEvent({
    event_name: "Purchase",
    event_time: Math.floor(Date.now() / 1000),
    event_id: req.body.event_id, // same ID as Pixel
    user_data: { /* ... */ },
    custom_data: {
      value: req.body.value,
      currency: req.body.currency,
    },
  });
});

When Meta receives two events with the same event_id and event_name, it deduplicates them and counts only one conversion.

Reading Conversion Data Through Xylo

Once your conversion tracking is set up, you can pull conversion metrics through Xylo's API. Xylo normalizes Meta's nested action arrays into flat, readable fields:

curl "https://api.xyloapi.dev/v1/campaigns?date_preset=last_7d" \
  -H "x-api-key: xylo_live_abc123" \
  -H "x-ad-account: act_123456789"

Response:

{
  "data": [
    {
      "id": "120210123456789",
      "name": "Summer Sale Prospecting",
      "status": "active",
      "insights": {
        "impressions": 142893,
        "clicks": 3847,
        "spend": 487.23,
        "conversions": 87,
        "cost_per_conversion": 5.60,
        "ctr": 2.69,
        "cpc": 0.13
      }
    }
  ],
  "meta": {
    "cached": false,
    "meta_api_version": "v22.0"
  }
}

No need to search through actions arrays or match action_type strings. Conversion count and cost per conversion are top-level fields.

Detailed Insights

For deeper conversion breakdowns, use the insights endpoint:

curl "https://api.xyloapi.dev/v1/insights?campaign_id=120210123456789&date_preset=last_7d&breakdowns=age,gender" \
  -H "x-api-key: xylo_live_abc123" \
  -H "x-ad-account: act_123456789"

This returns conversion data segmented by age and gender, helping you understand which audiences convert best.

Debugging Conversion Tracking

When conversions are not showing up, check these common issues:

  1. Event Match Quality. In Meta Events Manager, check the Event Match Quality score. Below 6.0 means your user data matching is poor. Add more parameters to your CAPI calls.

  2. Deduplication failures. If you see double the expected conversions, your event_id is not matching between Pixel and CAPI. Verify both sides use the same ID format.

  3. Delayed attribution. Meta's attribution window defaults to 7-day click, 1-day view. Conversions may take up to 72 hours to appear in reporting due to data processing delays.

  4. Domain verification. Ensure your domain is verified in Meta Business Settings. Unverified domains are limited to 8 conversion events.

Getting Started

  1. Set up the Pixel on your website for client-side tracking.
  2. Implement CAPI on your server for reliable server-side tracking.
  3. Use event deduplication with matching event_id values.
  4. Pull conversion data through Xylo's normalized API for reporting and optimization.

For API-level campaign management with conversion data, read the Meta Ads API developer guide. To automate conversion-based optimization, see our guide on Facebook ads budget optimization.

Sign up for Xylo to start pulling normalized conversion data from your Meta ad accounts.

Ready to simplify your ads API integration?

Get started with Xylo in minutes. One API key for every ad platform.