Skip to main content

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

EndpointMethodPurpose
/v1/e/facebook/insights/:account_idGETGet account-level aggregated insights
/v1/e/facebook/insights/:account_id/campaignsGETGet campaign-level insights
/v1/e/facebook/insights/:account_id/adsetsGETGet ad set-level insights
/v1/e/facebook/insights/:account_id/adsGETGet 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:

ParameterTypeRequiredDescription
account_idStringYesFacebook Ad Account ID (e.g., act_123456789)

Query Parameters:

ParameterTypeRequiredDescription
sinceStringYesStart date (YYYY-MM-DD)
untilStringYesEnd date (YYYY-MM-DD)
campaignidsStringNoComma-separated campaign IDs to filter
adsetidsStringNoComma-separated ad set IDs to filter
adidsStringNoComma-separated ad IDs to filter
campaignstatusStringNoFilter by campaign status (e.g., ACTIVE,PAUSED)
adsetstatusStringNoFilter by ad set status
adstatusStringNoFilter by ad status
fieldsStringNoAdditional fields to include
account_idStringNoDashClicks 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:

ParameterTypeRequiredDescription
limitNumberNoResults per page (default: 25, max: 100)
sortStringNoField to sort by (e.g., spend, impressions)
orderStringNoSort 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-ads source
  • 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

Additional Resources

💬

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:30 AM