Bid Multipliers

This API is available on a limited basis to whitelisted partners and advertisers. Contact your Facebook representative if you want to use it.

Advertisers typically create ad sets so that they can bid differently for various audience segments. For example, an advertiser can create 3 ad sets corresponding to 3 different age groups: 18–25, 26–35, 36–65.

Bid multipliers enable advertisers to reduce the amount of audience segmentation required because it allows them to maintain a nuanced bidding strategy within a single ad set with one targeted audience. For example, an advertiser can create a single ad set, then use bid multipliers to bid differently for each of those recency buckets.

Use complex, customized bidding strategies without having to create too many audience segments or ad sets. This helps you achieve better ads delivery while you avoid over-segmenting ad campaigns into a larger number of ad asets with relatively small audiences.

Bid multipliers are not specific to an objective, but they should only be used for OFFSITE_CONVERSIONS or LINK_CLICKS optimization.

In the past, you could create 3 ad sets corresponding to 3 different age groups. You could then separately bid for each one of these ad sets. You can now create a single ad set and use bid multipliers to bid differently for each of those age groups; a bid multiplier range from 0.01 to 1.0.

Set Up Adjustment Categories

With bid multiplier, you can define user groups and assign a bid multiplier for each group. User groups are defined with user demographic information, user device, and advertiser custom data.

Below is a list of all supported breakdowns.

Age

Group users by age ranges; for example, 18-25, 26-35. As an example, for an adset with USD 5 max bid, we'll bid USD 2.5 for users aged between 18 to 25, bid USD 3.5 for users aged between 26 and 40, bid USD 5 for all other users. Note: The default is optional; we can apply 1.0 as the multiplier.

{
    "age": {
      "18-25": 0.5,
      "26-40": 0.7,
      "default": 1.0
    }
}

Gender

Group users by gender.

{
    "gender": {
      "male": 0.5,
      "female": 0.7,
      "default": 1.0
    }
}

device_platform

Possible breakdown values are:

  • mobile
  • desktop

Example

{
  "device_platform": {
    "mobile": 0.7,
    "desktop": 0.9
  }
}

publisher_platform

Possible breakdown values are:

  • facebook
  • instagram
  • audience_network
  • messenger

Example

{
  "publisher_platform": {
    "facebook": 0.7,
    "instagram": 0.9,
    "default": 1.0 // We'll apply bid multiplier 1.0 if no default is specified anyway
  }
}

user_device

Possible breakdown values are iPad, iPhone. See other possible values at Targeting Search API with type=adTargetingCategory and class=user_device.

Example

{
  "user_device": {
    "iPad 2": 0.7,
    "iPhone": 0.9,
    "default": 1.0
  }
}

user_os

Possible breakdown values are iOS, Windows. See other possible values at Targeting Search API with type=adTargetingCategory and class=user_os. Current supported values are:

  • Android
  • Windows
  • Windows Phone
  • iOS

Example

{
  "user_os": {
    "Android": 0.7,
    "iOS": 0.9,
    "default": 1.0
  }
}

User Recency

Group users by time because they have any pixel fire or app events. You must specify which event sources to track and time windows. The example shows where we can apply bid multiplier 1.0 for users who have pixel fire or app events within 86400 seconds, and so on.

Example

{
    "user_recency": {
      "event_sources": [<pixel_id>,<app_id>,...],
      "0-86400": 1.0,
      "86401-172800": 0.7,
      "default": 0.5
  }
}

User Bucket

You can send us results of your own user classifier, and send us as an extra user_bucket parameter in the pixel fire or app event. User buckets are integers ranging from 0 to 100. Specify user bucket group definition with the following format:

  • event_sources - Pixel fire or app event source to track.
  • event_retention - Optional. Time, in seconds, to ignore old user_bucket values.
  • events_dedup_mode - Optional. Flag to indicate which user_bucket value to use when a single event source sends different user_bucket values for the same user. The default value is latest.
  • event_source_preference - Optional. Flag to indicate which user_bucket to use when multiple event sources send different user_bucket values for the same user. The default value is latest.

Example

{
   "user_bucket": {
      "event_sources": [<pixel_id>,<app_id>,...],
      "event_retention": 604800, // optional, exclude old events
      
      //optional, useful when multiple event sources have user_bucket
      "events_dedup_mode": "max"|"min"|"latest",
      
      //optional, dedup user_bucket values sent from one single event source
      "event_source_preference": "max"|"min"|"latest",
      
      "1":0.7, // these are the bid multipliers
      "2":1.0,
   }
 }

Cascade Multiple Breakdowns

You can also define user groups with multiple breakdowns by replacing a floating bid multiplier number with a further breakdown specification. For example, an adset with USD 5 max bid and below config would look like this:

  • bid USD 4.5 for male users
  • bid USD 3.5 for female users aged between 18 and 25
  • bid USD 4.0 for female users aged between 26 and 35
  • bid USD 5.0 for all other users
{
  "gender": {
    "male": 0.9,
    "female": {
      "age": {
        "18-25": 0.7,
        "26-35": 0.8
      }
    }
  }
}

Recommendations

We recommend to use accelerated delivery (disable pacing). With standard pacing, we're aiming to spend your budget evenly across a day. Our algorithm takes your max bid into consideration and sorts out a best spending strategy for you.

API

Get

curl -G \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/<API_VERSION>/<ADSET_ID>?fields=bid_adjustments

Update

curl \
  -F 'bid_adjustments={"user_groups":<USER_GROUP_WEIGHT_SPEC>}' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/<API_VERSION>/<PRODUCT_CATALOG_ID>/categories

For user group weight spec, we now support these breakdowns:

  • gender
  • age
  • device_platform
  • publisher_platform
  • user_device
  • user_os

The user group weight spec is a JSON structure, and looks like this:

{
  <BREAKDOWN_NAME>: {
    <BREAKDOWN_VALUE_1>: <WEIGHT_VALUE> OR <NESTED_USER_GROUP_WEIGHT_SPEC>,
    <BREAKDOWN_VALUE_2>: <WEIGHT_VALUE> OR <NESTED_USER_GROUP_WEIGHT_SPEC>,
    <BREAKDOWN_VALUE_3>: <WEIGHT_VALUE> OR <NESTED_USER_GROUP_WEIGHT_SPEC>,
    ...
    "default": <WEIGHT_VALUE> OR <NESTED_USER_GROUP_WEIGHT_SPEC>, // optional, if not specified, we'll use 1.0
  }
}

Example

{
  "age": {
    "18-25": 0.7,
    "26-35": {
      "gender": {
        "male": 0.9,
        "female": 1.0
      }
    },
    "default": 0.85
  }
}
  • For users between the ages of 18 and 25, we apply bid multiplier 0.7.
  • For male users between the ages of 26 and 35, we apply bid multiplier 0.9.
  • For female users between the ages of 26 and 35, we apply bid multiplier 1.0.
  • For all other users, we apply bid multiplier 0.85.

Breakdown values are integer age ranges; for example, 18-25, where the minimum age is 18 and ranges shouldn't overlap.

Example

{
  "age": {
    "18-25": 0.7,
    "30-40": 1.0,
    "default": 0.3
  }
}

Example

> curl -F 'bid_adjustments={"user_groups":{"user_device":{"iPad":0.3,"iPhone":{"user_os":{"Android":0.3,"iOS":{"age":{"18-35":0.3,"35-45":0.4}}}}}}}' -F access_token=$token https://graph.intern.facebook.com/v2.12/6089875504114
{"success":true}%
> curl -G -d access_token=$token https://graph.intern.facebook.com/v2.12/6089875504114\?fields\=bid_adjustments
{"bid_adjustments":{"user_groups":"{\"user_device\":{\"iPad\":0.3,\"iPhone\":{\"user_os\":{\"Android\":0.3,\"iOS\":{\"age\":{\"18-35\":0.3,\"35-45\":0.4}}}}}}"},"id":"6089875504114"}%

Create or Update Bid Multiplier for an Ad Set

Below is a sample API call to update an existing ad set (you'll need to substitute in an ad set ID, access-token, event sources and your desired bid multipliers):

curl -X POST \ -F 'bid_adjustments={"user_groups":{"user_bucket":{"event_sources":["<PIXEL_ID>","<APP_ID>"],"1":0.01,"2":0.02,"3":0.03,"default":{"gender":{"male":0.9996,"female":0.0129}}}}}' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.0/<AD_SET_ID>/
const adsSdk = require('facebook-nodejs-ads-sdk'); 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 fields = [ ]; const params = { 'bid_adjustments' : {'user_groups':{'user_bucket':{'event_sources':['<pixelID>','<appID>'],'1':0.01,'2':0.02,'3':0.03,'default':{'gender':{'male':0.9996,'female':0.0129}}}}}, }; (new AdSet(id)).update( fields, params ) .then((result) => { sample_code_id = result.id; console.log(sample_code_id); }) .catch((error) => { console.log(error); });
require __DIR__ . '/vendor/autoload.php'; 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( 'bid_adjustments' => array('user_groups' => array('user_bucket' => array('event_sources' => array('<pixelID>','<appID>'),'1' => 0.01,'2' => 0.02,'3' => 0.03,'default' => array('gender' => array('male' => 0.9996,'female' => 0.0129))))), ); echo json_encode((new AdSet($id))->update( $fields, $params )->getResponse()->getContent(), JSON_PRETTY_PRINT);
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 = { 'bid_adjustments': {'user_groups':{'user_bucket':{'event_sources':['<pixelID>','<appID>'],'1':0.01,'2':0.02,'3':0.03,'default':{'gender':{'male':0.9996,'female':0.0129}}}}}, } print AdSet(id).update( 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 AdSet(id, context).update() .setParam(\"bid_adjustments\", \"{\\"user_groups\\":{\\"user_bucket\\":{\\"event_sources\\":[\\"<pixelID>\\",\\"<appID>\\"],\\"1\\":0.01,\\"2\\":0.02,\\"3\\":0.03,\\"default\\":{\\"gender\\":{\\"male\\":0.9996,\\"female\\":0.0129}}}}}\") .execute(); } }
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_set = FacebookAds::AdSet.get(id) ad_set.bid_adjustments = {'user_groups':{'user_bucket':{'event_sources':['<pixelID>','<appID>'],'1':0.01,'2':0.02,'3':0.03,'default':{'gender':{'male':0.9996,'female':0.0129}}}}} ad_set.save

Set Up Test Campaign

For campaign setup, you should create your test campaigns using a conversion lift or split test, working with your Facebook account team and measurement lead. To assess performance, we suggest the following cells in a 3-way split test:

  • Cell 1 (Control A), Campaign 1 - 1 ad set with 1 bid for all targeted users
  • Cell 2 (Control B), Campaign 2 - Multiple ad sets where we create one ad set for each user group. (you can use Website/Mobile Custom Audiences rules to segment based on pixel/app parameters).
  • Cell 3 (Test), Campaign 3 - 1 ad set, with different bid multipliers for different user groups. Make sure the adjusted bid for each user group here is the same as the ad set level bid for the corresponding ad set in Cell 2.

Set Up Split Tests

At this point, you should set up split testing and control groups to verify performance using bid multipler. We run the tests for one to two weeks, and do not require a minimum budget. Set up split testing:

  • Cell 1, Control A Should contain one ad set with one bid amount and targeting should be based on all user_buckets.
  • Cell 2, Control B Should contain multiple ad sets, with one ad set per user_bucket. Each ad set should target a separate user_bucket and corresponding bid. You can use website or mobile custom Audiences rules to further segment your audience based on other Facebook Pixel or App Events parameters. See Website Custom Audiences and Mobile App Custom Audiences.
  • Cell 3, Test Contains one ad set with your bid_adjustment property to determine different bids for each user_bucket. Your multipliers should correspond to individual ad set bids for Cell 2, Control B.

For more information, see Split Testing.

Set Up Test Campaign

You should set up an ad campaign as you do normal campaigns. Contact your Facebook representative for help setting up your bid adjustment. You set up user_groups and different bid multipliers for each group. Your groups are based on demographic properties such as age, gender and specific properties such as user score or loyalty program. For example:

{
   "user_bucket":{
      "business_id":253286871795863,
      "1":0.7,
      "2":1.0,
      "3":{
         "gender":{
            "male":1.2,
            "female":0.8
         }
      }
      "default": 0.9
   }

In this example:

  • For people with loyalty_program value 1, apply bid multiplier 0.7
  • For people with loyalty_program value 2, apply bid multiplier 1.0
  • For males with loyalty_program value 3, apply bid multiplier 1.2
  • For females with loyalty_program value 3, apply bid multiplier 0.8
  • For people with loyalty_program other than 1, 2 or 3, apply bid multiplier 0.9
  • For all others, apply bid multiplier 1.0

Now you can create the ad campaign:

curl -X POST \ -F 'name=My Adset with bid multiplier' \ -F 'campaign_id=<AD_CAMPAIGN_ID>' \ -F 'daily_budget=3000' \ -F 'billing_event=IMPRESSIONS' \ -F 'optimization_goal=OFFSITE_CONVERSIONS' \ -F 'bid_amount=500' \ -F 'bid_adjustments={"user_groups":{"gender":{"male":0.8,"female":1}}}' \ -F 'promoted_object={"product_set_id":"<PRODUCT_SET_ID>","custom_event_type":"ADD_TO_CART"}' \ -F 'targeting={"geo_locations":{"countries":["US"]}}' \ -F 'status=PAUSED' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.0/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 fields = [ ]; const params = { 'name' : 'My Adset with bid multiplier', 'campaign_id' : '<adCampaignLinkClicksID>', 'daily_budget' : '3000', 'billing_event' : 'IMPRESSIONS', 'optimization_goal' : 'OFFSITE_CONVERSIONS', 'bid_amount' : '500', 'bid_adjustments' : {'user_groups':{'gender':{'male':0.8,'female':1}}}, 'promoted_object' : {'product_set_id':'<productSetID>','custom_event_type':'ADD_TO_CART'}, 'targeting' : {'geo_locations':{'countries':['US']}}, 'status' : 'PAUSED', }; (new AdAccount(id)).createAdSet( fields, params ) .then((result) => { adsets_id = result.id; console.log(adsets_id); }) .catch((error) => { console.log(error); });
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' => 'My Adset with bid multiplier', 'campaign_id' => '<adCampaignLinkClicksID>', 'daily_budget' => '3000', 'billing_event' => 'IMPRESSIONS', 'optimization_goal' => 'OFFSITE_CONVERSIONS', 'bid_amount' => '500', 'bid_adjustments' => array('user_groups' => array('gender' => array('male' => 0.8,'female' => 1))), 'promoted_object' => array('product_set_id' => '<productSetID>','custom_event_type' => 'ADD_TO_CART'), 'targeting' => array('geo_locations' => array('countries' => array('US'))), 'status' => 'PAUSED', ); echo json_encode((new AdAccount($id))->createAdSet( $fields, $params )->getResponse()->getContent(), 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': 'My Adset with bid multiplier', 'campaign_id': '<adCampaignLinkClicksID>', 'daily_budget': '3000', 'billing_event': 'IMPRESSIONS', 'optimization_goal': 'OFFSITE_CONVERSIONS', 'bid_amount': '500', 'bid_adjustments': {'user_groups':{'gender':{'male':0.8,'female':1}}}, 'promoted_object': {'product_set_id':'<productSetID>','custom_event_type':'ADD_TO_CART'}, 'targeting': {'geo_locations':{'countries':['US']}}, 'status': 'PAUSED', } 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(\"My Adset with bid multiplier\") .setCampaignId(\"<adCampaignLinkClicksID>\") .setDailyBudget(3000L) .setBillingEvent(AdSet.EnumBillingEvent.VALUE_IMPRESSIONS) .setOptimizationGoal(AdSet.EnumOptimizationGoal.VALUE_OFFSITE_CONVERSIONS) .setBidAmount(500L) .setParam(\"bid_adjustments\", \"{\\"user_groups\\":{\\"gender\\":{\\"male\\":0.8,\\"female\\":1}}}\") .setPromotedObject(\"{\\"product_set_id\\":\\"<productSetID>\\",\\"custom_event_type\\":\\"ADD_TO_CART\\"}\") .setTargeting( new Targeting() .setFieldGeoLocations( new TargetingGeoLocation() .setFieldCountries(Arrays.asList(\"US\")) ) ) .setStatus(AdSet.EnumStatus.VALUE_PAUSED) .execute(); } }
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) ad_account.adsets.create({ name: 'My Adset with bid multiplier', campaign_id: '<adCampaignLinkClicksID>', daily_budget: '3000', billing_event: 'IMPRESSIONS', optimization_goal: 'OFFSITE_CONVERSIONS', bid_amount: '500', bid_adjustments: {'user_groups':{'gender':{'male':0.8,'female':1}}}, promoted_object: {'product_set_id':'<productSetID>','custom_event_type':'ADD_TO_CART'}, targeting: {'geo_locations':{'countries':['US']}}, status: 'PAUSED', })

Once you create your ad sets, you should set bid_adjustments on the ad set via the API. The parameter should look something like this:

{
  "user_groups": {
    "user_bucket": {
      "event_sources": [<pixel_id>,<app_id>,...],
      "event_source_preference": "max"|"min"|"latest", //optional, useful when multiple event sources have user_bucket
      "events_dedup_mode": "max"|"min"|"latest", //optional, dedup user_bucket values sent from one single event source
      "event_retention": 604800, // optional, exclude old events
      "1":0.7, // these are the bid multipliers
      "2":1.0,
      "default": { // used in the case that we don't have user_bucket for that user
         "gender":{ // this is a nested example, but you could just do "default": 1.0,
            "male": {
              geo_location: {
                  cities: { // city id, can be found in search API, eg: search?type=adgeolocation&q=Menlo Park&limit=10
                    2420605 => 0.2,
                  },
                  regions: {
                     3847 => 0.5
                  },
                  countries: {
                    US => 0.2
                  }
                }
              }
            },
            "female": {
              "user_recency": {
                "event_sources": [<pixel_id>,<app_id>,...],
                "0-86400": 0.5,
                "86400-172800": 0.7,
                "default": 0.2
              }
            }
         },
      },
   },
}

We do not currenctly validate this parameter at present so send your Facebook representative your ad set ID and we can manually check it.

For each event source, the most recently received user_bucket for that user is used. But if multiple event sources are specified (e.g. pixel and app) and two or more of them have their own user_bucket recorded, then we take the max of them all.

Here is an example call to update an existing ad set. You should provide your own ad set ID, access-token, event sources and bid multipliers:

curl -X POST -F access_token=<access_token> \
-F 'bid_adjustments={"user_groups": {"user_bucket": {"event_sources": [<pixel_id>,<app_id>,...], "1": 0.01, "2": 0.02, "3": 0.03, "default": {"gender": {"male": 0.9996, "female": 0.0129}} }} }' \ 
https://graph.facebook.com/<VERSION>/<ad-set-id>

You can use Graph Explorer to do this, see Graph Explorer, Bid Multiplier. Expand the bid_adjustments field to see the entire set of parameters.