Facebook Analytics & Insights
The Facebook Insights API provides comprehensive performance data for campaigns, ad sets, and ads with support for custom date ranges, breakdowns, and aggregations.
Endpoints Overview
| Endpoint | Method | Purpose |
|---|---|---|
/v1/e/facebook/insights/:account_id | GET | Get account-level aggregated insights |
/v1/e/facebook/insights/:account_id/campaigns | GET | Get campaign-level insights |
/v1/e/facebook/insights/:account_id/adsets | GET | Get ad set-level insights |
/v1/e/facebook/insights/:account_id/ads | GET | Get ad-level insights |
Core Concepts
Insights Levels
Facebook provides insights at different hierarchical levels:
graph TD
A[Account Level] --> B[Campaign Level]
B --> C[Ad Set Level]
C --> D[Ad Level]
style A fill:#e1f5ff
style B fill:#fff4e1
style C fill:#f0e1ff
style D fill:#e1ffe1
Time Ranges
All insights endpoints support date filtering:
{
since: '2024-01-01', // Start date (YYYY-MM-DD)
until: '2024-01-31' // End date (YYYY-MM-DD)
}
// Automatically converted to Facebook format:
{
time_range: {
since: '2024-01-01',
until: '2024-01-31'
}
}
Attribution Windows
Facebook tracks conversions within attribution windows:
- Click-through: 1, 7, or 28 days after ad click
- View-through: 1 day after ad impression
// Use account's attribution settings (recommended)
{
use_account_attribution_setting: true;
}
Account-Level Insights
Get Account Summary
Retrieves aggregated performance metrics across all campaigns in an ad account.
Endpoint: GET /v1/e/facebook/insights/:account_id
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
account_id | String | Yes | Facebook Ad Account ID (e.g., act_123456789) |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
since | String | Yes | Start date (YYYY-MM-DD) |
until | String | Yes | End date (YYYY-MM-DD) |
campaignids | String | No | Comma-separated campaign IDs to filter |
adsetids | String | No | Comma-separated ad set IDs to filter |
adids | String | No | Comma-separated ad IDs to filter |
campaignstatus | String | No | Filter by campaign status (e.g., ACTIVE,PAUSED) |
adsetstatus | String | No | Filter by ad set status |
adstatus | String | No | Filter by ad status |
fields | String | No | Additional fields to include |
account_id | String | No | DashClicks sub-account ID |
Example Request:
GET /v1/e/facebook/insights/act_123456789?since=2024-01-01&until=2024-01-31&campaignstatus=ACTIVE
Authorization: Bearer eyJhbGc...
Response:
{
"success": true,
"message": "SUCCESS",
"data": {
"impressions": 125000,
"clicks": 3500,
"spend": 1250.5,
"reach": 87500,
"conversions": 175,
"total_conversion_value": 8750.0,
"roas": 7.0,
"cpm": 10.0,
"cpc": 0.36,
"account_currency": "USD",
"action_breakdown": {
"offsite_conversion.fb_pixel_purchase": 120,
"offsite_conversion.fb_pixel_lead": 55,
"link_click": 3500,
"landing_page_view": 2800,
"post_engagement": 450
}
}
}
Summary Metrics Explained:
const summaryMetrics = {
// Core Metrics
impressions: 'Total ad impressions',
clicks: 'Total clicks on ads',
spend: 'Total amount spent (account currency)',
reach: 'Unique users who saw ads',
// Conversions
conversions: 'Total conversion events (from results field)',
total_conversion_value: 'Total monetary value of conversions',
// Efficiency
roas: 'Return on ad spend (conversion_value / spend)',
cpm: 'Cost per 1000 impressions',
cpc: 'Cost per click',
// Currency
account_currency: 'Currency code (USD, EUR, etc.)',
// Action Breakdown
action_breakdown: {
description: 'Aggregated counts by action type',
example: {
'offsite_conversion.fb_pixel_purchase': 'Purchase events',
'offsite_conversion.fb_pixel_lead': 'Lead events',
link_click: 'Link clicks',
landing_page_view: 'Landing page views',
},
},
};
Filtering Logic:
// Campaign ID filtering
if (req.query.campaignids) {
filtering.push({
field: 'campaign.id',
operator: 'IN',
value: req.query.campaignids.split(','),
});
}
// Status filtering
if (req.query.campaignstatus) {
filtering.push({
field: 'campaign.effective_status',
operator: 'IN',
value: req.query.campaignstatus.split(','),
});
}
// Always filter for campaigns with impressions
filtering.push({
field: 'impressions',
operator: 'GREATER_THAN',
value: 0,
});
Implementation Details:
exports.getAccountInsights = async (req, res, next) => {
try {
let fbAccountId = req.params.account_id;
let filterParams = req.query;
let accountId = await checkAccountAccess(req);
// Get Facebook token
const existKeys = await facebookModel.find({
accountID: accountId,
source: 'meta-ads',
});
// Validate and refresh token
let accessToken = await facebookModel.checkAccessTokenValidity({
req: req,
expirationTime: existKeys.token.expires_in,
accessToken: existKeys.token.access_token,
docId: existKeys.docId,
source: 'meta-ads',
});
// Transform date parameters
if (filterParams.since && filterParams.until) {
filterParams.time_range = {
since: moment(filterParams.since).startOf('day').format('YYYY-MM-DD'),
until: moment(filterParams.until).startOf('day').format('YYYY-MM-DD'),
};
delete filterParams.since;
delete filterParams.until;
}
// Build filtering array
filterParams.filtering = [];
if (filterParams.campaignids) {
filterParams.filtering.push({
field: 'campaign.id',
operator: 'IN',
value: filterParams.campaignids.split(','),
});
delete filterParams.campaignids;
}
// Set level and required fields
filterParams.level = 'campaign';
const requiredFields = [
'impressions',
'clicks',
'spend',
'actions',
'action_values',
'results',
'account_currency',
'reach',
'cpm',
];
const requestedFields = filterParams.fields ? filterParams.fields.split(',') : [];
const allFields = new Set([...requiredFields, ...requestedFields]);
filterParams.fields = Array.from(allFields).join(',');
filterParams.use_account_attribution_setting = true;
filterParams.filtering.push({
field: 'impressions',
operator: 'GREATER_THAN',
value: 0,
});
// Fetch insights
let accountInsights = await fbProvider.getAccountLevelInsights(
fbAccountId,
accessToken,
filterParams,
);
// Aggregate summary
const summary = {
impressions: 0,
clicks: 0,
spend: 0,
reach: 0,
conversions: 0,
total_conversion_value: 0,
roas: 0,
cpm: 0,
cpc: 0,
action_breakdown: {},
};
if (accountInsights.data && accountInsights.data.length > 0) {
accountInsights.data.forEach(campaignData => {
summary.impressions += parseInt(campaignData.impressions || 0, 10);
summary.clicks += parseInt(campaignData.clicks || 0, 10);
summary.spend += parseFloat(campaignData.spend || 0);
summary.reach += parseInt(campaignData.reach || 0, 10);
// Aggregate results (conversions)
if (campaignData.results && Array.isArray(campaignData.results)) {
campaignData.results.forEach(result => {
if (result.values && Array.isArray(result.values)) {
result.values.forEach(valueObj => {
if (valueObj && valueObj.value) {
summary.conversions += parseInt(valueObj.value, 10);
}
});
}
});
}
// Aggregate actions
if (campaignData.actions && Array.isArray(campaignData.actions)) {
campaignData.actions.forEach(action => {
const actionType = action.action_type;
const value = parseInt(action.value, 10);
if (!isNaN(value)) {
summary.action_breakdown[actionType] =
(summary.action_breakdown[actionType] || 0) + value;
}
});
}
// Aggregate conversion values
if (campaignData.action_values && Array.isArray(campaignData.action_values)) {
campaignData.action_values.forEach(action => {
if (action.action_type === 'purchase') {
summary.total_conversion_value += parseFloat(action.value || 0);
}
});
}
});
}
// Calculate derived metrics
summary.roas = summary.spend > 0 ? summary.total_conversion_value / summary.spend : 0;
summary.cpm = summary.impressions > 0 ? (summary.spend / summary.impressions) * 1000 : 0;
summary.cpc = summary.clicks > 0 ? summary.spend / summary.clicks : 0;
// Round to 2 decimals
summary.spend = parseFloat(summary.spend.toFixed(2));
summary.total_conversion_value = parseFloat(summary.total_conversion_value.toFixed(2));
summary.roas = parseFloat(summary.roas.toFixed(2));
summary.cpm = parseFloat(summary.cpm.toFixed(2));
summary.cpc = parseFloat(summary.cpc.toFixed(2));
summary.account_currency =
accountInsights.data.length > 0 ? accountInsights.data[0].account_currency : 'USD';
return res.status(200).json({
success: true,
message: 'SUCCESS',
data: summary,
});
} catch (error) {
next(error);
}
};
Campaign-Level Insights
Get Campaign Performance
Retrieves performance metrics for each campaign individually.
Endpoint: GET /v1/e/facebook/insights/:account_id/campaigns
Query Parameters: Same as account-level plus:
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | Number | No | Results per page (default: 25, max: 100) |
sort | String | No | Field to sort by (e.g., spend, impressions) |
order | String | No | Sort order: ascending or descending |
Example Request:
GET /v1/e/facebook/insights/act_123456789/campaigns?since=2024-01-01&until=2024-01-31&sort=spend&order=descending&limit=50
Authorization: Bearer eyJhbGc...
Response:
{
"success": true,
"message": "SUCCESS",
"data": [
{
"campaign_id": "23851234567890123",
"campaign_name": "Summer Sale Campaign",
"impressions": "45000",
"reach": "32000",
"frequency": "1.40625",
"clicks": "1250",
"ctr": "2.77778",
"cpc": "0.45",
"cpm": "12.50",
"cpp": "8.93",
"spend": "562.50",
"account_currency": "USD",
"actions": [
{
"action_type": "offsite_conversion.fb_pixel_purchase",
"value": "45"
},
{
"action_type": "link_click",
"value": "1250"
},
{
"action_type": "landing_page_view",
"value": "980"
}
],
"action_values": [
{
"action_type": "offsite_conversion.fb_pixel_purchase",
"value": "2250.00"
}
],
"cost_per_action_type": [
{
"action_type": "offsite_conversion.fb_pixel_purchase",
"value": "12.50"
},
{
"action_type": "link_click",
"value": "0.45"
}
],
"results": [
{
"indicator": "results",
"values": [
{
"value": "45"
}
]
}
],
"cost_per_result": [
{
"indicator": "cost_per_result",
"values": [
{
"value": "12.50"
}
]
}
],
"date_start": "2024-01-01",
"date_stop": "2024-01-31"
}
],
"paging": {
"cursors": {
"before": "MAZDZD",
"after": "MjQZD"
}
}
}
Campaign Metrics Explained:
const campaignMetrics = {
// Delivery
impressions: 'Number of times ads were shown',
reach: 'Unique users who saw ads',
frequency: 'Average impressions per user (impressions / reach)',
// Engagement
clicks: 'Total clicks on ads',
ctr: 'Click-through rate (clicks / impressions * 100)',
cpc: 'Cost per click',
// Cost
spend: 'Total amount spent',
cpm: 'Cost per 1000 impressions',
cpp: 'Cost per 1000 people reached',
// Conversions
actions: 'Array of action types and counts',
action_values: 'Monetary values for actions',
cost_per_action_type: 'Cost per specific action',
// Results (based on optimization goal)
results: 'Primary result based on campaign objective',
cost_per_result: 'Cost per primary result',
// Currency
account_currency: 'Currency code',
// Date Range
date_start: 'Report start date',
date_stop: 'Report end date',
};
Sorting:
// Sort by spend (highest first)
?sort=spend&order=descending
// Converted to Facebook format:
{
sort: ['spend_descending']
}
Available Metrics
Core Performance Metrics
const coreMetrics = {
// Delivery
impressions: 'Total ad impressions',
reach: 'Unique people reached',
frequency: 'Average impressions per person',
// Engagement
clicks: 'Total clicks',
unique_clicks: 'Unique users who clicked',
ctr: 'Click-through rate (%)',
unique_ctr: 'Unique click-through rate (%)',
// Cost
spend: 'Total amount spent',
cpc: 'Cost per click',
cpm: 'Cost per 1000 impressions',
cpp: 'Cost per 1000 people reached',
cost_per_unique_click: 'Cost per unique click',
// Video (if applicable)
video_play_actions: 'Video plays',
video_view: 'Video views (3+ seconds)',
video_avg_time_watched_actions: 'Average watch time',
video_p25_watched_actions: 'Videos watched to 25%',
video_p50_watched_actions: 'Videos watched to 50%',
video_p75_watched_actions: 'Videos watched to 75%',
video_p100_watched_actions: 'Videos watched to 100%',
video_thruplay_watched_actions: 'ThruPlay views (15s or completion)',
// Quality
quality_ranking: 'Ad quality ranking',
engagement_rate_ranking: 'Engagement rate ranking',
conversion_rate_ranking: 'Conversion rate ranking',
};
Action Types
Common action types returned in the actions array:
const actionTypes = {
// Conversions
'offsite_conversion.fb_pixel_purchase': 'Purchase events',
'offsite_conversion.fb_pixel_lead': 'Lead events',
'offsite_conversion.fb_pixel_complete_registration': 'Registration events',
'offsite_conversion.fb_pixel_add_to_cart': 'Add to cart events',
'offsite_conversion.fb_pixel_initiate_checkout': 'Checkout initiated',
'offsite_conversion.fb_pixel_custom': 'Custom conversion',
// On-Platform Actions
link_click: 'Link clicks',
landing_page_view: 'Landing page views',
post_engagement: 'Post engagements (likes, comments, shares)',
post_reaction: 'Post reactions',
comment: 'Comments',
post: 'Shares',
page_engagement: 'Page engagements',
like: 'Page likes',
photo_view: 'Photo views',
video_view: 'Video views',
// Lead Generation
'onsite_conversion.lead_grouped': 'Lead form submissions',
'onsite_conversion.messaging_conversation_started_7d': 'Messaging conversations',
// App Events
app_install: 'App installs',
'app_custom_event.fb_mobile_purchase': 'In-app purchases',
'app_custom_event.fb_mobile_add_to_cart': 'In-app add to cart',
// Offline Events
'offline_conversion.purchase': 'Offline purchases',
'offline_conversion.other': 'Other offline conversions',
};
Action Name Mapping
The integration includes a helper to convert action types to friendly names:
/**
* Creates a user-friendly name from Facebook API action_type string
*/
const getActionName = (actionType, customConversionMap = new Map()) => {
if (!actionType) return 'N/A';
// Handle Custom Conversions
if (actionType.includes('fb_pixel_custom')) {
const customId = actionType.split('.').pop();
if (customConversionMap.has(customId)) {
return customConversionMap.get(customId);
}
}
// Handle generic categories
if (['lead', 'onsite_web_lead'].some(t => actionType.includes(t))) return 'Leads';
if (actionType.includes('purchase')) return 'Purchases';
if (actionType.includes('add_to_cart')) return 'Adds to Cart';
if (actionType.includes('initiate_checkout')) return 'Initiated Checkouts';
if (actionType.includes('complete_registration')) return 'Registrations';
// Fallback to friendly formatting
return actionType.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
};
Understanding Results vs Actions
Results Field
The results field represents the primary optimization goal outcome:
// For a campaign optimized for CONVERSIONS
{
"results": [
{
"indicator": "results",
"values": [
{
"value": "45" // 45 purchase conversions
}
]
}
],
"cost_per_result": [
{
"indicator": "cost_per_result",
"values": [
{
"value": "12.50" // $12.50 per purchase
}
]
}
]
}
What "Results" Means by Optimization Goal:
const resultDefinitions = {
OFFSITE_CONVERSIONS: 'Pixel conversion events',
LINK_CLICKS: 'Link clicks',
LANDING_PAGE_VIEWS: 'Landing page views',
POST_ENGAGEMENT: 'Post engagements',
PAGE_LIKES: 'Page likes',
LEAD_GENERATION: 'Lead form submissions',
REACH: 'Unique people reached',
IMPRESSIONS: 'Total impressions',
THRUPLAY: 'ThruPlay video views',
VIDEO_VIEWS: '3-second video views',
APP_INSTALLS: 'App installs',
CONVERSIONS: 'Conversion events',
VALUE: 'Total conversion value',
};
Actions Field
The actions array contains all actions taken, not just the optimization goal:
{
"actions": [
{
"action_type": "offsite_conversion.fb_pixel_purchase",
"value": "45" // Primary result
},
{
"action_type": "link_click",
"value": "1250" // Additional action
},
{
"action_type": "landing_page_view",
"value": "980" // Additional action
}
]
}
Ad Set & Ad Level Insights
The same insights structure applies to ad set and ad levels:
Ad Set Insights: GET /v1/e/facebook/insights/:account_id/adsets
Ad Insights: GET /v1/e/facebook/insights/:account_id/ads
Both endpoints support the same query parameters and return similar data structures with level-specific IDs (adset_id, ad_id).
Pagination
Insights endpoints support cursor-based pagination:
// First page
GET /v1/e/facebook/insights/act_123/campaigns?limit=25
// Response includes paging info
{
"data": [...],
"paging": {
"cursors": {
"before": "MAZDZD",
"after": "MjQZD"
},
"next": "https://graph.facebook.com/v18.0/act_123/insights?after=MjQZD"
}
}
// Next page
GET /v1/e/facebook/insights/act_123/campaigns?limit=25&after=MjQZD
Date Range Best Practices
Maximum Range
Facebook recommends keeping date ranges to 93 days or less for optimal performance.
Time Granularity
Request specific time granularity:
{
time_increment: 1, // Daily breakdown
time_increment: 'monthly', // Monthly rollup
time_increment: 'all_days' // Total for entire range (default)
}
Timezone Considerations
All dates use the ad account's timezone setting. Convert dates to account timezone before querying.
Error Handling
Common Errors
// Invalid date format
{
"error": {
"message": "Invalid date format. Use YYYY-MM-DD",
"type": "OAuthException",
"code": 100
}
}
// Date range too large
{
"error": {
"message": "Please reduce the amount of data you're asking for",
"type": "OAuthException",
"code": 1
}
}
// No data for filters
{
"success": true,
"data": {
"impressions": 0,
"clicks": 0,
"spend": 0,
"action_breakdown": {}
}
}
Performance Optimization
Field Selection
Only request metrics you need:
# Minimal metrics
?fields=impressions,clicks,spend
# Full metrics (default)
?fields=impressions,clicks,spend,actions,action_values,results,reach,cpm,ctr
Filtering
Use filters to reduce data volume:
# Only active campaigns
?campaignstatus=ACTIVE
# Specific campaigns
?campaignids=123,456,789
# Campaigns with spend
?filtering=[{"field":"spend","operator":"GREATER_THAN","value":0}]
Caching
Consider caching insights data:
- Historical data: Can be cached indefinitely (won't change)
- Recent data: Cache for 1-6 hours (may update)
- Real-time data: Minimal caching (1-5 minutes)
Integration Checklist
Analytics Setup
- Connect Facebook integration with
meta-adssource - Verify insights API access
- Test account-level aggregation
- Test campaign-level breakdown
- Test date range filtering
- Test status filtering
- Verify action type mapping
- Test pagination
Data Processing
- Implement result definition mapping
- Handle custom conversion names
- Calculate derived metrics (ROAS, CTR, etc.)
- Format currency values
- Handle missing/null values
- Aggregate action breakdowns
Monitoring
- Track API response times
- Monitor data freshness
- Alert on missing metrics
- Measure cache hit rates
- Track query optimization
Related Documentation
- Facebook Index - Integration overview
- Authentication - OAuth and tokens
- Campaigns - Campaign operations
- Breakdowns - Advanced dimension breakdowns