Billing Events

billing_event defines events you want to pay for such as impressions, clicks, or various actions. Billing depends on the size of your audience and your budget.

You can choose between a daily or a lifetime budget, as well as a cost per thousand impressions bid (CPM) or cost per click bid (CPC). Facebook uses maximum bids for billing; this means if you bid $5 cost per click (CPC), you'll be charged no more than $5. In most cases, you'll be charged less.

For example, to optimize for POST_ENGAGEMENT and pay per IMPRESSIONS:

curl -X POST \ -F 'name="My First Adset"' \ -F 'lifetime_budget=20000' \ -F 'start_time="2020-04-09T21:28:57-0700"' \ -F 'end_time="2020-04-19T21:28:57-0700"' \ -F 'campaign_id="<AD_CAMPAIGN_ID>"' \ -F 'bid_amount=500' \ -F 'billing_event="IMPRESSIONS"' \ -F 'optimization_goal="POST_ENGAGEMENT"' \ -F 'targeting={ "facebook_positions": [ "feed" ], "geo_locations": { "countries": [ "US" ], "regions": [ { "key": "4081" } ], "cities": [ { "key": 777934, "radius": 10, "distance_unit": "mile" } ] }, "genders": [ 1 ], "age_max": 24, "age_min": 20, "behaviors": [ { "id": 6002714895372, "name": "All travelers" } ], "life_events": [ { "id": 6002714398172, "name": "Newlywed (1 year)" } ], "publisher_platforms": [ "facebook" ], "device_platforms": [ "desktop" ] }' \ -F 'status="PAUSED"' \ -F 'access_token=<ACCESS_TOKEN>' \<AD_ACCOUNT_ID>/adsets
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const AdAccount = bizSdk.AdAccount; const AdSet = bizSdk.AdSet; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<AD_ACCOUNT_ID>'; const api = bizSdk.FacebookAdsApi.init(access_token); const showDebugingInfo = true; // Setting this to true shows more debugging info. if (showDebugingInfo) { api.setDebug(true); } const logApiCallResult = (apiCallName, data) => { console.log(apiCallName); if (showDebugingInfo) { console.log('Data:' + JSON.stringify(data)); } }; let fields, params; fields = [ ]; params = { 'name' : 'My First Adset', 'lifetime_budget' : '20000', 'start_time' : '2019-12-05T23:43:27-0800', 'end_time' : '2019-12-15T23:43:27-0800', 'campaign_id' : '<adCampaignLinkClicksID>', 'bid_amount' : '500', 'billing_event' : 'IMPRESSIONS', 'optimization_goal' : 'POST_ENGAGEMENT', 'targeting' : {'facebook_positions':['feed'],'geo_locations':{'countries':['US'],'regions':[{'key':'4081'}],'cities':[{'key':777934,'radius':10,'distance_unit':'mile'}]},'genders':[1],'age_max':24,'age_min':20,'behaviors':[{'id':6002714895372,'name':'All travelers'}],'life_events':[{'id':6002714398172,'name':'Newlywed (1 year)'}],'publisher_platforms':['facebook'],'device_platforms':['desktop']}, 'status' : 'PAUSED', }; const adsets = (new AdAccount(id)).createAdSet( fields, params ); logApiCallResult('adsets api call complete.', adsets);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\AdAccount; use FacebookAds\Object\AdSet; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<AD_ACCOUNT_ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( ); $params = array( 'name' => 'My First Adset', 'lifetime_budget' => '20000', 'start_time' => '2019-12-05T23:43:27-0800', 'end_time' => '2019-12-15T23:43:27-0800', 'campaign_id' => '<adCampaignLinkClicksID>', 'bid_amount' => '500', 'billing_event' => 'IMPRESSIONS', 'optimization_goal' => 'POST_ENGAGEMENT', 'targeting' => array('facebook_positions' => array('feed'),'geo_locations' => array('countries' => array('US'),'regions' => array(array('key' => '4081')),'cities' => array(array('key' => 777934,'radius' => 10,'distance_unit' => 'mile'))),'genders' => array(1),'age_max' => 24,'age_min' => 20,'behaviors' => array(array('id' => 6002714895372,'name' => 'All travelers')),'life_events' => array(array('id' => 6002714398172,'name' => 'Newlywed (1 year)')),'publisher_platforms' => array('facebook'),'device_platforms' => array('desktop')), 'status' => 'PAUSED', ); echo json_encode((new AdAccount($id))->createAdSet( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.adaccount import AdAccount from facebook_business.adobjects.adset import AdSet from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_ACCOUNT_ID>' FacebookAdsApi.init(access_token=access_token) fields = [ ] params = { 'name': 'My First Adset', 'lifetime_budget': '20000', 'start_time': '2019-12-05T23:43:27-0800', 'end_time': '2019-12-15T23:43:27-0800', 'campaign_id': '<adCampaignLinkClicksID>', 'bid_amount': '500', 'billing_event': 'IMPRESSIONS', 'optimization_goal': 'POST_ENGAGEMENT', 'targeting': {'facebook_positions':['feed'],'geo_locations':{'countries':['US'],'regions':[{'key':'4081'}],'cities':[{'key':777934,'radius':10,'distance_unit':'mile'}]},'genders':[1],'age_max':24,'age_min':20,'behaviors':[{'id':6002714895372,'name':'All travelers'}],'life_events':[{'id':6002714398172,'name':'Newlywed (1 year)'}],'publisher_platforms':['facebook'],'device_platforms':['desktop']}, 'status': 'PAUSED', } print AdAccount(id).create_ad_set( fields=fields, params=params, )
import*; import; import java.util.Arrays; public class SAMPLE_CODE_EXAMPLE { public static void main (String args[]) throws APIException { String access_token = \"<ACCESS_TOKEN>\"; String app_secret = \"<APP_SECRET>\"; String app_id = \"<APP_ID>\"; String id = \"<AD_ACCOUNT_ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new AdAccount(id, context).createAdSet() .setName(\"My First Adset\") .setLifetimeBudget(20000L) .setStartTime(\"2019-12-05T23:43:27-0800\") .setEndTime(\"2019-12-15T23:43:27-0800\") .setCampaignId(\"<adCampaignLinkClicksID>\") .setBidAmount(500L) .setBillingEvent(AdSet.EnumBillingEvent.VALUE_IMPRESSIONS) .setOptimizationGoal(AdSet.EnumOptimizationGoal.VALUE_POST_ENGAGEMENT) .setTargeting( new Targeting() .setFieldAgeMax(24L) .setFieldAgeMin(20L) .setFieldBehaviors(Arrays.asList( new IDName() .setFieldId(6002714895372L) .setFieldName(\"All travelers\") )) .setFieldDevicePlatforms(Arrays.asList(Targeting.EnumDevicePlatforms.VALUE_DESKTOP)) .setFieldFacebookPositions(Arrays.asList(\"feed\")) .setFieldGenders(Arrays.asList(1L)) .setFieldGeoLocations( new TargetingGeoLocation() .setFieldCities(Arrays.asList( new TargetingGeoLocationCity() .setFieldDistanceUnit(\"mile\") .setFieldKey(777934L) .setFieldRadius(10L) )) .setFieldCountries(Arrays.asList(\"US\")) .setFieldRegions(Arrays.asList( new TargetingGeoLocationRegion() .setFieldKey(\"4081\") )) ) .setFieldLifeEvents(Arrays.asList( new IDName() .setFieldId(6002714398172L) .setFieldName(\"Newlywed (1 year)\") )) .setFieldPublisherPlatforms(Arrays.asList(\"facebook\")) ) .setStatus(AdSet.EnumStatus.VALUE_PAUSED) .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_ACCOUNT_ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end ad_account = FacebookAds::AdAccount.get(id) adsets = ad_account.adsets.create({ name: 'My First Adset', lifetime_budget: '20000', start_time: '2019-12-05T23:43:27-0800', end_time: '2019-12-15T23:43:27-0800', campaign_id: '<adCampaignLinkClicksID>', bid_amount: '500', billing_event: 'IMPRESSIONS', optimization_goal: 'POST_ENGAGEMENT', targeting: {'facebook_positions':['feed'],'geo_locations':{'countries':['US'],'regions':[{'key':'4081'}],'cities':[{'key':777934,'radius':10,'distance_unit':'mile'}]},'genders':[1],'age_max':24,'age_min':20,'behaviors':[{'id':6002714895372,'name':'All travelers'}],'life_events':[{'id':6002714398172,'name':'Newlywed (1 year)'}],'publisher_platforms':['facebook'],'device_platforms':['desktop']}, status: 'PAUSED', })

Once you select optimization_goal, you may have one or more billing_event options. See Marketing API Validation: Optimization Goal and Billing events and CPA.

For example, imagine you have a campaign with optimization_goal of APP_INSTALLS and bid_amount as $10.00. You can choose IMPRESSIONS or APP_INSTALLS as billing_event. If Facebook could perfectly estimate the chance someone installs your app 100%, either choice gives you the same number of impressions, the same number of installs, and the same total budget spent.

With 1% chance of install per view and 1,000 views, you would get 10 installs for $100.00 or less. If billing_event is IMPRESSIONS, each impression costs is about $0.10, which is bid_amount times probability of optimization_goal. If billing_event is APP_INSTALLS, each installation costs $10.00 or less.

In reality, results from two different billing_events vary due to budget pacing and variance between actual actions and our predictions.


Imagine a Mobile App Install ad with these settings:

  • optimization_goal: APP_INSTALLS
  • optimization_goal probability from Facebook: 1%
  • bid_amount: $10.00
  • billing_event: IMPRESSIONS
  • Each impression cost: $0.10, or bid_amount
  • optimization_goal probability, or less
  • budget: not a direct factor

After 1,000 people see your ad and you spend $100.00, it's possible only .5%, or 5 people install your app. So your actual cost per installation (CPI) is $20.00 or less; more than your bid_amount at $10.00. However, while CPI here exceeds bid_amount, it's unlikely to happen after you run a campaign a while and you see a reasonable amount of installs. This improves Facebook predictive models, ads delivery, and your costs and results. Generally, CPI may be slightly higher than bid_amount for campaigns with very short durations or with very few actions taken.


Here is an example of a Mobile App Install ad, which demonstrates a principle you should understand about any ads experiencing sparse events. This campaign has these settings:

  • optimization_goal: APP_INSTALLS
  • optimization_goal from Facebook: 1%
  • bid_amount: $10.00
  • billing_event: APP_INSTALLS
  • Each install cost: $10.00 (bid_amount) or less
  • daily_budget: $100.00

Facebook's ad pacing tries to evenly deliver ads and spend your budget over time. If you get 10 app installs in the morning, your daily budget is spent. Your ad gets no more delivery and you have very uneven delivery. Alternately, if your ad gets 3 installs quickly, but then few people install it later, our systems increases your effective bid to spend your budget. But day's end, your ad only gets 5 installs and it's also under-delivered.

In these examples your cost per install is $10.00 or less, but you miss smooth ads delivery. Because app install events are relatively sparse, it is hard to pace evenly. Other sparse events include PAGE_LIKES and OFFER_CLAIMS.

IMPRESSIONS as billing_event

When to use

Use IMPRESSIONS when you have a budget-constrained campaign, and you want to have the campaign delivered smoothly, with all budget spent by the end time. In this case, our pacing system quickly adjusts to impression-based billing.

When to avoid

To be certain CPI is less than or equal to bid_amount, do not use IMPRESSIONS as billing_event. This has some variance based on actual actions taken. Use APP_INSTALLS.

APP_INSTALL or other actions as billing_event

When to use

Use APP_INSTALL or other actions as billing_event for bid-constrained campaigns. This means your budget is high enough to avoid pacing issues and your priority is cost per action no higher than bid_amount. In these cases, your average cost per action is less than or equal to bid_amount.

When to avoid

If your ad set has limited budget and you need smooth delivery. This means we adjust effective bid over time so it generates as many optimization_goals possible for your budget. See Ad Delivery and Pacing.

For frequent events such as POST_ENGAGEMENT and LINK_CLICKS, uneven pacing and infrequent billing_events are unlikely. If you set billing_event to a frequent event, Facebook can keep your cost per action close to bid_amount or lower, while having smooth delivery.