To create RoI-Based Budget Rebalancing rules, it is important to understand each individual component. Here, we look in depth at each component in the rebalance rule, and how each parameter affects how the rule runs.

Schedule Spec

For rebalancing rules, it is recommended to use either a DAILY or CUSTOM schedule, as the action should not be frequently occurring.

Evaluation Spec

The evaluation criteria will work harmoniously with the rebalance_spec to determine the lists of objects that will be affected by rebalancing. For all rebalance types, the list of objects that pass evaluation will be the source of budgets. The list of recipients vary depending on the rebalance type specified, but for most of them (e.g. EVEN) the recipients are the objects that did not pass evaluation. For example, if my EVEN type rule criteria is cost_per_mobile_app_install > 2.50, this means that all Ad Sets that have a cost per mobile app install greater than 2.50 will be paused, and their budgets moved to all Ad Sets that have a cost per mobile app install less than or equal to 2.50.

Execution Spec

The rebalance_spec will determine exactly how the recipients get their budget. There are five parameters here: type, target_field, target_count, is_cross_campaign, is_inverse.

Field Description Supported Values

type

Required field that determines how the budgets will be allocated. If the value is not EVEN, a target_field will be required as well to perform the ranking.

EVEN, PROPORTIONAL, NO_PAUSE_PROPORTIONAL, MATCHED_ONLY_PROPORTIONAL

target_field

Optional field that specifies the Insights metric used to rank the recipients. This is required if type is not EVEN, or if target_count also exists in the spec.

An Insights field, such as cpa or impressions

target_count

Optional field that specifies the number (K) of recipients. The combination of this, type, and target_field will determine the top K recipients that receive the budget. This is useful when you do not wish to move the budget to every possible recipient. If K is larger than the number of recipients, the rule will rebalance to all of them. If this is specified, target_field will be required.

A positive integer, such as 5

is_cross_campaign

Optional field that specifies whether or not we allow budgets to be allocated across Campaigns. If this is not specified or false, we will only move budgets within Campaigns. If this is true, we will evaluate and execute all Ad Sets together, which can result in budgets shifting between Campaigns.

A boolean value, such as true

is_inverse

Optional field that specifies whether or not recipients shall be ranked from high to low of the inverse of their target_field values. This is useful if we want to rank the lowest actual values highest.

A boolean value, such as true

Specific Nuances

There are some specific nuances regarding this action that we would like to clarify here.

  • If the set of Ad Sets to rebalance contains both Daily and Lifetime budgets, we separate the Ad Sets into the two buckets. This means that Ad Sets will only move their Daily budgets to other Ad Sets that have Daily budgets, and same with Lifetime.
  • For Ad Sets with Lifetime budgets, we take their leftover budget - the difference between their Lifetime budget and Lifetime spend - when determining the amount of budget they can allocate. This ensures that the total budget on the Campaign level is unchanged.
  • For EVEN and PROPORTIONAL types, we pause the matched objects (the donors of the budget to the recipients). When we pause these objects, we do not adjust their budgets in any way. The reason is twofold - we don't need to worry about their delivery since they are paused, and it does not make sense to have no budget on any Ad Set. This means that if you re-enable the Ad Set afterwards, it will retain the same budget that it had before. This can be seen when interacting with the paused object and fetching its budget data.
  • For NO_PAUSE_PROPORTIONAL type, we do not pause the matched objects. We determine how much budget do adjust by looking at all the objects (donors and recipients) together and ranking their performance. This still guarantees that budget will only be moved from donors to recipients, so that we don't end up with a situation where rebalancing results in a well-performing Ad Set donating instead to an underperforming Ad Set simply due to how much budget it has. Please see the example below for more information.
  • For MATCHED_ONLY_PROPORTIONAL type, we only look at the matched objects. Again, we do not pause them, and instead rank them among themselves and redistribute their budgets based on how they perform against each other. This means that we take the total budget from all the donors, and proportionally share that with the same list of donors. Please see the example below for more information.
  • For types ending in PROPORTIONAL, we distribute more budgets to Ad Sets that are performing better based on the target_field defined. For example, if the metric is reach and I have two recipient Ad Sets that have 10 and 20 reach, we allocate 33.3% and 66.6% of the budget pool to these Ad Sets, respectively. If the type is EVEN, they would each get 50%.
  • The is_inverse flag is useful for metrics such as cost_per_mobile_app_install, where a lower metric number means a higher performing Ad Set. This is reemphasized in the example below, and means that Ad Sets with a lower such value will get a higher portion of the budget allocation.

Here's an example of a rebalance rule. This rule will pause all under-performing Ad Sets in the Ad Account and shift their budgets to the rest, where we define under-performing as stably having a high cost_per_mobile_app_install. We will proportionally allocate the budget from all under-performing Ad Sets to the best 10 Ad Sets in the Ad Account. We will run this rule at 8AM every day, while looking at lifetime data.

curl \
-F 'name=Test Rebalance Rule' \
-F 'schedule_spec={
     "schedule_type": "CUSTOM",
     "schedule": [
       {
          "start_minute": 480
       }
     ]
   }' \
-F 'evaluation_spec={
     "evaluation_type": "SCHEDULE",
     "filters": [
       {
         "field": "entity_type",
         "value": "ADSET",
         "operator": "EQUAL"
       },
       {
         "field": "time_preset",
         "value": "LIFETIME",
         "operator": "EQUAL"
       },
       {
         "field": "mobile_app_install",
         "value": 100,
         "operator": "GREATER_THAN"
       },
       {
         "field": "cost_per_mobile_app_install",
         "value": 3.0,
         "operator": "GREATER_THAN"
       }
     ]
   }' \
-F 'execution_spec={
     "execution_type": "REBALANCE_BUDGET",
     "execution_options": [
       {
         "field": "rebalance_spec",
         "value": {
           "type": "INVERSE_PROPORTIONAL",
           "target_field": "cost_per_mobile_app_install",
           "target_count": 10,
           "is_cross_campaign": true
         },
         "operator": "EQUAL"
       },
     ]
   }' \
-F "access_token=<ACCESS_TOKEN>" \
https://graph.facebook.com/<VERSION>/<AD_ACCOUNT_ID>/adrules_library

Here's another example, where we pause and evenly rebalance the budget of all Ad Sets every day that have reached a high percentage of their audience size, but do not allow budgets to shift across Campaigns.

curl \
-F 'name=Test Rebalance Rule' \
-F 'schedule_spec={
     "schedule_type": "DAILY"
   }' \
-F 'evaluation_spec={
     "evaluation_type": "SCHEDULE",
     "filters": [
       {
         "field": "entity_type",
         "value": "ADSET",
         "operator": "EQUAL"
       },
       {
         "field": "time_preset",
         "value": "LIFETIME",
         "operator": "EQUAL"
       },
       {
         "field": "impressions",
         "value": 8000,
         "operator": "GREATER_THAN"
       },
       {
         "field": "audience_reached_percentage",
         "value": 70,
         "operator": "GREATER_THAN"
       }
     ]
   }' \
-F 'execution_spec={
     "execution_type": "REBALANCE_BUDGET",
     "execution_options": [
       {
         "field": "rebalance_spec",
         "value": {
           "type": "EVEN"
         },
         "operator": "EQUAL"
       },
     ]
   }' \
-F "access_token=<ACCESS_TOKEN>" \
https://graph.facebook.com/<VERSION>/<AD_ACCOUNT_ID>/adrules_library

Here's an example leveraging the NO_PAUSE_PROPORTIONAL type. In this case, we reallocate budget from Ad Sets within Campaigns from those with a low amount of video views. However, in this case we do not pause them, but rather also leave them with a proportional amount of budget.

Here's a numeric example of what happens:

Say I have Ad Sets 1-5 with video_view of 1-5, 3000 daily budget each, and the below rule. We first take the 6000 budget from Ad Sets 1 and 2, and then determine proportionally how to distribute that. In this case, we will have ratios of 1/15 up to 5/15 for each Ad Set. As a result, we end up with the Ad Sets having values of 400, 800, 4200, 4600, and 5000 respectively. This guarantees that the recipients (Ad Sets 1, 2, and 3) will always increase their budget.

curl \
-F 'name=Test Rebalance Rule' \
-F 'schedule_spec={
     "schedule_type": "DAILY"
   }' \
-F 'evaluation_spec={
     "evaluation_type": "SCHEDULE",
     "filters": [
       {
         "field": "entity_type",
         "value": "ADSET",
         "operator": "EQUAL"
       },
       {
         "field": "time_preset",
         "value": "LIFETIME",
         "operator": "EQUAL"
       },
       {
         "field": "video_view",
         "value": 3,
         "operator": "LESS_THAN"
       },
     ]
   }' \
-F 'execution_spec={
     "execution_type": "REBALANCE_BUDGET",
     "execution_options": [
       {
         "field": "rebalance_spec",
         "value": {
           "type": "NO_PAUSE_PROPORTIONAL",
           "target_field": "video_view"
         },
         "operator": "EQUAL"
       },
     ]
   }' \
-F "access_token=<ACCESS_TOKEN>" \
https://graph.facebook.com/<VERSION>/<AD_ACCOUNT_ID>/adrules_library

Finally, here's an example that leverages the MATCHED_ONLY_PROPORTIONAL type. In this case, we don't need to worry about unmatched objects, and can focus on Ad Sets that satisfy the rule's filters. For example, we can use the same example above, except now we don't need to determine the two lists based on how underperforming they are.

With the same numeric example above, we would end up using all their budgets in the pool (15000), and again distributing this proportionally. As a result, Ad Sets 1-5 above will end up with 1000-5000 budget.

The main downside to this type is that we can no longer guarantee that better performing Ad Sets won't end up losing budget, especially in cases of unbalanced budget values. All else being the same, if Ad Set 5 had started with 18000 budget instead, it would end up losing 8000 of its budget.

curl \
-F 'name=Test Rebalance Rule' \
-F 'schedule_spec={
     "schedule_type": "DAILY"
   }' \
-F 'evaluation_spec={
     "evaluation_type": "SCHEDULE",
     "filters": [
       {
         "field": "entity_type",
         "value": "ADSET",
         "operator": "EQUAL"
       },
       {
         "field": "time_preset",
         "value": "LIFETIME",
         "operator": "EQUAL"
       },
     ]
   }' \
-F 'execution_spec={
     "execution_type": "REBALANCE_BUDGET",
     "execution_options": [
       {
         "field": "rebalance_spec",
         "value": {
           "type": "MATCHED_ONLY_PROPORTIONAL",
           "target_field": "video_view"
         },
         "operator": "EQUAL"
       },
     ]
   }' \
-F "access_token=<ACCESS_TOKEN>" \
https://graph.facebook.com/<VERSION>/<AD_ACCOUNT_ID>/adrules_library