How to Set Up Conversion API (CAPI) for Meta Ads
Implement server-side tracking with CAPI to recover lost conversions.
Jay Banlasan
The AI Systems Guy
This meta ads conversion API CAPI setup guide walks through server-side event tracking from scratch. Browser-based pixels miss 20-30% of conversions due to ad blockers, cookie restrictions, and iOS privacy changes. CAPI sends events directly from your server to Meta, recovering that lost data.
I set this up for every ad account I manage. The difference in reported conversions is immediate and significant.
What You Need Before Starting
- A Meta Pixel ID and Conversions API access token
- A server or serverless function (Node.js or Python)
- Your website's form submission or purchase event data
- Python 3.8+ with
requestsandhashlib
Step 1: Get Your CAPI Credentials
Go to Meta Events Manager. Select your pixel. Go to Settings. Generate a Conversions API access token. Store it:
META_PIXEL_ID=123456789
META_CAPI_TOKEN=your_capi_token_here
Step 2: Build the Event Sender
import requests
import hashlib
import time
import os
from dotenv import load_dotenv
load_dotenv()
PIXEL_ID = os.getenv("META_PIXEL_ID")
CAPI_TOKEN = os.getenv("META_CAPI_TOKEN")
def hash_value(value):
if not value:
return None
return hashlib.sha256(value.strip().lower().encode()).hexdigest()
def send_event(event_name, email, phone=None, first_name=None, last_name=None,
source_url=None, fbc=None, fbp=None, value=None, currency="USD"):
user_data = {
"em": [hash_value(email)] if email else None,
"ph": [hash_value(phone)] if phone else None,
"fn": [hash_value(first_name)] if first_name else None,
"ln": [hash_value(last_name)] if last_name else None,
"client_user_agent": "server-side",
}
if fbc:
user_data["fbc"] = fbc
if fbp:
user_data["fbp"] = fbp
# Remove None values
user_data = {k: v for k, v in user_data.items() if v is not None}
event = {
"event_name": event_name,
"event_time": int(time.time()),
"action_source": "website",
"event_source_url": source_url or "https://yoursite.com",
"user_data": user_data,
}
if value:
event["custom_data"] = {"value": value, "currency": currency}
payload = {"data": [event]}
url = f"https://graph.facebook.com/v19.0/{PIXEL_ID}/events"
resp = requests.post(url, params={"access_token": CAPI_TOKEN}, json=payload)
return resp.json()
Step 3: Send Lead Events
For form submissions and lead generation:
def track_lead(email, phone=None, first_name=None, source_url=None, fbc=None, fbp=None):
result = send_event(
event_name="Lead",
email=email,
phone=phone,
first_name=first_name,
source_url=source_url,
fbc=fbc,
fbp=fbp,
)
print(f"Lead event sent for {email[:3]}***: {result}")
return result
Step 4: Send Purchase Events
For ecommerce or course sales:
def track_purchase(email, amount, currency="USD", phone=None, source_url=None, fbc=None, fbp=None):
result = send_event(
event_name="Purchase",
email=email,
phone=phone,
value=amount,
currency=currency,
source_url=source_url,
fbc=fbc,
fbp=fbp,
)
print(f"Purchase event sent: ${amount} {currency}: {result}")
return result
Step 5: Integrate with Your Form Handler
If you use Express (Node.js) for your form:
const express = require('express');
const app = express();
app.post('/form-submit', async (req, res) => {
const { email, phone, firstName, lastName } = req.body;
// Get Facebook click ID from cookies
const fbc = req.cookies._fbc || null;
const fbp = req.cookies._fbp || null;
// Send to CAPI
await fetch(`https://graph.facebook.com/v19.0/${process.env.META_PIXEL_ID}/events`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
data: [{
event_name: 'Lead',
event_time: Math.floor(Date.now() / 1000),
action_source: 'website',
event_source_url: req.headers.referer,
user_data: {
em: [hashSha256(email.toLowerCase())],
ph: phone ? [hashSha256(phone)] : undefined,
fn: firstName ? [hashSha256(firstName.toLowerCase())] : undefined,
fbc: fbc,
fbp: fbp,
},
}],
access_token: process.env.META_CAPI_TOKEN,
}),
});
res.json({ success: true });
});
Step 6: Verify in Events Manager
Go to Meta Events Manager. Click "Test Events." Send a test submission from your site. You should see the event appear with "Server" as the source. If you have both pixel and CAPI running, Meta deduplicates automatically using the event ID.
Check the Event Match Quality score. Aim for 6.0 or higher. Adding more user data parameters (phone, name, city) improves match quality.
What to Build Next
Add event deduplication by passing a shared event ID between your browser pixel and CAPI calls. Then build a monitoring script that checks event delivery rates daily and alerts if CAPI events drop.
Related Reading
- AI in Paid Advertising: The Complete Overview - the full ad operations landscape
- The Infrastructure Mindset - building infrastructure that compounds
- The Feedback Loop That Powers Everything - closing the data feedback loop
Want this system built for your business?
Get a free assessment. We will map every system your business needs and show you the ROI.
Get Your Free Assessment