Dynamic Creative Optimization

Automatically deliver different variations of an ad's creative. This helps you find the best ad creative combination per impression by taking your ad's images, titles, descriptions, and other assets, then learning from outcomes of how these combinations perform across audiences.

Build and display the best creatives for groups of people in your broader target audience. This improves your ability to efficiently explore numerous creative combinations and audiences. Dynamic Creative can improve ads ROI by:

  • Automating workflow used to test creative
  • Choosing the effective combination of creative assets through learning across audiences

High-Level Steps

Optimization

The optimization is different between Dynamic Creative Optimization and the ads in our current system. If you create various image-title combinations today, our delivery system picks the best performing, fully-formed ad and puts the majority of spend behind that ad. Creative optimization is happening at a fixed, and fully formed ad within the ad set. With Dynamic Creative, provide creative optimization for each individual, creative asset such as image, title, body, and then converge on the best creative asset combination per impression.

Dynamic Creative Optimization is a workflow to run actual ad campaigns and determine the best performing creatives. It is ideal for new, ongoing campaigns, or campaigns that run longer than 5 days. We recommend that you perform split testing of your Dynamic Creative ads with your existing campaigns to see which approach performs better for your needs.

Dynamic Creative optimizes with more advertiser data over time. So you should be able to consistently get higher ROI using Dynamic Creative compared to the standard ads delivery system.

Asset Feed Spec

Provide an ad creative with asset_feed_spec:

curl -X POST \ -F 'name="Dynamic Ad Creative with Asset Feed Spec Sample"' \ -F 'object_story_spec={ "page_id": "<PAGE_ID>" }' \ -F 'asset_feed_spec={ "images": [ { "hash": "<IMAGE_HASH>" } ], "bodies": [ { "text": "Begin Your Adventure" }, { "text": "Once a Trainer, always a Trainer." } ], "titles": [ { "text": "Level Up" }, { "text": "Swipe to evolve" } ], "descriptions": [ { "text": "First Dynamic Ad Creative Sample" } ], "ad_formats": [ "SINGLE_IMAGE" ], "call_to_action_types": [ "SHOP_NOW" ], "link_urls": [ { "website_url": "https://www.example.com/" } ], "videos": [] }' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.3/act_<AD_ACCOUNT_ID>/adcreatives
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const AdAccount = bizSdk.AdAccount; const AdCreative = bizSdk.AdCreative; 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' : 'Dynamic Ad Creative with Asset Feed Spec Sample', 'object_story_spec' : {'page_id':'<pageID>'}, 'asset_feed_spec' : {'images':[{'hash':'<imageHash>'}],'bodies':[{'text':'Begin Your Adventure'},{'text':'Once a Trainer, always a Trainer.'}],'titles':[{'text':'Level Up'},{'text':'Swipe to evolve'}],'descriptions':[{'text':'First Dynamic Ad Creative Sample'}],'ad_formats':['SINGLE_IMAGE'],'call_to_action_types':['SHOP_NOW'],'link_urls':[{'website_url':'https://www.example.com/'}],'videos':[]}, }; const adcreatives = (new AdAccount(id)).createAdCreative( fields, params ); logApiCallResult('adcreatives api call complete.', adcreatives);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\AdAccount; use FacebookAds\Object\AdCreative; 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' => 'Dynamic Ad Creative with Asset Feed Spec Sample', 'object_story_spec' => array('page_id' => '<pageID>'), 'asset_feed_spec' => array('images' => array(array('hash' => '<imageHash>')),'bodies' => array(array('text' => 'Begin Your Adventure'),array('text' => 'Once a Trainer, always a Trainer.')),'titles' => array(array('text' => 'Level Up'),array('text' => 'Swipe to evolve')),'descriptions' => array(array('text' => 'First Dynamic Ad Creative Sample')),'ad_formats' => array('SINGLE_IMAGE'),'call_to_action_types' => array('SHOP_NOW'),'link_urls' => array(array('website_url' => 'https://www.example.com/')),'videos' => array()), ); echo json_encode((new AdAccount($id))->createAdCreative( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.adaccount import AdAccount from facebook_business.adobjects.adcreative import AdCreative 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': 'Dynamic Ad Creative with Asset Feed Spec Sample', 'object_story_spec': {'page_id':'<pageID>'}, 'asset_feed_spec': {'images':[{'hash':'<imageHash>'}],'bodies':[{'text':'Begin Your Adventure'},{'text':'Once a Trainer, always a Trainer.'}],'titles':[{'text':'Level Up'},{'text':'Swipe to evolve'}],'descriptions':[{'text':'First Dynamic Ad Creative Sample'}],'ad_formats':['SINGLE_IMAGE'],'call_to_action_types':['SHOP_NOW'],'link_urls':[{'website_url':'https://www.example.com/'}],'videos':[]}, } print AdAccount(id).create_ad_creative( 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 = \"<AD_ACCOUNT_ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new AdAccount(id, context).createAdCreative() .setName(\"Dynamic Ad Creative with Asset Feed Spec Sample\") .setObjectStorySpec( new AdCreativeObjectStorySpec() .setFieldPageId(\"<pageID>\") ) .setAssetFeedSpec(\"{\\"images\\":[{\\"hash\\":\\"<imageHash>\\"}],\\"bodies\\":[{\\"text\\":\\"Begin Your Adventure\\"},{\\"text\\":\\"Once a Trainer, always a Trainer.\\"}],\\"titles\\":[{\\"text\\":\\"Level Up\\"},{\\"text\\":\\"Swipe to evolve\\"}],\\"descriptions\\":[{\\"text\\":\\"First Dynamic Ad Creative Sample\\"}],\\"ad_formats\\":[\\"SINGLE_IMAGE\\"],\\"call_to_action_types\\":[\\"SHOP_NOW\\"],\\"link_urls\\":[{\\"website_url\\":\\"https://www.example.com/\\"}],\\"videos\\":[]}\") .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) adcreatives = ad_account.adcreatives.create({ name: 'Dynamic Ad Creative with Asset Feed Spec Sample', object_story_spec: {'page_id':'<pageID>'}, asset_feed_spec: {'images':[{'hash':'<imageHash>'}],'bodies':[{'text':'Begin Your Adventure'},{'text':'Once a Trainer, always a Trainer.'}],'titles':[{'text':'Level Up'},{'text':'Swipe to evolve'}],'descriptions':[{'text':'First Dynamic Ad Creative Sample'}],'ad_formats':['SINGLE_IMAGE'],'call_to_action_types':['SHOP_NOW'],'link_urls':[{'website_url':'https://www.example.com/'}],'videos':[]}, })

See also all available options for Asset Feed Spec.

To verify this, read asset_feed_spec from the ad creative:

curl -X GET \ -d 'fields="asset_feed_spec"' \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.3/<CREATIVE_ID>/
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const AdCreative = bizSdk.AdCreative; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<AD_CREATIVE_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 = [ 'asset_feed_spec', ]; params = { }; const sample_code = (new AdCreative(id)).get( fields, params ); logApiCallResult('sample_code api call complete.', sample_code);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\AdCreative; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<AD_CREATIVE_ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( 'asset_feed_spec', ); $params = array( ); echo json_encode((new AdCreative($id))->getSelf( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.adcreative import AdCreative from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_CREATIVE_ID>' FacebookAdsApi.init(access_token=access_token) fields = [ 'asset_feed_spec', ] params = { } print AdCreative(id).get( 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 = \"<AD_CREATIVE_ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new AdCreative(id, context).get() .requestAssetFeedSpecField() .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_CREATIVE_ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end ad_creative = FacebookAds::AdCreative.get(id ,'asset_feed_spec')

The response looks like this:

{  
   "asset_feed_spec":{  
      "images":[  
         {  
            "url_tags":"image=image1",
            "hash":"785095162a2034666e0d0cc4ea1faf89"
         },
         {  
            "url_tags":"image=image2",
            "hash":"3a24122c13923569599be35567ce4e9e"
         }
      ],
      "bodies":[  
         {  
            "text":"Begin Your Adventure"
         },
         {  
            "text":"Once a Trainer, always a Trainer."
         }
      ],
      "call_to_action_types":[  
         "LEARN_MORE"
      ],
      "call_to_actions":[  
         {  
            "type":"LEARN_MORE"
         }
      ],
      "descriptions":[  
         {  
            "text":"Begin Your Adventure"
         }
      ],
      "link_urls":[  
         {  
            "website_url":"<WEBSITE_URL>"
         }
      ],
      "titles":[  
         {  
            "text":"Swipe to evolve"
         },
         {  
            "text":"Level Up"
         }
      ],
      "ad_formats":[  
         "SINGLE_IMAGE"
      ],
      "optimization_type":"REGULAR"
   },
   "id":"<AD_CREATIVE_ID>",
}

Or for example, create asset_feed_spec with two alternate videos, bodies, and titles:

curl 
-F 'object_story_spec={
       "page_id": "YOUR_PAGE_ID"
       "instagram_actor_id" : "INSTAGRAM_ACTOR_ID",
    }' 
-F "asset_feed_spec={'videos': [{'video_id':'2053108854721025', 'thumbnail_url':'<thumnail_url>', 'url_tags':'video=video1'},{'video_id':'2104406249780616', 'thumbnail_url':'<thumnail_url>','url_tags':'video=video2'}], 'bodies': [{'text':'Begin Your Adventure'}, {'text':'Once a Trainer, always a Trainer.'}], 'titles': [{'text':'Level Up'}, {'text':'Swipe to evolve'}], 'descriptions': [{'text':'Begin Your Adventure'}], 'ad_formats': ['SINGLE_IMAGE'], 'link_urls': [{'website_url':'<WEBSITE_URL>'}]}"
-F 'access_token=<ACCESS_TOKEN>'  
https://graph.facebook.com/API_VERSION/act_AD_ACCOUNT_ID/adcreatives

To verify you can use the asset_feed_spec, read it:

curl -G 
-d "access_token=ACCESS_TOKEN"
-d "fields=asset_feed_spec" 
https://graph.facebook.com/API_VERSION/AD_CREATIVE_ID

The response contains:

{  
   "asset_feed_spec":{  
      "videos":[  
         {  
            "url_tags":"video=video1",
            "video_id":"2053108854721025",
            "thumbnail_url":"<thumnail_url>",
            "thumbnail_hash":"<thumnail_hash>"
         },
         {  
            "url_tags":"video=video2",
            "video_id":"2104406249780616",
            "thumbnail_url":"<thumnail_url>",
            "thumbnail_hash":"<thumnail_hash>"
         }
      ],
      "bodies":[  
         {  
            "text":"Begin Your Adventure"
         },
         {  
            "text":"Once a Trainer, always a Trainer."
         }
      ],
      "call_to_action_types":[  
         "LEARN_MORE"
      ],
      "call_to_actions":[  
         {  
            "type":"LEARN_MORE"
         }
      ],
      "descriptions":[  
         {  
            "text":"Begin Your Adventure"
         }
      ],
      "link_urls":[  
         {  
            "website_url":"<WEBSITE_URL>"
         }
      ],
      "titles":[  
         {  
            "text":"Swipe to evolve"
         },
         {  
            "text":"Level Up"
         }
      ],
      "ad_formats":[  
         "SINGLE_VIDEO"
      ],
      "optimization_type":"REGULAR"
   },
   "id":"<AD_CREATIVE_ID>",
}

Edit Ad Creative

You can edit the ad creative to add, replace, or remove any of the assets. To do so, provide a new creative with your new asset feed spec, and we deliver the new assets. Then for the same type of asset in asset_feed_spec, such as image hashes, we report how the assets perform as original ones.

When you create a new ad creative to replace an old one, you must still fulfill all restrictions which apply.

We reuse assets based on asset value and type. For example, if you have two asset_feed_specs with the same image hash, and you update one asset_feed_spec with another in your ad, you see combined impressions for that asset in the asset breakdowns.

You can add new assets, remove existing assets and replace assets with completely new ones. However, you cannot change formats such as changing SINGLE IMAGE to VIDEO. You also cannot update a asset-feed based creative ad to be a non-Dynamic Creative ad which has no asset_feed_spec in the ad creative.

curl 
  -F 'access_token=ACCESS_TOKEN' 
  -F 'creative={
      "creative_id": CREATIVE_ID,
   }' 
https://graph.facebook.com/VERSION/AD_ID

Carousel Ads

Dynamic Creative delivers the best combination of assets in carousel ad format. The number of cards is the number of images in the asset feed if less than 10, otherwise we display 10. In asset insights breakdown, for in-card assets, such as image, title, and description, we aggregate metrics when the asset is in the first card. We recommend square sizes for images.

If you are using the carousel ads format, you cannot use these features from carousel ads:

  • BODY_LABEL
  • CALL_TO_ACTION_TYPE_LABEL
  • LINK_URL_LABEL
  • CAPTION_LABEL
  • AD_FORMAT_LABEL

For background information, see Carousel ads.

Image Cropping

Dynamic creative supports image cropping. Specify cropping for your image definition in images. You can provide only one crop per image. We apply your crops to all placements of your image. See Marketing API, Image Cropping.