Optimized Cost Per Mille (oCPM) and Cost Per Action (CPA)

With oCPM, prioritize your marketing goals then automatically deliver ads towards these goals in the most effective way possible. Specify goals in absolute values, such as how much you value the fulfillment of a particular goal. These values are not bids. These values should be the value you place on an outcome.

With CPA, specify conversion events and get charged by the amount of conversions. This differs from CPM and oCPM ads, which charge per impressions served on the site, as well as CPC ads which charge for on-site clicks.

Optimized CPM (oCPM)

We automatically bid on your behalf, constrained by your defined campaign budget. The dynamic bids enable you to to capture the highest-value impressions for your goals, and you should expect total ROI for a campaign to exceed that of a CPC or a traditional CPM campaign.

Optimize your campaigns by defining the ad set's optimization_goal.

oCPM for Mobile App Installs is only available if the application has reported an install event in the last 28 days via the Facebook SDK or a mobile measurement partner.

Create

The ad set object contains bid information. To create an oCPM ad:

use FacebookAds\Object\AdSet;
use FacebookAds\Object\Fields\AdSetFields;
use FacebookAds\Object\Values\AdSetBillingEventValues;
use FacebookAds\Object\Values\AdSetOptimizationGoalValues;

$adset = new AdSet(null, 'act_<AD_ACCOUNT_ID>');
$adset->setData(array(
  AdSetFields::NAME => 'My Ad Set for oCPM',
  AdSetFields::BILLING_EVENT => AdSetBillingEventValues::IMPRESSIONS,
  AdSetFields::OPTIMIZATION_GOAL => AdSetOptimizationGoalValues::LINK_CLICKS,
  AdSetFields::BID_AMOUNT => 150,
  AdSetFields::CAMPAIGN_ID => <CAMPAIGN_ID>,
  AdSetFields::DAILY_BUDGET => 1000,
  AdSetFields::TARGETING => array(
    'geo_locations' => array(
      'countries' => array(
        'US'
      ),
    ),
  ),
));

$adset->create(array(
  AdSet::STATUS_PARAM_NAME => AdSet::STATUS_PAUSED,
));
from facebookads.adobjects.adset import AdSet
from facebookads.adobjects.targeting import Targeting

# Create an Ad Set with bid_type set to oCPM
adset = AdSet(parent_id='act_<AD_ACCOUNT_ID>')
adset.update({
    AdSet.Field.name: 'My Ad Set for oCPM',
    AdSet.Field.billing_event: AdSet.BillingEvent.impressions,
    AdSet.Field.optimization_goal: AdSet.OptimizationGoal.link_clicks,
    AdSet.Field.bid_amount: 150,
    AdSet.Field.campaign_id: '<CAMPAIGN_ID>',
    AdSet.Field.daily_budget: 1000,
    AdSet.Field.targeting: {
        Targeting.Field.geo_locations: {
            'countries': ['US'],
        },
    },
})

adset.remote_create(params={
    'status': AdSet.Status.paused,
})
AdSet adSet = new AdAccount(act_<AD_ACCOUNT_ID>, context).createAdSet()
  .setName("My Ad Set for oCPM")
  .setBillingEvent(AdSet.EnumBillingEvent.VALUE_IMPRESSIONS)
  .setOptimizationGoal(AdSet.EnumOptimizationGoal.VALUE_LINK_CLICKS)
  .setBidAmount(150L)
  .setCampaignId(<CAMPAIGN_ID>)
  .setDailyBudget(1000L)
  .setTargeting(
    new Targeting()
      .setFieldGeoLocations(
        new TargetingGeoLocation()
          .setFieldCountries(Arrays.asList("US"))
      )
  )
  .setStatus(AdSet.EnumStatus.VALUE_PAUSED)
  .execute();
String ad_set_id = adSet.getId();
curl \
  -F 'name=My Ad Set for oCPM' \
  -F 'billing_event=IMPRESSIONS' \
  -F 'optimization_goal=LINK_CLICKS' \
  -F 'bid_amount=150' \
  -F 'campaign_id=<CAMPAIGN_ID>' \
  -F 'daily_budget=1000' \
  -F 'targeting={"geo_locations":{"countries":["US"]}}' \
  -F 'status=PAUSED' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/adsets

When you do this, these fields must follow the restrictions below:

Name Description

billing_event

You must set to IMPRESSIONS.

optimization_goal

Set to what action you want to optimize.

bid_amount

Value you place on the optimization goal, specified in cents.

Facebook's Create Flow in Ads Manager will default these values to:


1000 for optimization_goal=APP_INSTALLS
3000 for optimization_goal=OFFSITE_CONVERSIONS, however

we recommend to bid true value of the conversion, which may be higher

500 for all other actions

See Validation Best practices for ad units that support oCPM bidding.

Budget and Pricing

An Optimized CPM campaign must have a budget. The ad system bids on each impression on your behalf, bidding high when the impression is likely to work for your goals, and bidding low when it’s unlikely to work for your goals.

Keep in mind that this dynamic enables you to capture the highest-value impressions for your goals, and you should expect the total ROI on the campaign to surpass your traditional CPC or CPM campaign. Note that stats for each of the goals are provided in Ads Manager and in the API, so you can check your ad's performance and gauge the success of a campaign, based on your goals.

Cost Per Action (CPA)

Your bid is defined for an ad set. These fields must follow the restrictions below:

Name Description

billing_event

Defines the action you pay on. Set to APP_INSTALLS, LINK_CLICKS, PAGE_LIKES, OFFER_CLAIMS, or VIDEO_VIEWS. As of v2.11 POST_ENGAGEMENT is not an option.

optimization_goal

Defines the action you optimize for. Set to same value as your billing_event, APP_INSTALLS, LINK_CLICKS, PAGE_LIKES, POST_ENGAGEMENT, OFFER_CLAIMS, or VIDEO_VIEWS

bid_amount

Value you place on the objective, specified in cents, minimum 1 cent. e.g. bid_amount=150 means you would like to bid $1.50 on that action. This should represent the maximum value you are willing to pay for this action.

targeting

For those ads which optimize for connections including page_like it is required to use the excluded_connections field in the targeting in order to exclude users who have already performed the one-time conversion (page like) for the destination object. See the examples in the section below for how to specify the appropriate excluded connections.

Note the following limitations:

  • The offsite link click action is only supported for links to offsite domains and Facebook hosted app domains.
  • CPA billing for non-video view ads are based on one day click through conversions. CPV (CPA for video view) ads are billed on 10-second video views.
  • Offsite link click billing is additionally restricted to link clicks within the advertisement (inline).
  • You will not be able to buy CPA for mobile app installs unless you have already reported back some installs and your account id-app id-mobile store trio has been deemed non-fraudulent. In the meantime, your ad creation call will fail.

See ad set document on allowed updates to ad sets.

Examples

The example below creates a CPA bidded ad set. Note that for CPA ad sets, you must set a promoted_object.

curl -X POST \ -F 'name="A CPA Ad Set"' \ -F 'campaign_id="<AD_CAMPAIGN_ID>"' \ -F 'daily_budget=5000' \ -F 'start_time="2018-10-29T13:52:27-0700"' \ -F 'end_time="2018-11-05T13:52:27-0800"' \ -F 'billing_event="IMPRESSIONS"' \ -F 'optimization_goal="REACH"' \ -F 'bid_amount=1000' \ -F 'promoted_object={ "page_id": "<PAGE_ID>" }' \ -F 'targeting={ "geo_locations": { "countries": [ "US" ] } }' \ -F 'user_os="iOS"' \ -F 'publisher_platforms="facebook"' \ -F 'device_platforms="mobile"' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.1/act_<AD_ACCOUNT_ID>/adsets
const adsSdk = require('facebook-nodejs-ads-sdk'); const AdAccount = adsSdk.AdAccount; const AdSet = adsSdk.AdSet; let access_token = '<ACCESS_TOKEN>'; let app_secret = '<APP_SECRET>'; let app_id = '<APP_ID>'; let id = '<ID>'; const api = adsSdk.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' : 'A CPA Ad Set', 'campaign_id' : '<adCampaignLinkClicksID>', 'daily_budget' : '5000', 'start_time' : '2018-09-27T12:12:21-0700', 'end_time' : '2018-10-04T12:12:21-0700', 'billing_event' : 'IMPRESSIONS', 'optimization_goal' : 'REACH', 'bid_amount' : '1000', 'promoted_object' : {'page_id':'<pageID>'}, 'targeting' : {'geo_locations':{'countries':['US']}}, 'user_os' : 'iOS', 'publisher_platforms' : 'facebook', 'device_platforms' : 'mobile', }; let 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 = '<ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( ); $params = array( 'name' => 'A CPA Ad Set', 'campaign_id' => '<adCampaignLinkClicksID>', 'daily_budget' => '5000', 'start_time' => '2018-09-27T12:12:21-0700', 'end_time' => '2018-10-04T12:12:21-0700', 'billing_event' => 'IMPRESSIONS', 'optimization_goal' => 'REACH', 'bid_amount' => '1000', 'promoted_object' => array('page_id' => '<pageID>'), 'targeting' => array('geo_locations' => array('countries' => array('US'))), 'user_os' => 'iOS', 'publisher_platforms' => 'facebook', 'device_platforms' => 'mobile', ); echo json_encode((new AdAccount($id))->createAdSet( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebookads.adobjects.adaccount import AdAccount from facebookads.adobjects.adset import AdSet from facebookads.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<ID>' FacebookAdsApi.init(access_token=access_token) fields = [ ] params = { 'name': 'A CPA Ad Set', 'campaign_id': '<adCampaignLinkClicksID>', 'daily_budget': '5000', 'start_time': '2018-09-27T12:12:21-0700', 'end_time': '2018-10-04T12:12:21-0700', 'billing_event': 'IMPRESSIONS', 'optimization_goal': 'REACH', 'bid_amount': '1000', 'promoted_object': {'page_id':'<pageID>'}, 'targeting': {'geo_locations':{'countries':['US']}}, 'user_os': 'iOS', 'publisher_platforms': 'facebook', 'device_platforms': 'mobile', } print AdAccount(id).create_ad_set( fields=fields, params=params, )
import com.facebook.ads.sdk.*; import java.io.File; 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 = \"<ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new AdAccount(id, context).createAdSet() .setName(\"A CPA Ad Set\") .setCampaignId(\"<adCampaignLinkClicksID>\") .setDailyBudget(5000L) .setStartTime(\"2018-09-27T12:12:21-0700\") .setEndTime(\"2018-10-04T12:12:21-0700\") .setBillingEvent(AdSet.EnumBillingEvent.VALUE_IMPRESSIONS) .setOptimizationGoal(AdSet.EnumOptimizationGoal.VALUE_REACH) .setBidAmount(1000L) .setPromotedObject(\"{\\"page_id\\":\\"<pageID>\\"}\") .setTargeting( new Targeting() .setFieldGeoLocations( new TargetingGeoLocation() .setFieldCountries(Arrays.asList(\"US\")) ) ) .setParam(\"user_os\", \"iOS\") .setParam(\"publisher_platforms\", \"facebook\") .setParam(\"device_platforms\", \"mobile\") .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<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: 'A CPA Ad Set', campaign_id: '<adCampaignLinkClicksID>', daily_budget: '5000', start_time: '2018-09-27T12:12:21-0700', end_time: '2018-10-04T12:12:21-0700', billing_event: 'IMPRESSIONS', optimization_goal: 'REACH', bid_amount: '1000', promoted_object: {'page_id':'<pageID>'}, targeting: {'geo_locations':{'countries':['US']}}, user_os: 'iOS', publisher_platforms: 'facebook', device_platforms: 'mobile', })

After that, you can create ads and put them into this ad set following the creation flow here.

Update

Change the bid to a CPA ad set

use FacebookAds\Object\AdSet;
use FacebookAds\Object\Fields\AdSetFields;
use FacebookAds\Object\Values\AdSetBillingEventValues;
use FacebookAds\Object\Values\AdSetOptimizationGoalValues;

$adset = new AdSet(<AD_SET_ID>, 'act_<AD_ACCOUNT_ID>');
$adset->setData(array(
  AdSetFields::BILLING_EVENT => AdSetBillingEventValues::IMPRESSIONS,
  AdSetFields::OPTIMIZATION_GOAL => AdSetOptimizationGoalValues::LINK_CLICKS,
  AdSetFields::BID_AMOUNT => 200,
));

$adset->update();
from facebookads.adobjects.adset import AdSet

adset = AdSet(fbid=<AD_SET_ID>, parent_id='act_<AD_ACCOUNT_ID>')
adset.update({
    AdSet.Field.optimization_goal: AdSet.OptimizationGoal.link_clicks,
    AdSet.Field.billing_event: AdSet.BillingEvent.impressions,
    AdSet.Field.bid_amount: 200,
})
adset.remote_update()
new AdSet(<AD_SET_ID>, context).update()
  .setBillingEvent(AdSet.EnumBillingEvent.VALUE_IMPRESSIONS)
  .setOptimizationGoal(AdSet.EnumOptimizationGoal.VALUE_LINK_CLICKS)
  .setBidAmount(200L)
  .execute();
curl \
  -F 'billing_event=IMPRESSIONS' \
  -F 'optimization_goal=LINK_CLICKS' \
  -F 'bid_amount=200' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/<AD_SET_ID>

CPV example

To create an ad bidding CPV, or CPA for video views, first create an ad campaign with objective=VIDEO_VIEWS.

curl -X POST \ -F 'name="Video Views campaign"' \ -F 'objective="VIDEO_VIEWS"' \ -F 'status="PAUSED"' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.1/act_<AD_ACCOUNT_ID>/campaigns
const adsSdk = require('facebook-nodejs-ads-sdk'); const AdAccount = adsSdk.AdAccount; const Campaign = adsSdk.Campaign; let access_token = '<ACCESS_TOKEN>'; let app_secret = '<APP_SECRET>'; let app_id = '<APP_ID>'; let id = '<ID>'; const api = adsSdk.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' : 'Video Views campaign', 'objective' : 'VIDEO_VIEWS', 'status' : 'PAUSED', }; let campaigns = (new AdAccount(id)).createCampaign( fields, params ); logApiCallResult('campaigns api call complete.', campaigns);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\AdAccount; use FacebookAds\Object\Campaign; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( ); $params = array( 'name' => 'Video Views campaign', 'objective' => 'VIDEO_VIEWS', 'status' => 'PAUSED', ); echo json_encode((new AdAccount($id))->createCampaign( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebookads.adobjects.adaccount import AdAccount from facebookads.adobjects.campaign import Campaign from facebookads.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<ID>' FacebookAdsApi.init(access_token=access_token) fields = [ ] params = { 'name': 'Video Views campaign', 'objective': 'VIDEO_VIEWS', 'status': 'PAUSED', } print AdAccount(id).create_campaign( fields=fields, params=params, )
import com.facebook.ads.sdk.*; import java.io.File; 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 = \"<ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new AdAccount(id, context).createCampaign() .setName(\"Video Views campaign\") .setObjective(Campaign.EnumObjective.VALUE_VIDEO_VIEWS) .setStatus(Campaign.EnumStatus.VALUE_PAUSED) .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end ad_account = FacebookAds::AdAccount.get(id) campaigns = ad_account.campaigns.create({ name: 'Video Views campaign', objective: 'VIDEO_VIEWS', status: 'PAUSED', })

Then, set the CPA for video views bid_info at the ad set:

use FacebookAds\Object\AdSet;
use FacebookAds\Object\Fields\AdSetFields;
use FacebookAds\Object\Fields\TargetingFields;
use FacebookAds\Object\Targeting;
use FacebookAds\Object\Values\AdSetBillingEventValues;
use FacebookAds\Object\Values\AdSetOptimizationGoalValues;

$adset = new AdSet(null, 'act_<AD_ACCOUNT_ID>');
$adset->setData(array(
  AdSetFields::NAME => 'A CPV Ad Set',
  AdSetFields::CAMPAIGN_ID => <CAMPAIGN_ID>,
  AdSetFields::DAILY_BUDGET => 500,
  AdSetFields::START_TIME =>
    (new \DateTime("+1 week"))->format(\DateTime::ISO8601),
  AdSetFields::END_TIME =>
    (new \DateTime("+2 week"))->format(\DateTime::ISO8601),
  AdSetFields::BILLING_EVENT => AdSetBillingEventValues::VIDEO_VIEWS,
  AdSetFields::OPTIMIZATION_GOAL => AdSetOptimizationGoalValues::VIDEO_VIEWS,
  AdSetFields::BID_AMOUNT => 100,
  AdSetFields::TARGETING => (new Targeting())->setData(array(
    TargetingFields::GEO_LOCATIONS => array(
      'countries' => array(
        'US',
      ),
    ),
    TargetingFields::PUBLISHER_PLATFORMS => array('facebook'),
    TargetingFields::DEVICE_PLATFORMS => array('mobile'),
  )),
));

$adset->create(array(
  AdSet::STATUS_PARAM_NAME => AdSet::STATUS_PAUSED,
));
import time
from facebookads.adobjects.adset import AdSet

adset = AdSet(parent_id='act_<AD_ACCOUNT_ID>')
adset.update({
    AdSet.Field.name: 'A CPV Ad Set',
    AdSet.Field.campaign_id: <CAMPAIGN_ID>,
    AdSet.Field.daily_budget: 500,
    AdSet.Field.start_time: int(time.time()),
    AdSet.Field.end_time: int(time.time() + 100000),
    AdSet.Field.optimization_goal: AdSet.OptimizationGoal.video_views,
    AdSet.Field.billing_event: AdSet.BillingEvent.video_views,
    AdSet.Field.bid_amount: 100,
    AdSet.Field.targeting: {
        'geo_locations': {
            'countries': ['US'],
        },
        Targeting.Field.publisher_platforms: ['facebook'],
        Targeting.Field.device_platforms: ['mobile'],
    },
})

adset.remote_create(params={
    'status': AdSet.Status.paused,
})
AdSet adSet = new AdAccount(act_<AD_ACCOUNT_ID>, context).createAdSet()
  .setName("A CPV Ad Set")
  .setCampaignId(<CAMPAIGN_ID>)
  .setDailyBudget(500L)
  .setStartTime(start_time)
  .setEndTime(end_time)
  .setBillingEvent(AdSet.EnumBillingEvent.VALUE_VIDEO_VIEWS)
  .setOptimizationGoal(AdSet.EnumOptimizationGoal.VALUE_VIDEO_VIEWS)
  .setBidAmount(100L)
  .setTargeting(
    new Targeting()
      .setFieldDevicePlatforms(Arrays.asList(Targeting.EnumDevicePlatforms.VALUE_MOBILE))
      .setFieldGeoLocations(
        new TargetingGeoLocation()
          .setFieldCountries(Arrays.asList("US"))
      )
      .setFieldPublisherPlatforms(Arrays.asList("facebook"))
  )
  .setStatus(AdSet.EnumStatus.VALUE_PAUSED)
  .execute();
String ad_set_id = adSet.getId();
curl \
  -F 'name=A CPV Ad Set' \
  -F 'campaign_id=<CAMPAIGN_ID>' \
  -F 'daily_budget=500' \
  -F 'start_time=2018-02-06T04:45:29+0000' \
  -F 'end_time=2018-02-13T04:45:29+0000' \
  -F 'billing_event=VIDEO_VIEWS' \
  -F 'optimization_goal=VIDEO_VIEWS' \
  -F 'bid_amount=100' \
  -F 'targeting={ 
    "device_platforms": ["mobile"], 
    "geo_locations": {"countries":["US"]}, 
    "publisher_platforms": ["facebook"] 
  }' \
  -F 'status=PAUSED' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/adsets