Instagram Ads

Overview

With Marketing API you can create, measure, and optimize ad on Instagram, either in the main Stream, or in the Stories:

  1. Get Instagram account ID - Connect an Instagram account with you business or Page, or create an ad-only Instagram account using a Facebook Page.
  2. Create ad campaign - Instagram ads do not support all Facebook ads objectives.
  3. Creating ad set - Pick a placement including Instagram. We recommend including both Facebook and Instagram, so our system automatically delivers ads to the best audiences on both platforms.
  4. Create ad creative - Not all creative formats supported by Facebook work on Instagram.
  5. Create ads - Call preview API to see how it looks, and call insights API to collect its stats after it's active.

Create Ad Campaign

You can run campaigns for most objectives in Instagram stream, however REACH, VIDEO_VIEWS, LINK_CLICKS, CONVERSIONS, and APP_INSTALLS are the only objectives valid for Instagram Stories. Check which objectives you can use in Instagram in Facebook Ads Guide.

For more predicatble reach, create a Reach & Frequency campaign with buying_type as RESERVED. Currently, REACH, LINK_CLICKS, POST_ENGAGEMENT, APP_INSTALLS, VIDEO_VIEWS, and BRAND_AWARENESS objectives are supported for Instagram Reach and Frequency ads.

use FacebookAds\Object\Campaign;
use FacebookAds\Object\Fields\CampaignFields;
use FacebookAds\Object\Values\CampaignObjectiveValues;

$campaign = new Campaign(null, 'act_<AD_ACCOUNT_ID>');
$campaign->setData(array(
  CampaignFields::NAME => 'My campaign',
  CampaignFields::OBJECTIVE => CampaignObjectiveValues::LINK_CLICKS,
));

$campaign->create(array(
  Campaign::STATUS_PARAM_NAME => Campaign::STATUS_PAUSED,
));
from facebookads.adobjects.campaign import Campaign

campaign = Campaign(parent_id='act_<AD_ACCOUNT_ID>')
campaign.update({
    Campaign.Field.name: 'My Campaign',
    Campaign.Field.objective: Campaign.Objective.link_clicks,
})

campaign.remote_create(params={
    'status': Campaign.Status.paused,
})
Campaign campaign = new AdAccount(act_<AD_ACCOUNT_ID>, context).createCampaign()
  .setName("My campaign")
  .setObjective(Campaign.EnumObjective.VALUE_LINK_CLICKS)
  .setStatus(Campaign.EnumStatus.VALUE_PAUSED)
  .execute();
String campaign_id = campaign.getId();
curl \
  -F 'name=My campaign' \
  -F 'objective=LINK_CLICKS' \
  -F 'status=PAUSED' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.10/act_<AD_ACCOUNT_ID>/campaigns

Create Ad Set

Create an Ad Set with desired targeting, budget, and so on. If you create a Reach and Frequency ad set, you must set rf_prediction_id and the destination_ids of the Reach Frequency Prediction must contain the Instagram account id.

Optimization and Bid

The optimization_goal and billing_event follow the same guidelines as in Facebook. The optimization_goal depends on objective, and the billing_event value depends on which optimization_goal you choose; all follow the same validation rules as on Facebook.

For APP_INSTALLS and CONVERSIONS, a promoted_object is required.

Targeting and Placement

You can use all Facebook targeting options for Instagram campaigns, including:

To deliver ads to Instagram, include instagram in publisher_platforms. You can use Instagram Stream or Stories individually, or you can enable multiple platforms including Instagram Stream. If you choose multiple platforms, Facebook optimizes delivery based on your target audience on each platform with Placement Optimization. You cannot currently use Instagram Stories with the other placements currently.

To show ads in Stream or Stories, provide instagram_placements. If this field is not specified, Facebook uses ["stream"]. To deliver ads to Instagram Stories, use ["story"] only. In this case, instagram should be the only value for publisher_platforms.

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 => 'Instagram Adset',
  AdSetFields::OPTIMIZATION_GOAL => AdSetOptimizationGoalValues::LINK_CLICKS,
  AdSetFields::BILLING_EVENT => AdSetBillingEventValues::IMPRESSIONS,
  AdSetFields::BID_AMOUNT => 2,
  AdSetFields::DAILY_BUDGET => 1000,
  AdSetFields::CAMPAIGN_ID => <CAMPAIGN_ID>,
  AdSetFields::TARGETING => (new Targeting())->setData(array(
    TargetingFields::GEO_LOCATIONS => array(
      'countries' => array('US'),
    ),
    TargetingFields::USER_OS => array('iOS'),
    TargetingFields::PUBLISHER_PLATFORMS => array('instagram'),
  )),
));

$adset->create();
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: 'Instagram Adset',
    AdSet.Field.optimization_goal: AdSet.OptimizationGoal.link_clicks,
    AdSet.Field.billing_event: AdSet.BillingEvent.impressions,
    AdSet.Field.bid_amount: 2,
    AdSet.Field.daily_budget: 1000,
    AdSet.Field.campaign_id: <CAMPAIGN_ID>,
    AdSet.Field.targeting: {
        Targeting.Field.geo_locations: {
            Targeting.Field.countries: ['US'],
        },
        Targeting.Field.user_os: ['iOS'],
        Targeting.Field.publisher_platforms: ['instagram'],
    },
    AdSet.Field.status: AdSet.Status.active,
}
adset = ad_account.create_ad_set(params=params)

adset.remote_delete()
AdSet adSet = new AdAccount(act_<AD_ACCOUNT_ID>, context).createAdSet()
  .setName("Instagram Adset")
  .setOptimizationGoal(AdSet.EnumOptimizationGoal.VALUE_LINK_CLICKS)
  .setBillingEvent(AdSet.EnumBillingEvent.VALUE_IMPRESSIONS)
  .setBidAmount(2L)
  .setDailyBudget(1000L)
  .setCampaignId(<CAMPAIGN_ID>)
  .setTargeting(
    new Targeting()
      .setFieldGeoLocations(
        new TargetingGeoLocation()
          .setFieldCountries(Arrays.asList("US"))
      )
      .setFieldPublisherPlatforms(Arrays.asList("instagram"))
      .setFieldUserOs(Arrays.asList("iOS"))
  )
  .execute();
String ad_set_id = adSet.getId();
curl \
  -F 'name=Instagram Adset' \
  -F 'optimization_goal=LINK_CLICKS' \
  -F 'billing_event=IMPRESSIONS' \
  -F 'bid_amount=2' \
  -F 'daily_budget=1000' \
  -F 'campaign_id=<CAMPAIGN_ID>' \
  -F 'targeting={ 
    "geo_locations": {"countries":["US"]}, 
    "publisher_platforms": ["instagram"], 
    "user_os": ["iOS"] 
  }' \
  -F 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.10/act_<AD_ACCOUNT_ID>/adsets

You can get an estimated number of individuals for your targeting and what bids to make reach that target group with the /reachestimate API. See Reach Estimate. Call this API in two ways. By specifying an ad account and a targeting spec.

use FacebookAds\Object\AdAccount;
use FacebookAds\Object\Fields\TargetingFields;
use FacebookAds\Object\Targeting;
use FacebookAds\Object\Values\AdSetOptimizationGoalValues;

$account = new AdAccount('act_<AD_ACCOUNT_ID>');
$targeting = new Targeting();
$targeting->setData(array(
  TargetingFields::GEO_LOCATIONS => array(
    TargetingFields::COUNTRIES => array('US', 'GB'),
  ),
  TargetingFields::USER_OS => array('iOS'),
  TargetingFields::PUBLISHER_PLATFORMS => array('instagram'),
));

$estimate = $account->getReachEstimate(array(), array(
  'targeting_spec' => $targeting,
  'optimize_for' => AdSetOptimizationGoalValues::IMPRESSIONS,
));
from facebookads.adobjects.adaccount import AdAccount
from facebookads.adobjects.adset import AdSet
from facebookads.adobjects.targeting import Targeting

account = AdAccount('act_<AD_ACCOUNT_ID>')

targeting = Targeting()
targeting.set_data({
    Targeting.Field.geo_locations: {
        Targeting.Field.countries: ['US', 'GB'],
    },
    Targeting.Field.user_os: ['iOS'],
    Targeting.Field.publisher_platforms: ['instagram'],
})

estimate = account.get_reach_estimate(params={
    'targeting_spec': targeting,
    'optimize_for': AdSet.OptimizationGoal.impressions,
})
APINodeList<ReachEstimate> reachEstimates = new AdAccount(act_<AD_ACCOUNT_ID>, context).getReachEstimate()
  .setTargetingSpec(
    new Targeting()
      .setFieldGeoLocations(
        new TargetingGeoLocation()
          .setFieldCountries(Arrays.asList("US", "GB"))
      )
      .setFieldPublisherPlatforms(Arrays.asList("instagram"))
      .setFieldUserOs(Arrays.asList("iOS"))
  )
  .setOptimizeFor(ReachEstimate.EnumOptimizeFor.VALUE_IMPRESSIONS)
  .execute();
curl -G \
  --data-urlencode 'targeting_spec={ 
    "geo_locations": {"countries":["US","GB"]}, 
    "publisher_platforms": ["instagram"], 
    "user_os": ["iOS"] 
  }' \
  -d 'optimize_for=IMPRESSIONS' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.10/act_<AD_ACCOUNT_ID>/reachestimate

Or by specifying an existing ad or ad set.

use FacebookAds\Object\Ad;


$adgroup = new Ad(<AD_ID>);
$reach_estimate = $adgroup->getReachEstimate();
from facebookads.adobjects.ad import Ad

ad = Ad(<AD_ID>)
reach_estimate = ad.get_reach_estimate()
print(reach_estimate)
APINodeList<ReachEstimate> reachEstimates = new Ad(<AD_ID>, context).getReachEstimate()
  .execute();
curl -G \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.8/<AD_ID>/reachestimate

Create Ad Creative

See Instagram Ad Creatives are documented separately.

Creating ad objects for Instagram is the same is it is for Facebook ads, see Ad, Reference.

Ad Preview

For ad_format use INSTAGRAM_STANDARD or INSTAGRAM_STORY with the ad or ad creative id to preview an existing ad. See Ad Preview.

use FacebookAds\Object\Ad;
use FacebookAds\Object\Values\AdPreviewAdFormatValues;

$adgroup = new Ad(<AD_ID>);
$preview = $adgroup->getPreviews(array(), array(
  'ad_format' => AdPreviewAdFormatValues::INSTAGRAM_STANDARD,
))->current();
from facebookads.adobjects.ad import Ad
from facebookads.adobjects.adpreview import AdPreview

ad = Ad(<AD_ID>)
ad.get_previews(params={
    'ad_format': AdPreview.AdFormat.instagram_standard,
})
curl -G \
  -d 'ad_format=INSTAGRAM_STANDARD' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.10/<AD_ID>/previews

To preview an ad before providing ad creative, pass the creative's object_story_spec in the preview's creative parameter. You must provide instagram_actor_id and page_id for both Instagram only placement and mixed placement ads:

use FacebookAds\Object\AdAccount;
use FacebookAds\Object\AdCreative;
use FacebookAds\Object\AdCreativeLinkData;
use FacebookAds\Object\Values\AdCreativeCallToActionTypeValues;
use FacebookAds\Object\Fields\AdCreativeFields;
use FacebookAds\Object\Fields\AdCreativeLinkDataFields;
use FacebookAds\Object\Fields\AdCreativeObjectStorySpecFields;
use FacebookAds\Object\Fields\AdPreviewFields;
use FacebookAds\Object\AdCreativeObjectStorySpec;
use FacebookAds\Object\Values\AdPreviewAdFormatValues;

$creative = new AdCreative();
$creative->setData(array(
  AdCreativeFields::OBJECT_STORY_SPEC =>
    (new AdCreativeObjectStorySpec())->setData(array(
      AdCreativeObjectStorySpecFields::PAGE_ID => <PAGE_ID>,
      AdCreativeObjectStorySpecFields::INSTAGRAM_ACTOR_ID =>
        <INSTAGRAM_ACTOR_ID>,
      AdCreativeObjectStorySpecFields::LINK_DATA =>
        (new AdCreativeLinkData())->setData(array(
          AdCreativeLinkDataFields::IMAGE_HASH => '<IMAGE_HASH>',
          AdCreativeLinkDataFields::MESSAGE => 'Ad Message',
          AdCreativeLinkDataFields::CAPTION => 'www.example.com',
          AdCreativeLinkDataFields::LINK => '<URL>',
          AdCreativeLinkDataFields::CALL_TO_ACTION => array(
            'type' => AdCreativeCallToActionTypeValues::LEARN_MORE,
            'value' => array(
              'link' => '<URL>',
            ),
          ),
      )),
    )),
));

$account = new AdAccount('act_<AD_ACCOUNT_ID>');
$preview = $account->getGeneratePreviews(array(), array(
  AdPreviewFields::CREATIVE => $creative,
  AdPreviewFields::AD_FORMAT => AdPreviewAdFormatValues::INSTAGRAM_STANDARD,
))->current();
from facebookads.adobjects.adaccount import AdAccount
from facebookads.adobjects.adcreative import AdCreative
from facebookads.adobjects.adcreativelinkdatacalltoaction \
    import AdCreativeLinkDataCallToAction
from facebookads.adobjects.adcreativelinkdata import AdCreativeLinkData
from facebookads.adobjects.adcreativeobjectstoryspec \
    import AdCreativeObjectStorySpec
from facebookads.adobjects.adpreview import AdPreview

account = AdAccount('act_<AD_ACCOUNT_ID>')

link_data = AdCreativeLinkData()
link_data.set_data({
    AdCreativeLinkData.Field.image_hash: '<IMAGE_HASH>',
    AdCreativeLinkData.Field.message: 'Ad message',
    AdCreativeLinkData.Field.caption: 'www.example.com',
    AdCreativeLinkData.Field.link: '<URL>',
    AdCreativeLinkData.Field.call_to_action: {
        'type': AdCreativeLinkDataCallToAction.Type.learn_more,
        'value': {
            'link': '<URL>',
        },
    },
})

story = AdCreativeObjectStorySpec()
story.set_data({
    AdCreativeObjectStorySpec.Field.page_id: <PAGE_ID>,
    AdCreativeObjectStorySpec.Field.instagram_actor_id: <INSTAGRAM_ACTOR_ID>,
    AdCreativeObjectStorySpec.Field.link_data: link_data,
})

creative = AdCreative()
creative.set_data({
    AdCreative.Field.object_story_spec: story,
})

preview = account.get_generate_previews(params={
    'creative': creative,
    'ad_format': AdPreview.AdFormat.instagram_standard,
})
APINodeList<AdPreview> adPreviews = new AdAccount(act_<AD_ACCOUNT_ID>, context).getGeneratePreviews()
  .setCreative(
    new AdCreative()
      .setFieldObjectStorySpec(
        new AdCreativeObjectStorySpec()
          .setFieldInstagramActorId(<INSTAGRAM_ACTOR_ID>)
          .setFieldLinkData(
            new AdCreativeLinkData()
              .setFieldCallToAction(
                new AdCreativeLinkDataCallToAction()
                  .setFieldType(AdCreativeLinkDataCallToAction.EnumType.VALUE_LEARN_MORE)
                  .setFieldValue(
                    new AdCreativeLinkDataCallToActionValue()
                      .setFieldLink(<URL>)
                  )
              )
              .setFieldCaption("www.example.com")
              .setFieldImageHash(<IMAGE_HASH>)
              .setFieldLink(<URL>)
              .setFieldMessage("Ad Message")
          )
          .setFieldPageId(<PAGE_ID>)
      )
  )
  .setAdFormat(AdPreview.EnumAdFormat.VALUE_INSTAGRAM_STANDARD)
  .execute();
curl -G \
  --data-urlencode 'creative={ 
    "object_story_spec": { 
      "instagram_actor_id": "<INSTAGRAM_ACTOR_ID>", 
      "link_data": { 
        "call_to_action": {"type":"LEARN_MORE","value":{"link":"<URL>"}}, 
        "caption": "www.example.com", 
        "image_hash": "<IMAGE_HASH>", 
        "link": "<URL>", 
        "message": "Ad Message" 
      }, 
      "page_id": "<PAGE_ID>" 
    } 
  }' \
  -d 'ad_format=INSTAGRAM_STANDARD' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.10/act_<AD_ACCOUNT_ID>/generatepreviews

After you provide ad creative, you can get the URL for the corresponding Instagram post, and can see reponses to the ad post. The post is not identical to the one your audience sees; it does not have "Sponsored" or CTA. This URL is not available with Dynamic Ad creatives, which is when you use template_data in object_story_spec. Neither is this URL available for ad creatives for ads in Instagram stories.

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

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

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

The response:

{
  "instagram_permalink_url": "<INSTAGRAM_POST_URL>",
  "id": "<AD_CREATIVE_ID>"
}

Ad Insights

For stats on your Instagram ads, use the Insights API from ad account to ads.

use FacebookAds\Object\Campaign;

$campaign = new Campaign(<CAMPAIGN_ID>);
$insights = $campaign->getInsights();
from facebookads.adobjects.campaign import Campaign

campaign = Campaign(<CAMPAIGN_ID>)
insights = campaign.get_insights()
curl -G \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.10/<CAMPAIGN_ID>/insights

If you are running a campaign on Instagram and Facebook, add breakdowns=placement to see the stats of Facebook and Instagram placements separately:

use FacebookAds\Object\AdSet;
use FacebookAds\Object\Values\AdsInsightsBreakdownsValues;

$adset = new AdSet(<AD_SET_ID>);
$insights = $adset->getInsights(array(), array(
  'breakdowns' => AdsInsightsBreakdownsValues::PLACEMENT,
));
from facebookads.adobjects.adset import AdSet
from facebookads.adobjects.adsinsights import AdsInsights

adset = AdSet(<AD_SET_ID>)
insights = adset.get_insights(params={
    'breakdowns': AdsInsights.Breakdowns.placement,
})
APINodeList<AdsInsights> adsInsightss = new AdSet(<AD_SET_ID>, context).getInsights()
  .setParam("breakdown", "placement")
  .execute();
curl -G \
  -d 'breakdowns=placement' \
  -d 'access_token=<ACCESS_TOKEN>' \
  https://graph.facebook.com/v2.8/<AD_SET_ID>/insights

The results are similar to this:

{
  "data": [
    {
      "actions": [
        {
          "action_type": "comment", 
          "value": 49
        }, 
        ...
      ], 
      "actions_per_impression": 0.13427956056216, 
      ...
      "clicks": 3917, 
 
      "video_avg_pct_watched_actions": [
        {
          "action_type": "video_view", 
          "value": 88.13
        }
      ], 
      ...
      "video_p25_watched_actions": [
        {
          "action_type": "video_view", 
          "value": 28016
        }
      ], 
      "video_p50_watched_actions": [
        {
          "action_type": "video_view", 
          "value": 17606
        }
      ], 
      ...
      "placement": "instagram_stream"
    }
  ], 
  "paging": {
    ...
  }
}

The two possible placements in Insights are instagram_stream and instagram_stories.

There are other breakdown combinations which include placement that you can use. To track performance of ads with both Facebook and Instagram placements with external tools, use the url_tags macro SITE_SOURCE_NAME to distinguish different placements.