How Meta Ads Tracking Works
Meta Ads conversion tracking uses two parallel systems that work together:
- Meta Pixel (fbq) -- A client-side JavaScript snippet that fires browser events to Meta when users take actions on your site.
- Conversions API (CAPI) -- A server-side HTTP endpoint where your backend sends the same events directly to Meta's servers.
- Event matching -- Meta deduplicates events received from both Pixel and CAPI using an
event_idparameter, then matches them to ad clicks usingfbclid(Facebook Click ID) and hashed user data.
The data flow: a user clicks a Meta ad with an fbclid parameter in the URL. The Pixel fires a browser event with user identifiers (hashed email, phone, IP). Simultaneously, your server sends the same event via CAPI with matching event_id. Meta deduplicates the pair, matches the event to the original ad click, and attributes the conversion to the campaign.
Meta recommends running both Pixel and CAPI together (redundant tracking). The Pixel provides real-time browser signals. CAPI provides reliable server-side data that survives ad blockers and ITP. Without deduplication via event_id, you get double-counted conversions.
Since iOS 14.5 and App Tracking Transparency (ATT), browser-side tracking alone misses a significant portion of conversions. CAPI is no longer optional for accurate attribution.
Installing the Meta Pixel
Base Pixel Code
Place this in the <head> of every page. Replace YOUR_PIXEL_ID with your 15-16 digit Pixel ID from Events Manager.
<!-- Meta Pixel Code -->
<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>
<noscript><img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=YOUR_PIXEL_ID&ev=PageView&noscript=1"
/></noscript>
The fbq('init', ...) call initializes the pixel and sets a _fbp first-party cookie (browser ID). The fbq('track', 'PageView') fires a page view event on every load.
Advanced Matching (Client-Side)
Pass hashed user data at initialization to improve event match quality:
fbq('init', 'YOUR_PIXEL_ID', {
em: 'user@example.com', // Email (auto-hashed by pixel)
ph: '15551234567', // Phone with country code
fn: 'john', // First name
ln: 'doe', // Last name
ct: 'new york', // City
st: 'ny', // State (2-letter)
zp: '10001', // Zip
country: 'us' // Country (2-letter ISO)
});
The Pixel automatically SHA-256 hashes all PII fields before transmission. Pass values in lowercase, trimmed of whitespace.
Conversion Tracking
Standard Events
Meta defines 17 standard events. Use these instead of custom events when possible -- Meta optimizes delivery against recognized event names.
// Purchase
fbq('track', 'Purchase', {
value: 49.99,
currency: 'USD',
content_ids: ['SKU-123', 'SKU-456'],
content_type: 'product',
num_items: 2
});
// Lead
fbq('track', 'Lead', {
value: 0,
currency: 'USD'
});
// Add to Cart
fbq('track', 'AddToCart', {
value: 29.99,
currency: 'USD',
content_ids: ['SKU-123'],
content_type: 'product'
});
// Complete Registration
fbq('track', 'CompleteRegistration', {
value: 0,
currency: 'USD',
status: 'complete'
});
// Initiate Checkout
fbq('track', 'InitiateCheckout', {
value: 79.98,
currency: 'USD',
num_items: 2
});
Custom Events
For actions not covered by standard events:
fbq('trackCustom', 'PricingPageView', {
plan_type: 'enterprise',
source: 'blog_cta'
});
Custom events can be used as optimization targets, but Meta's algorithm performs better when optimizing against standard events with recognized semantics.
Event Deduplication
Every event sent from both Pixel and CAPI must include a matching event_id. Without it, Meta counts both as separate conversions.
// Client-side: generate a unique event ID
const eventId = crypto.randomUUID();
fbq('track', 'Purchase', {
value: 49.99,
currency: 'USD',
content_ids: ['SKU-123'],
content_type: 'product'
}, { eventID: eventId });
// Pass eventId to your server for the CAPI call
Note the parameter name difference: client-side uses eventID (camelCase in the options object), server-side uses event_id (snake_case in the API payload).
Conversions API (Server-Side)
CAPI sends events from your server directly to Meta's graph.facebook.com endpoint. This is the primary mechanism for reliable conversion data.
API Endpoint
POST https://graph.facebook.com/v21.0/{PIXEL_ID}/events?access_token={TOKEN}
Event Payload
curl -X POST \
"https://graph.facebook.com/v21.0/YOUR_PIXEL_ID/events?access_token=YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"event_name": "Purchase",
"event_time": 1709312400,
"event_id": "abc123-unique-id",
"event_source_url": "https://example.com/thank-you",
"action_source": "website",
"user_data": {
"em": ["309a0a5c3e211326ae75ca18196d301a9bdda1a04f06e4d4e92c99d13a5e5a0e"],
"ph": ["254aa248acb47dd654ca3ea53f48c2c26d641d23d7e2e93a1ec56258df7674c4"],
"client_ip_address": "203.0.113.50",
"client_user_agent": "Mozilla/5.0...",
"fbc": "fb.1.1709312000.AbCdEfGhIjKl",
"fbp": "fb.1.1709312000.1234567890"
},
"custom_data": {
"value": 49.99,
"currency": "USD",
"content_ids": ["SKU-123"],
"content_type": "product",
"order_id": "ORDER-12345"
}
}
]
}'
Key Fields
| Field | Required | Description |
|---|---|---|
event_name |
Yes | Standard event name (Purchase, Lead, etc.) or custom name |
event_time |
Yes | Unix timestamp (seconds). Must be within 7 days of current time |
event_id |
Yes (for dedup) | Matches the client-side eventID to prevent double-counting |
action_source |
Yes | Where the event occurred: website, app, email, phone_call, chat, physical_store, other |
user_data.em |
Recommended | SHA-256 hashed email (lowercase, trimmed). Pass as array for multiple |
user_data.ph |
Recommended | SHA-256 hashed phone (digits only with country code) |
user_data.fbc |
Recommended | The _fbc cookie value (contains fbclid) |
user_data.fbp |
Recommended | The _fbp cookie value (browser ID) |
user_data.client_ip_address |
Recommended | User's IP address (not hashed) |
user_data.client_user_agent |
Recommended | User's browser user agent (not hashed) |
Hashing Requirements
CAPI requires you to hash PII yourself before sending. Unlike the Pixel, it does not auto-hash.
const crypto = require('crypto');
function hashSHA256(value) {
if (!value) return null;
return crypto.createHash('sha256')
.update(value.trim().toLowerCase())
.digest('hex');
}
// Hash email
const hashedEmail = hashSHA256('User@Example.com');
// → "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"
Do not hash client_ip_address, client_user_agent, fbc, or fbp -- these are sent in plaintext.
Capturing fbclid
The _fbc cookie stores the Facebook Click ID. Capture it from the URL on landing:
function captureFbclid() {
const params = new URLSearchParams(window.location.search);
const fbclid = params.get('fbclid');
if (fbclid) {
const timestamp = Math.floor(Date.now() / 1000);
const fbc = `fb.1.${timestamp}.${fbclid}`;
document.cookie = `_fbc=${fbc};max-age=${7776000};path=/;SameSite=Lax`;
}
}
captureFbclid();
The _fbc format is fb.{subdomain_index}.{creation_time}.{fbclid}. Pass this value in CAPI calls as user_data.fbc.
Audience and Retargeting
How Meta Builds Audiences
Meta builds audiences from three data sources:
- Pixel events -- Anyone who triggered a tracked event on your site (page views, add to cart, purchase) is added to an audience pool.
- Customer lists -- You upload hashed email/phone lists. Meta matches them against user accounts.
- Engagement -- Users who interacted with your Facebook/Instagram content, videos, or lead forms.
Custom Audiences via Pixel
Custom audiences are created in Events Manager or the Marketing API. You define rules based on events and time windows:
- All visitors in the last 30 days
- Users who added to cart but did not purchase in the last 14 days
- Users who viewed a specific product category in the last 7 days
Lookalike Audiences
Lookalike audiences find users similar to your source audience. Meta analyzes the behavioral and demographic patterns of your source (e.g., purchasers) and finds statistically similar users. You set a percentage (1-10%) of a target country's population. Lower percentages are more similar to the source.
Customer List Upload (API)
curl -X POST \
"https://graph.facebook.com/v21.0/act_AD_ACCOUNT_ID/customaudiences" \
-F "name=Purchasers Q1 2026" \
-F "subtype=CUSTOM" \
-F "customer_file_source=USER_PROVIDED_ONLY" \
-F "access_token=YOUR_TOKEN"
Then add users:
curl -X POST \
"https://graph.facebook.com/v21.0/AUDIENCE_ID/users" \
-F 'payload={"schema":["EMAIL","PHONE"],"data":[["hashed_email","hashed_phone"]]}' \
-F "access_token=YOUR_TOKEN"
Aggregated Event Measurement (AEM)
After iOS 14.5, Meta introduced Aggregated Event Measurement to handle conversions from users who opt out of tracking via ATT.
How AEM Works
- Each domain can configure up to 8 conversion events, ranked by priority.
- When a user who opted out of tracking converts, Meta only reports the highest-priority event from that session.
- Lower-priority events from opted-out users are dropped from reporting.
- Events are reported with a 72-hour delay (vs. real-time for opted-in users).
Event Priority Configuration
Configure in Events Manager under Aggregated Event Measurement:
| Priority | Event | Notes |
|---|---|---|
| 1 (highest) | Purchase | Most valuable event |
| 2 | InitiateCheckout | |
| 3 | AddToCart | |
| 4 | Lead | |
| 5 | CompleteRegistration | |
| 6 | ViewContent | |
| 7 | PageView | Lowest value |
| 8 | (unused) |
Domain Verification
AEM requires domain verification. Add a DNS TXT record or upload a meta-tag to your domain root:
<meta name="facebook-domain-verification" content="YOUR_VERIFICATION_CODE" />
Domain verification is done in Business Settings > Brand Safety > Domains. Without it, your events will not be eligible for AEM reporting on iOS 14.5+ users.
Data Processing Options
Meta provides a Limited Data Use (LDU) flag for compliance with privacy regulations like CCPA. When enabled, Meta restricts how it processes data from specific users.
Setting LDU via Pixel
fbq('dataProcessingOptions', ['LDU'], 0, 0);
// [0, 0] = use IP-based geolocation to determine if LDU applies
// [1000, 0] = force LDU for all users (US)
// [] = no restrictions
Call this before any fbq('track', ...) calls.
Setting LDU via CAPI
Add to the event payload:
{
"data": [{
"event_name": "Purchase",
"data_processing_options": ["LDU"],
"data_processing_options_country": 0,
"data_processing_options_state": 0,
...
}]
}
When LDU is active, Meta will not use the event data for ad optimization or measurement beyond aggregate reporting.
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Events Manager shows "No Activity" | Pixel not loading, blocked by ad blocker, or wrong Pixel ID | Use Meta Pixel Helper browser extension. Check for JS errors in console. Verify Pixel ID matches Events Manager. |
| Duplicate conversions | Missing or mismatched event_id between Pixel and CAPI |
Ensure both Pixel and CAPI send the same event_id string for each conversion. Check for camelCase (eventID) client-side vs snake_case (event_id) server-side. |
| CAPI events showing "Low" match quality | Missing user data parameters | Include em, ph, client_ip_address, client_user_agent, fbc, and fbp in every CAPI event. The more parameters, the higher the match rate. |
| "Event received but not matched" | No fbc/fbp cookie and no hashed PII |
Capture _fbc and _fbp cookies from the browser and pass them to your server. Add hashed email/phone as fallback matching. |
event_time error |
Timestamp more than 7 days in the past or in the future | CAPI rejects events with timestamps older than 7 days. Send events within minutes of occurrence. Use Unix timestamps in seconds, not milliseconds. |
| AEM dropping events | Low-priority event from opted-out iOS user | Reorder event priorities in Aggregated Event Measurement. Only the highest-priority event per opted-out user per session is reported. |
_fbc cookie not set |
User landed without fbclid parameter or cookie was blocked |
Verify your landing pages preserve the fbclid query parameter through redirects. Check that cookie domain and path are correct. |
| CAPI 400 error: "Invalid parameter" | Sending unhashed PII | Hash all PII fields (em, ph, fn, ln, ct, st, zp, country) with SHA-256 before sending. Do NOT hash ip, user_agent, fbc, or fbp. |
| Modeled conversions appearing instead of real | Consent or ATT opt-out preventing direct attribution | This is expected. Meta models conversions for users who opted out. Improve match quality with more user data parameters to increase direct attribution. |
Meta-Specific Considerations
Consent Mode
For GDPR compliance, defer Pixel loading until consent is granted:
// Do NOT load fbevents.js until consent
if (userConsented) {
// Load pixel script dynamically
fbq('consent', 'grant');
fbq('init', 'YOUR_PIXEL_ID');
fbq('track', 'PageView');
} else {
fbq('consent', 'revoke');
}
Alternatively, use Meta's built-in consent mode:
fbq('consent', 'revoke'); // Before init, blocks all tracking
// ... later, when user consents:
fbq('consent', 'grant'); // Resumes tracking
Attribution Windows
Meta Ads defaults:
- Click-through: 7 days (configurable: 1 or 7 days)
- View-through: 1 day (configurable: 0 or 1 day)
Post-iOS 14.5, the maximum click-through window was reduced from 28 days to 7 days. View-through attribution is only available for users who have not opted out of tracking.
First-Party Cookie Domain
The _fbp cookie is set on your domain as a first-party cookie. The _fbc cookie stores the click ID. Both survive third-party cookie blocking since they are first-party, but they can be cleared by ITP (Safari limits first-party cookies set by JavaScript to 7 days).
To mitigate ITP: set cookies server-side with Set-Cookie headers (which are not subject to the 7-day cap), or implement CAPI to ensure conversions are still attributed when cookies expire.
Related Guides
- Install Meta Pixel -- Platform-specific installation instructions
- Meta Ads Event Tracking -- Complete standard and custom event reference
- Server-Side vs Client-Side -- When to use Pixel, CAPI, or both
- Troubleshooting and Debugging -- Diagnosing tracking issues
- Meta Ads Integrations -- Connecting with CRMs and e-commerce platforms