Creative, Placement, and Preview

Use Facebook ads with your existing customers and to reach new ones. Each guide describes Facebook's ads products to help meet your advertising goals. The are several types of ad units with a variety of appearances, placement and creative options. For guidelines on ads units as creative content see Facebook Ads Guide.

Breaking Change: Event Ads, Link Ads not Associated with Valid Page

We recently announced an initiative to make the Facebook Advertising platform more transparent to Facebook users. Read more about this in the Facebook press release

To support this initiative, we are deprecating deprecating Event Ads and Link Ads that are not connected to a valid page from Marketing API.

This breaking change impacts all supported API versions, including the upcoming Marketing API version v2.11, and v2.10 and v2.9 which are available but will be deprecated. This breaking change will take effect the second week of November 2017.

You will no longer be able to create or edit Event Ads and Link Ads that are not connected to a valid page. Requests will do so return the error: ErrorCode::ADPRO2__AD_MUST_HAVE_PAGE (1885833)

The following ad options used together will fail:

  • Event Ads
  • Objective: EVENT_RESPONSES
  • Creative fields: body, object_id
  • Link Ads
  • Objective: LINK_CLICKS
  • Creative fields: title, body, object_url containing image_file or image_hash

You can still create Event Ads and Link Ads if you provide a valid actor_id in the ad creative's object_story_id or object_story_spec fields

These options used together are valid:

  • Event Ads
  • Objective: EVENT_RESPONSES
  • Creative fields: object_story_id or object_story_spec
  • Link Ads
  • Objective: LINK_CLICKS
  • Creative fields: object_story_id or object_story_spec

The nodes, edges and requests impacted are:

Any pre-existing Event or Link Ads continue to run but you cannnot modify these ad's creatives or create new ads with the invalid options once the change goes in effect.

Creative

An ad creative is an object that contains all the data for visually rendering the ad itself. In the API, there are different types of ads that you can create on Facebook, all listed here.

Suppose you have a campaign with Page Post Engagement Objective, you will now create an ad that promotes a post made by the page. This is considered a Page post ad. Page post ads require a field called object_story_id, which is the id property of a Page post, see Ad Creative, Reference

Ad creative has three parts:

  • Ad creative itself, defined by the visual attributes of the creative object;
  • Placement that the ad will run on;
  • Preview of the unit itself, per placement.

To create the ad creative object make the following call:

use FacebookAds\Object\AdCreative;
use FacebookAds\Object\Fields\AdCreativeFields;

$creative = new AdCreative(null, 'act_<AD_ACCOUNT_ID>');

$creative->setData(array(
  AdCreativeFields::NAME => 'Sample Promoted Post',
  AdCreativeFields::OBJECT_STORY_ID => <POST_ID>,
));

$creative->create();
from facebookads.adobjects.adcreative import AdCreative

creative = AdCreative(parent_id='act_<AD_ACCOUNT_ID>')
creative[AdCreative.Field.object_story_id] = <POST_ID>
creative[AdCreative.Field.name] = 'AdCreative with post ID'

creative.remote_create()
print(creative)
AdCreative adCreative = new AdAccount(act_<AD_ACCOUNT_ID>, context).createAdCreative()
  .setName("Sample Promoted Post")
  .setObjectStoryId(object_story_id)
  .execute();
String ad_creative_id = adCreative.getId();
curl \
  -F 'name=Sample Promoted Post' \
  -F 'object_story_id=<POST_ID>' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/adcreatives

The response to the API call will be the id of the creative object. Store this, as you'll need it for the ad object:

use FacebookAds\Object\Ad;
use FacebookAds\Object\Fields\AdFields;

$data = array(
  AdFields::NAME => 'My Ad',
  AdFields::ADSET_ID => <AD_SET_ID>,
  AdFields::CREATIVE => array(
    'creative_id' => <CREATIVE_ID>,
  ),
);

$ad = new Ad(null, 'act_<AD_ACCOUNT_ID>');
$ad->setData($data);
$ad->create(array(
  Ad::STATUS_PARAM_NAME => Ad::STATUS_PAUSED,
));
from facebookads.adobjects.ad import Ad

ad = Ad(parent_id='act_<AD_ACCOUNT_ID>')
ad[Ad.Field.name] = 'My Ad'
ad[Ad.Field.adset_id] = <AD_SET_ID>
ad[Ad.Field.creative] = {
    'creative_id': <CREATIVE_ID>,
}
ad.remote_create(params={
    'status': Ad.Status.paused,
})
Ad ad = new AdAccount(act_<AD_ACCOUNT_ID>, context).createAd()
  .setName("My Ad")
  .setAdsetId(<AD_SET_ID>)
  .setCreative(
    new AdCreative()
      .setFieldId(<CREATIVE_ID>)
  )
  .setStatus(Ad.EnumStatus.VALUE_PAUSED)
  .execute();
curl \
  -F 'name=My Ad' \
  -F 'adset_id=<AD_SET_ID>' \
  -F 'creative={"creative_id":"<CREATIVE_ID>"}' \
  -F 'status=PAUSED' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/ads

Creative Limits

There are limits on the creative's text, image size, image aspect ratio, and other aspects of the creative, see the Ads Guide.

Read

In the Ads API, each field you wish to retrieve needs to be asked for explicitly, except for id. Each object's Reference has a section for reading back the object and lists what fields are readable. For the creative, it's the same fields as specified when creating the object, and id:

use FacebookAds\Object\AdCreative;
use FacebookAds\Object\Fields\AdCreativeFields;

$creative = new AdCreative(<CREATIVE_ID>);
$creative->read(array(
  AdCreativeFields::NAME,
  AdCreativeFields::OBJECT_STORY_ID,
));
from facebookads.adobjects.adcreative import AdCreative

creative = AdCreative(<CREATIVE_ID>)
creative.remote_read(
    fields=[AdCreative.Field.name, AdCreative.Field.object_story_id]
)
AdCreative adCreative2 = new AdCreative(<CREATIVE_ID>, context).get()
  .requestNameField()
  .requestObjectStoryIdField()
  .execute();
curl -G \
  -d 'fields=name,object_story_id' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/<CREATIVE_ID>

Placements

Placement is where your ad is shown on Facebook such as on News Feed on desktop, News Feed on a mobile device or on the right column, see Ads Product Guide.

In our example, we have page post ad, so the placements available are Mobile News Feed, Desktop News Feed, and Right column of Facebook. In the API, see Placement Options. Let's choose desktopfeed and rightcolumn as the page_type, so our ad runs on Desktop News Feed and Right column placements. Any ad created below this ad set will have only desktop placement as expected.

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 => 'Desktop Ad Set',
  AdSetFields::CAMPAIGN_ID => <CAMPAIGN_ID>,
  AdSetFields::DAILY_BUDGET => 10000,
  AdSetFields::TARGETING => (new Targeting())->setData(array(
    TargetingFields::PUBLISHER_PLATFORMS => array(
      'facebook',
      'audience_network',
    ),
    TargetingFields::GEO_LOCATIONS => array(
      'countries' => array('BR'),
    ),
  )),
  AdSetFields::OPTIMIZATION_GOAL =>
    AdSetOptimizationGoalValues::POST_ENGAGEMENT,
  AdSetFields::BILLING_EVENT => AdSetBillingEventValues::POST_ENGAGEMENT,
  AdSetFields::BID_AMOUNT => 1500,
));

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

ad_account = AdAccount(fbid='act_<AD_ACCOUNT_ID>')

params = {
    AdSet.Field.name: 'Desktop Ad Set',
    AdSet.Field.campaign_id: <CAMPAIGN_ID>,
    AdSet.Field.daily_budget: 10000,
    AdSet.Field.targeting: {
        Targeting.Field.publisher_platforms: ['facebook', 'audience_network'],
        Targeting.Field.facebook_positions: ['feed'],
        Targeting.Field.geo_locations: {
            'countries': ['BR'],
        },
    },
    AdSet.Field.optimization_goal: AdSet.OptimizationGoal.post_engagement,
    AdSet.Field.billing_event: AdSet.BillingEvent.post_engagement,
    AdSet.Field.bid_amount: 1500,
    AdSet.Field.status: AdSet.Status.active,
}
adset = ad_account.create_ad_set(params=params)
AdSet adSet = new AdAccount(act_<AD_ACCOUNT_ID>, context).createAdSet()
  .setName("Desktop Ad Set")
  .setCampaignId(<CAMPAIGN_ID>)
  .setDailyBudget(10000L)
  .setTargeting(
    new Targeting()
      .setFieldGeoLocations(
        new TargetingGeoLocation()
          .setFieldCountries(Arrays.asList("BR"))
      )
      .setFieldPublisherPlatforms(Arrays.asList("facebook", "audience_network"))
  )
  .setOptimizationGoal(AdSet.EnumOptimizationGoal.VALUE_POST_ENGAGEMENT)
  .setBillingEvent(AdSet.EnumBillingEvent.VALUE_POST_ENGAGEMENT)
  .setBidAmount(1500L)
  .setStatus(AdSet.EnumStatus.VALUE_PAUSED)
  .execute();
String ad_set_id = adSet.getId();
curl \
  -F 'name=Desktop Ad Set' \
  -F 'campaign_id=<CAMPAIGN_ID>' \
  -F 'daily_budget=10000' \
  -F 'targeting={ 
    "geo_locations": {"countries":["BR"]}, 
    "publisher_platforms": ["facebook","audience_network"] 
  }' \
  -F 'optimization_goal=POST_ENGAGEMENT' \
  -F 'billing_event=POST_ENGAGEMENT' \
  -F 'bid_amount=1500' \
  -F 'status=PAUSED' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/adsets

Ad Previews

There are two ways to do preview an ad: with ad preview API or the ad preview plugin. There are three ways to generate a preview with the API:

  1. By ad ID
  2. By ad creative ID
  3. By supplying a creative spec

Following the reference docs for the preview API, the minimum required API call is:

use FacebookAds\Object\AdAccount;
use FacebookAds\Object\Fields\AdPreviewFields;

$account = new AdAccount('act_<AD_ACCOUNT_ID>');
$account->getGeneratePreviews(array(), array(
  AdPreviewFields::CREATIVE => <CREATIVE_SPEC>,
  AdPreviewFields::AD_FORMAT => '<FORMAT>',
));
from facebookads.adobjects.adaccount import AdAccount

account = AdAccount('act_<AD_ACCOUNT_ID>')
params = {
    'creative': <CREATIVE_SPEC>,
    'ad_format': '<FORMAT>',
}
account.get_generate_previews(params=params)
APINodeList<AdPreview> adPreviews = new AdAccount(act_<AD_ACCOUNT_ID>, context).getGeneratePreviews()
  .setCreative(<CREATIVE_SPEC>)
  .setAdFormat(<FORMAT>)
  .execute();
curl -G \
  -d 'creative=<CREATIVE_SPEC>' \
  -d 'ad_format=<FORMAT>' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/generatepreviews

The creative spec is an array of each field and value required to create the ad creative.

Currently, our ad creative call looks like:

use FacebookAds\Object\AdCreative;
use FacebookAds\Object\Fields\AdCreativeFields;

$creative = new AdCreative(null, 'act_<AD_ACCOUNT_ID>');

$creative->setData(array(
  AdCreativeFields::NAME => 'Sample Promoted Post',
  AdCreativeFields::OBJECT_STORY_ID => <POST_ID>,
));

$creative->create();
from facebookads.adobjects.adcreative import AdCreative

creative = AdCreative(parent_id='act_<AD_ACCOUNT_ID>')
creative[AdCreative.Field.object_story_id] = <POST_ID>
creative[AdCreative.Field.name] = 'AdCreative with post ID'

creative.remote_create()
print(creative)
AdCreative adCreative = new AdAccount(act_<AD_ACCOUNT_ID>, context).createAdCreative()
  .setName("Sample Promoted Post")
  .setObjectStoryId(object_story_id)
  .execute();
String ad_creative_id = adCreative.getId();
curl \
  -F 'name=Sample Promoted Post' \
  -F 'object_story_id=<POST_ID>' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/adcreatives

Take object_story_id and use it in the preview API call:

use FacebookAds\Object\AdAccount;
use FacebookAds\Object\Fields\AdPreviewFields;
use FacebookAds\Object\Fields\AdCreativeFields;

$account = new AdAccount('act_<AD_ACCOUNT_ID>');
$account->getGeneratePreviews(array(), array(
  AdPreviewFields::CREATIVE => array(
    AdCreativeFields::OBJECT_STORY_ID => <POST_ID>,
  ),
  AdPreviewFields::AD_FORMAT => '<FORMAT>',
));
from facebookads.adobjects.adaccount import AdAccount
from facebookads.adobjects.adcreative import AdCreative

account = AdAccount('act_<AD_ACCOUNT_ID>')
params = {
    'creative': {
        AdCreative.Field.object_story_id: '<POST_ID>',
    },
    'ad_format': '<FORMAT>',
}
account.get_generate_previews(params=params)
APINodeList<AdPreview> adPreviews = new AdAccount(act_<AD_ACCOUNT_ID>, context).getGeneratePreviews()
  .setCreative(
    new AdCreative()
      .setFieldObjectStoryId(<OBJECT_STORY_ID>)
  )
  .setAdFormat(<FORMAT>)
  .execute();
curl -G \
  -d 'creative={"object_story_id":"<POST_ID>"}' \
  -d 'ad_format=<FORMAT>' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/generatepreviews

The available values for ad_format differ a bit from page_types, but since we have selected Desktop News Feed and Right column of Facebook, we should make two API calls to generate the previews for each placement:

use FacebookAds\Object\AdAccount;
use FacebookAds\Object\Fields\AdPreviewFields;
use FacebookAds\Object\Fields\AdCreativeFields;
use FacebookAds\Object\Values\AdPreviewAdFormatValues;

$account = new AdAccount('act_<AD_ACCOUNT_ID>');
$account->getGeneratePreviews(array(), array(
  AdPreviewFields::CREATIVE => array(
    AdCreativeFields::OBJECT_STORY_ID => <POST_ID>,
  ),
  AdPreviewFields::AD_FORMAT => AdPreviewAdFormatValues::DESKTOP_FEED_STANDARD,
));
from facebookads.adobjects.adaccount import AdAccount
from facebookads.adobjects.adpreview import AdPreview
from facebookads.adobjects.adcreative import AdCreative

account = AdAccount('act_<AD_ACCOUNT_ID>')
params = {
    'creative': {
        AdCreative.Field.object_story_id: '<POST_ID>',
    },
    'ad_format': AdPreview.AdFormat.desktop_feed_standard,
}
account.get_generate_previews(params=params)
APINodeList<AdPreview> adPreviews = new AdAccount(act_<AD_ACCOUNT_ID>, context).getGeneratePreviews()
  .setCreative(
    new AdCreative()
      .setFieldObjectStoryId(<OBJECT_STORY_ID>)
  )
  .setAdFormat(AdPreview.EnumAdFormat.VALUE_DESKTOP_FEED_STANDARD)
  .execute();
curl -G \
  -d 'creative={"object_story_id":"<POST_ID>"}' \
  -d 'ad_format=DESKTOP_FEED_STANDARD' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/generatepreviews
use FacebookAds\Object\AdAccount;
use FacebookAds\Object\Fields\AdPreviewFields;
use FacebookAds\Object\Fields\AdCreativeFields;
use FacebookAds\Object\Values\AdPreviewAdFormatValues;

$account = new AdAccount('act_<AD_ACCOUNT_ID>');
$account->getGeneratePreviews(array(), array(
  AdPreviewFields::CREATIVE => array(
    AdCreativeFields::OBJECT_STORY_ID => <POST_ID>,
  ),
  AdPreviewFields::AD_FORMAT => AdPreviewAdFormatValues::RIGHT_COLUMN_STANDARD,
));
from facebookads.adobjects.adaccount import AdAccount
from facebookads.adobjects.adpreview import AdPreview
from facebookads.adobjects.adcreative import AdCreative

account = AdAccount('act_<AD_ACCOUNT_ID>')
params = {
    'creative': {
        AdCreative.Field.object_story_id: '<POST_ID>',
    },
    'ad_format': AdPreview.AdFormat.right_column_standard,
}
account.get_generate_previews(params=params)
APINodeList<AdPreview> adPreviews = new AdAccount(act_<AD_ACCOUNT_ID>, context).getGeneratePreviews()
  .setCreative(
    new AdCreative()
      .setFieldObjectStoryId(<OBJECT_STORY_ID>)
  )
  .setAdFormat(AdPreview.EnumAdFormat.VALUE_RIGHT_COLUMN_STANDARD)
  .execute();
curl -G \
  -d 'creative={"object_story_id":"<POST_ID>"}' \
  -d 'ad_format=RIGHT_COLUMN_STANDARD' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.11/act_<AD_ACCOUNT_ID>/generatepreviews

The response is an iFrame that is valid for 24 hrs.

Copying Ads

You can also copy an existing ad, asset or campaign. It helps you to duplicate a campaigns faster to change configurations or create test groups to extract performance knowledge. See: