Facebook Campaigns, Ad Sets & Ads
The Facebook integration provides comprehensive campaign management across the three-tier hierarchy: Campaigns → Ad Sets → Ads.
Resource Hierarchy
graph TD
A[Ad Account] --> B[Campaign]
B --> C1[Ad Set 1]
B --> C2[Ad Set 2]
C1 --> D1[Ad 1]
C1 --> D2[Ad 2]
C2 --> D3[Ad 3]
style A fill:#e1f5ff
style B fill:#fff4e1
style C1 fill:#f0e1ff
style C2 fill:#f0e1ff
style D1 fill:#e1ffe1
style D2 fill:#e1ffe1
style D3 fill:#e1ffe1
Hierarchy Explained
- Campaign: High-level objective (e.g., Traffic, Conversions, Brand Awareness)
- Ad Set: Targeting, budget, schedule, and optimization
- Ad: Creative content (images, videos, text) shown to users
Endpoints Overview
| Endpoint | Method | Purpose |
|---|---|---|
/v1/e/facebook/campaign/:campaign_id | GET | Get single campaign details |
/v1/e/facebook/campaign/adset/:adset_id | GET | Get single ad set details |
/v1/e/facebook/campaign/ad/:ad_id | GET | Get single ad details |
Campaign Management
Get Campaign Details
Retrieves detailed information about a specific Facebook campaign.
Endpoint: GET /v1/e/facebook/campaign/:campaign_id
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
campaign_id | String | Yes | Facebook Campaign ID |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
fields | String | No | Comma-separated list of fields to retrieve |
account_id | String | No | Sub-account ID |
Example Request:
GET /v1/e/facebook/campaign/23851234567890123?fields=id,name,status,objective,daily_budget,lifetime_budget
Authorization: Bearer eyJhbGc...
Response:
{
"success": true,
"data": {
"id": "23851234567890123",
"name": "Summer Sale Campaign 2024",
"status": "ACTIVE",
"objective": "CONVERSIONS",
"daily_budget": "5000",
"lifetime_budget": null,
"budget_remaining": "3250",
"created_time": "2024-01-15T10:30:00+0000",
"updated_time": "2024-01-20T14:22:00+0000",
"start_time": "2024-01-15T10:30:00+0000",
"stop_time": null,
"effective_status": "ACTIVE",
"configured_status": "ACTIVE",
"buying_type": "AUCTION",
"bid_strategy": "LOWEST_COST_WITHOUT_CAP",
"special_ad_categories": [],
"account_id": "act_123456789"
}
}
Campaign Fields:
const campaignFields = {
// Basic Info
id: 'Campaign ID',
name: 'Campaign name',
account_id: 'Parent ad account',
// Status
status: 'Campaign status (ACTIVE, PAUSED, DELETED, ARCHIVED)',
effective_status: 'Computed status considering parent and schedule',
configured_status: 'User-set status',
// Objective
objective: 'Campaign objective (CONVERSIONS, LINK_CLICKS, etc.)',
// Budget
daily_budget: 'Daily budget in cents',
lifetime_budget: 'Lifetime budget in cents',
budget_remaining: 'Budget remaining in cents',
// Dates
created_time: 'Creation timestamp',
updated_time: 'Last update timestamp',
start_time: 'Campaign start time',
stop_time: 'Campaign end time (null if ongoing)',
// Buying
buying_type: 'AUCTION or RESERVED',
bid_strategy: 'Bidding strategy',
// Special
special_ad_categories: 'Array of special categories (HOUSING, EMPLOYMENT, CREDIT)',
spend_cap: 'Maximum campaign spend limit',
// Optimization
optimization_goal: 'What to optimize for',
promoted_object: 'Object being promoted (pixel, page, app)',
};
Campaign Objectives:
const objectives = {
OUTCOME_AWARENESS: 'Reach people near your business',
OUTCOME_ENGAGEMENT: 'Get more engagement',
OUTCOME_LEADS: 'Generate leads',
OUTCOME_SALES: 'Drive online sales',
OUTCOME_TRAFFIC: 'Drive traffic',
OUTCOME_APP_PROMOTION: 'Promote your app',
// Legacy objectives (still supported)
BRAND_AWARENESS: 'Brand awareness',
REACH: 'Reach',
LINK_CLICKS: 'Traffic',
POST_ENGAGEMENT: 'Engagement',
PAGE_LIKES: 'Page likes',
EVENT_RESPONSES: 'Event responses',
CONVERSIONS: 'Conversions',
PRODUCT_CATALOG_SALES: 'Catalog sales',
STORE_VISITS: 'Store traffic',
LEAD_GENERATION: 'Lead generation',
MESSAGES: 'Messages',
VIDEO_VIEWS: 'Video views',
APP_INSTALLS: 'App installs',
};
Status Values:
// effective_status (computed from multiple factors)
const effectiveStatuses = {
ACTIVE: 'Campaign is running',
PAUSED: 'Campaign is paused',
DELETED: 'Campaign is deleted',
ARCHIVED: 'Campaign is archived',
IN_PROCESS: 'Campaign is being created',
WITH_ISSUES: 'Campaign has issues',
};
// configured_status (user-set)
const configuredStatuses = {
ACTIVE: 'Set to active',
PAUSED: 'Set to paused',
DELETED: 'Set to deleted',
ARCHIVED: 'Set to archived',
};
Implementation Details:
exports.getCampaign = async (req, res, next) => {
try {
let campaign_id = req.params.campaign_id;
let params = req.query;
let accountId = await checkAccountAccess(req);
if (!accountId) {
return res.status(400).json({
success: false,
errno: 400,
message: 'Invalid Account Id',
});
}
const existKeys = await facebookModel.find({
accountID: accountId,
source: 'meta-ads',
});
if (!existKeys) {
return res.status(400).json({
success: false,
errno: 400,
message: 'Record Not found!',
});
}
let accessToken = await facebookModel.checkAccessTokenValidity({
req: req,
expirationTime: existKeys.token.expires_in,
accessToken: existKeys.token.access_token,
docId: existKeys.docId,
source: 'meta-ads',
});
const data = await fbProvider.getCampaign(campaign_id, accessToken, params);
res.json({
success: true,
data,
});
} catch (err) {
next(err);
}
};
Facebook API Call:
// In Providers/facebook.js
exports.getCampaign = async (campaign_id, access_token, params) => {
const url = `https://graph.facebook.com/${process.env.FACEBOOK_API_VERSION}/${campaign_id}`;
const options = {
method: 'GET',
headers: { Authorization: `Bearer ${access_token}` },
params: params,
url,
};
const result = await axios(options);
return result.data;
};
Ad Set Management
Get Ad Set Details
Retrieves detailed information about a specific ad set, including targeting and budget settings.
Endpoint: GET /v1/e/facebook/campaign/adset/:adset_id
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
adset_id | String | Yes | Facebook Ad Set ID |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
fields | String | No | Comma-separated list of fields |
account_id | String | No | Sub-account ID |
Example Request:
GET /v1/e/facebook/campaign/adset/23851234567890456?fields=id,name,status,targeting,optimization_goal,billing_event,daily_budget
Authorization: Bearer eyJhbGc...
Response:
{
"success": true,
"data": {
"id": "23851234567890456",
"name": "Desktop Users - US",
"campaign_id": "23851234567890123",
"status": "ACTIVE",
"effective_status": "ACTIVE",
"configured_status": "ACTIVE",
"optimization_goal": "OFFSITE_CONVERSIONS",
"billing_event": "IMPRESSIONS",
"bid_strategy": "LOWEST_COST_WITHOUT_CAP",
"daily_budget": "2000",
"lifetime_budget": null,
"budget_remaining": "1500",
"targeting": {
"age_min": 25,
"age_max": 55,
"genders": [1, 2],
"geo_locations": {
"countries": ["US"],
"location_types": ["home", "recent"]
},
"publisher_platforms": ["facebook", "instagram"],
"facebook_positions": ["feed", "instant_article"],
"device_platforms": ["desktop"],
"interests": [
{
"id": "6003139266461",
"name": "Online shopping"
}
],
"behaviors": [
{
"id": "6002714895372",
"name": "Frequent travelers"
}
]
},
"promoted_object": {
"pixel_id": "123456789012345",
"custom_event_type": "PURCHASE"
},
"start_time": "2024-01-15T10:30:00+0000",
"end_time": null,
"created_time": "2024-01-15T10:30:00+0000",
"updated_time": "2024-01-18T09:15:00+0000",
"pacing_type": ["standard"],
"destination_type": "WEBSITE"
}
}
Ad Set Fields:
const adSetFields = {
// Basic Info
id: 'Ad Set ID',
name: 'Ad Set name',
campaign_id: 'Parent campaign ID',
// Status
status: 'Ad Set status',
effective_status: 'Computed status',
configured_status: 'User-set status',
// Optimization
optimization_goal: 'What to optimize for (OFFSITE_CONVERSIONS, LINK_CLICKS, etc.)',
billing_event: 'What you pay for (IMPRESSIONS, LINK_CLICKS, etc.)',
bid_strategy: 'Bidding strategy',
bid_amount: 'Bid amount in cents (if using bid cap)',
// Budget
daily_budget: 'Daily budget in cents',
lifetime_budget: 'Lifetime budget in cents',
budget_remaining: 'Budget remaining in cents',
// Targeting
targeting: 'Targeting criteria object',
// Promoted Object
promoted_object: 'Pixel, page, or app being promoted',
// Schedule
start_time: 'Ad Set start time',
end_time: 'Ad Set end time',
// Pacing
pacing_type: 'Budget pacing (standard or day_parting)',
// Destination
destination_type: 'WEBSITE, APP, MESSENGER, etc.',
};
Optimization Goals:
const optimizationGoals = {
OFFSITE_CONVERSIONS: 'Website conversions via pixel',
CONVERSIONS: 'Conversion events',
LINK_CLICKS: 'Link clicks to destination',
LANDING_PAGE_VIEWS: 'Landing page views',
POST_ENGAGEMENT: 'Post likes, comments, shares',
PAGE_LIKES: 'Facebook Page likes',
REACH: 'Unique people reached',
IMPRESSIONS: 'Total impressions',
LEAD_GENERATION: 'Lead form submissions',
THRUPLAY: 'Video views (15 seconds or to completion)',
VIDEO_VIEWS: 'Video views (3+ seconds)',
APP_INSTALLS: 'Mobile app installs',
VALUE: 'Conversion value',
QUALITY_CALL: 'Quality phone calls',
QUALITY_LEAD: 'Quality leads',
};
Billing Events:
const billingEvents = {
IMPRESSIONS: 'Pay per impression (CPM)',
LINK_CLICKS: 'Pay per link click (CPC)',
POST_ENGAGEMENT: 'Pay per engagement',
THRUPLAY: 'Pay per ThruPlay',
LISTING_INTERACTION: 'Pay per listing interaction',
};
Targeting Structure:
const targeting = {
// Demographics
age_min: 18, // Minimum age (13-65+)
age_max: 65, // Maximum age
genders: [1], // 1=male, 2=female, 0=all
// Location
geo_locations: {
countries: ['US', 'CA'], // ISO country codes
regions: [{ key: '3847' }], // Region IDs (e.g., California)
cities: [{ key: '2418779' }], // City IDs
zips: [{ key: 'US:90210' }], // ZIP codes
location_types: ['home'], // 'home', 'recent', 'travel_in'
},
// Placements
publisher_platforms: ['facebook', 'instagram', 'audience_network', 'messenger'],
facebook_positions: [
'feed',
'instant_article',
'marketplace',
'video_feeds',
'right_hand_column',
'story',
],
instagram_positions: ['stream', 'story', 'explore'],
// Devices
device_platforms: ['mobile', 'desktop'],
user_device: ['Android', 'iOS'],
user_os: ['iOS_ver_14.0_and_above'],
// Interests
interests: [{ id: '6003139266461', name: 'Online shopping' }],
// Behaviors
behaviors: [{ id: '6002714895372', name: 'Frequent travelers' }],
// Custom Audiences
custom_audiences: [{ id: '23850123456789012', name: 'Website Visitors - 30 days' }],
// Excluded
excluded_custom_audiences: [],
excluded_geo_locations: {},
// Language
locales: [6], // Language IDs (6 = English (US))
// Connections
connections: [], // People connected to Pages/Apps/Events
excluded_connections: [],
};
Implementation Details:
exports.getAdset = async (req, res, next) => {
try {
let adset_id = req.params.adset_id;
let params = req.query;
let accountId = await checkAccountAccess(req);
if (!accountId) {
return res.status(400).json({
success: false,
errno: 400,
message: 'Invalid Account Id',
});
}
const existKeys = await facebookModel.find({
accountID: accountId,
source: 'meta-ads',
});
if (!existKeys) {
return res.status(400).json({
success: false,
errno: 400,
message: 'Record Not found!',
});
}
let accessToken = await facebookModel.checkAccessTokenValidity({
req: req,
expirationTime: existKeys.token.expires_in,
accessToken: existKeys.token.access_token,
docId: existKeys.docId,
source: 'meta-ads',
});
const data = await fbProvider.getAdset(adset_id, accessToken, params);
res.json({
success: true,
data,
});
} catch (err) {
next(err);
}
};
Ad Management
Get Ad Details
Retrieves detailed information about a specific ad, including creative content.
Endpoint: GET /v1/e/facebook/campaign/ad/:ad_id
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ad_id | String | Yes | Facebook Ad ID |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
fields | String | No | Comma-separated list of fields |
edges | String | No | Related data to fetch (e.g., 'adcreatives,previews') |
account_id | String | No | Sub-account ID |
Example Request:
GET /v1/e/facebook/campaign/ad/23851234567890789?fields=id,name,status,creative,adset_id&edges=adcreatives
Authorization: Bearer eyJhbGc...
Response:
{
"success": true,
"data": {
"id": "23851234567890789",
"name": "Summer Sale - Image Ad 1",
"adset_id": "23851234567890456",
"campaign_id": "23851234567890123",
"status": "ACTIVE",
"effective_status": "ACTIVE",
"configured_status": "ACTIVE",
"creative": {
"id": "23851234567891011",
"name": "Creative - Summer Sale",
"object_story_spec": {
"page_id": "123456789012345",
"link_data": {
"message": "🌞 Summer Sale! Up to 50% off on all items. Limited time only!",
"link": "https://example.com/summer-sale",
"name": "Summer Sale - Shop Now",
"description": "Don't miss out on amazing deals this summer.",
"picture": "https://example.com/images/summer-sale.jpg",
"call_to_action": {
"type": "SHOP_NOW",
"value": {
"link": "https://example.com/summer-sale"
}
}
}
},
"thumbnail_url": "https://scontent.xx.fbcdn.net/...",
"effective_object_story_id": "123456789012345_987654321098765"
},
"tracking_specs": [
{
"action.type": ["offsite_conversion"],
"fb_pixel": ["123456789012345"]
}
],
"conversion_specs": [
{
"action.type": ["purchase"],
"fb_pixel": ["123456789012345"]
}
],
"created_time": "2024-01-15T11:00:00+0000",
"updated_time": "2024-01-15T11:00:00+0000",
"bid_amount": 500,
"last_updated_by_app_id": "123456789"
}
}
Ad Fields:
const adFields = {
// Basic Info
id: 'Ad ID',
name: 'Ad name',
adset_id: 'Parent ad set ID',
campaign_id: 'Parent campaign ID',
// Status
status: 'Ad status',
effective_status: 'Computed status',
configured_status: 'User-set status',
// Creative
creative: 'Ad creative object',
// Tracking
tracking_specs: 'Pixel/conversion tracking',
conversion_specs: 'Conversion optimization',
// Dates
created_time: 'Creation timestamp',
updated_time: 'Last update timestamp',
// Bidding
bid_amount: 'Bid amount (if ad-level bidding)',
// Source
source_ad_id: 'If copied from another ad',
last_updated_by_app_id: 'Last app that updated',
};
Creative Types:
const creativeTypes = {
// Image Ads
LINK_AD: {
description: 'Single image with link',
components: ['image', 'headline', 'description', 'link', 'call_to_action'],
},
// Video Ads
VIDEO_AD: {
description: 'Video with link',
components: ['video', 'headline', 'description', 'link', 'call_to_action'],
},
// Carousel Ads
CAROUSEL_AD: {
description: 'Multiple images/videos that scroll',
components: ['multiple_images', 'headlines', 'descriptions', 'links', 'call_to_actions'],
},
// Collection Ads
COLLECTION_AD: {
description: 'Cover image/video with product catalog',
components: ['cover_media', 'product_set', 'headline', 'link'],
},
// Dynamic Ads
DYNAMIC_CREATIVE_AD: {
description: 'Multiple variations tested automatically',
components: ['images', 'videos', 'headlines', 'descriptions', 'call_to_actions'],
},
};
Call-to-Action Types:
const callToActionTypes = [
'APPLY_NOW', // Job applications
'BOOK_TRAVEL', // Travel bookings
'CONTACT_US', // Contact forms
'DONATE', // Donations
'DOWNLOAD', // App/file downloads
'GET_OFFER', // Special offers
'GET_QUOTE', // Quote requests
'LEARN_MORE', // More information
'LISTEN_NOW', // Audio content
'ORDER_NOW', // Food/product orders
'PLAY_GAME', // Games
'SHOP_NOW', // E-commerce
'SIGN_UP', // Account creation
'SUBSCRIBE', // Subscriptions
'WATCH_MORE', // Video content
'WHATSAPP_MESSAGE', // WhatsApp
'NO_BUTTON', // No CTA button
];
Creative Structure (Link Ad):
const creative = {
name: 'Creative Name',
object_story_spec: {
page_id: '123456789012345', // Facebook Page ID
link_data: {
// Text content
message: 'Primary text (main message)',
name: 'Headline (bold title)',
description: 'Description (below headline)',
// Media
link: 'https://example.com/landing',
picture: 'https://example.com/image.jpg',
// Call-to-Action
call_to_action: {
type: 'SHOP_NOW',
value: {
link: 'https://example.com/landing',
},
},
// Optional
caption: 'Website domain display',
child_attachments: [], // For carousel ads
},
},
// Alternative: Video Ad
video_data: {
message: 'Primary text',
video_id: '123456789',
call_to_action: {
type: 'LEARN_MORE',
value: {
link: 'https://example.com/video',
},
},
},
};
Implementation Details:
exports.getAd = async (req, res, next) => {
try {
let ad_id = req.params.ad_id;
let params = req.query;
let edges = req.query.edges;
delete params['edges'];
let accountId = await checkAccountAccess(req);
if (!accountId) {
return res.status(400).json({
success: false,
errno: 400,
message: 'Invalid Account Id',
});
}
const existKeys = await facebookModel.find({
accountID: accountId,
source: 'meta-ads',
});
if (!existKeys) {
return res.status(400).json({
success: false,
errno: 400,
message: 'Record Not found!',
});
}
let accessToken = await facebookModel.checkAccessTokenValidity({
req: req,
expirationTime: existKeys.token.expires_in,
accessToken: existKeys.token.access_token,
docId: existKeys.docId,
source: 'meta-ads',
});
const data = await fbProvider.getAd(ad_id, accessToken, params, edges);
res.json({
success: true,
data,
});
} catch (err) {
next(err);
}
};
Facebook API Call with Edges:
// In Providers/facebook.js
exports.getAd = async (ad_id, access_token, params, edges) => {
let url = `https://graph.facebook.com/${process.env.FACEBOOK_API_VERSION}/${ad_id}`;
// Add edges if specified
if (edges) {
url += `?${edges}`;
}
const options = {
method: 'GET',
headers: { Authorization: `Bearer ${access_token}` },
params: params,
url,
};
const result = await axios(options);
return result.data;
};
Common Operations
Fetching Related Resources
Use edges to fetch related data in a single request:
# Get ad with creative details
GET /v1/e/facebook/campaign/ad/123?edges=adcreatives
# Get campaign with all ad sets
GET /v1/e/facebook/campaign/123?edges=adsets
# Get ad set with all ads
GET /v1/e/facebook/campaign/adset/456?edges=ads
Field Selection
Request only needed fields to reduce response size and improve performance:
# Minimal campaign data
GET /v1/e/facebook/campaign/123?fields=id,name,status
# Campaign with budget info
GET /v1/e/facebook/campaign/123?fields=id,name,daily_budget,lifetime_budget,budget_remaining
# Ad with creative and tracking
GET /v1/e/facebook/campaign/ad/789?fields=id,name,creative,tracking_specs,conversion_specs
Error Handling
Common Errors
// Missing token
{
"success": false,
"errno": 400,
"message": "Record Not found!"
}
// Invalid account access
{
"success": false,
"errno": 400,
"message": "Invalid Account Id"
}
// Invalid campaign/ad set/ad ID
{
"error": {
"message": "(#803) Some of the aliases you requested do not exist: 23851234567890123",
"type": "OAuthException",
"code": 803
}
}
// Insufficient permissions
{
"error": {
"message": "Insufficient permissions to view campaign",
"type": "OAuthException",
"code": 200
}
}
Integration Checklist
Campaign Management
- Connect Facebook integration with
meta-adssource - Verify ad account access
- Test campaign retrieval
- Test ad set retrieval
- Test ad retrieval
- Verify creative data structure
- Test field selection
- Test edges for related data
Development
- Implement campaign listing (see insights.md)
- Add campaign creation endpoint
- Add campaign update endpoint
- Add campaign deletion/archival
- Implement ad set operations
- Implement ad operations
- Add creative upload functionality
Monitoring
- Track API call success rate
- Monitor token expiration
- Alert on permission errors
- Measure response times
- Track campaign status changes
Best Practices
Performance Optimization
- Use Field Selection: Only request needed fields
- Use Edges Wisely: Fetch related data in single request when needed
- Cache Static Data: Campaign objectives, statuses don't change often
- Batch Operations: Use batch API for multiple resources
Data Integrity
- Validate IDs: Ensure IDs match expected format
- Check Status: Verify resource exists before operations
- Handle Async Updates: Facebook updates may take time to propagate
- Store Metadata: Keep campaign/ad set/ad relationships in database
Creative Management
- Test Creative: Use ad preview API before going live
- Validate URLs: Ensure landing pages are accessible
- Check Image Specs: Follow Facebook's image size requirements
- Review Text: Check character limits and policy compliance
Related Documentation
- Facebook Index - Integration overview
- Authentication - OAuth and token management
- Accounts & Pages - Ad account and page management
- Analytics & Insights - Performance metrics and reporting
- Breakdowns - Advanced analytics dimensions