Creating Ads

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.

Also see Advertising on Instagram and Instagram Ads FAQ

Create Ad Campaign

You can run campaigns for most objectives in Instagram stream. Check which objectives you can use in Instagram in Facebook Ads Guide. You can also find the latest list of objectives at Reference, Instagram Ads, Campaign. We update our docs when we add more objectives. Note that REACH, VIDEO_VIEWS, LINK_CLICKS, CONVERSIONS, and APP_INSTALLS are the only objectives valid for Instagram Stories.

curl -X POST \ -F 'name="My campaign"' \ -F 'objective="LINK_CLICKS"' \ -F 'status="PAUSED"' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.2/act_<AD_ACCOUNT_ID>/campaigns
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const AdAccount = bizSdk.AdAccount; const Campaign = bizSdk.Campaign; 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' : 'My campaign', 'objective' : 'LINK_CLICKS', 'status' : 'PAUSED', }; const campaigns = (new AdAccount(id)).createCampaign( fields, params ); logApiCallResult('campaigns api call complete.', campaigns);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\AdAccount; use FacebookAds\Object\Campaign; 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' => 'My campaign', 'objective' => 'LINK_CLICKS', 'status' => 'PAUSED', ); echo json_encode((new AdAccount($id))->createCampaign( $fields, $params )->exportAllData(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.adaccount import AdAccount from facebook_business.adobjects.campaign import Campaign 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': 'My campaign', 'objective': 'LINK_CLICKS', 'status': 'PAUSED', } print AdAccount(id).create_campaign( 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).createCampaign() .setName(\"My campaign\") .setObjective(Campaign.EnumObjective.VALUE_LINK_CLICKS) .setStatus(Campaign.EnumStatus.VALUE_PAUSED) .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) campaigns = ad_account.campaigns.create({ name: 'My campaign', objective: 'LINK_CLICKS', status: 'PAUSED', })

The minimum spend budget on Instagram is same as Facebook self-serve ads, with different limits per currency and limits based on bid_amount.

Instagram stream is a default placement for ads. Note that defaults may change without notice.

Reach and Frequency

For predictable 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.

Reach estimates in Ads Manager and the API can give partners guidance on what they can reasonably expect. Instagram community comes first and we try to achieve reach objectives conservatively and expect to evolve over time. Also please note that all policies that apply to using reach and frequency estimate for Facebook also apply to Instagram.

Dynamic Ads

Dynamic Ads on Instagram is available. You can use Website Custom Audience targeting to reach people who visited or engaged on your website.

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.

To show ads exclusively in Stream or Stories, provide this in the instagram_positions field. If this field is not specified, we deliver ads to both Instagram Feed and Stories. To deliver ads only to Instagram Stories, use ["story"] only. In this case, you should have instagram as 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.adset import AdSet
from facebookads.adobjects.targeting import Targeting

adset = AdSet(parent_id='act_<AD_ACCOUNT_ID>')
adset.update({
    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.remote_create()
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.11/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.11/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

Ad Creative

See Instagram Ad Creatives

Ad Review

The ad review policies are the same for Facebook and Instagram. However, as we make Instagram available to more businesses, we want the same high-quality ad experience on Instagram that we have on Facebook.

This requires understanding more about how the community interacts with different kinds of advertiser content on Instagram. Since it takes time to build the same kind of models that drive Facebook ads, we currently rely on human review to filter out a small percentage of ads and provide suggestions for improvement.

Our ultimate goal is to make running a campaign across Facebook and Instagram a seamless experience and to make ads a relevant, valuable part of the Instagram product.

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.11/<CAMPAIGN_ID>/insights

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

curl -X GET \ -d 'fields="impressions"' \ -d 'breakdown="publisher_platform"' \ -d 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.2/<AD_SET_ID>/insights
'use strict'; const bizSdk = require('facebook-nodejs-business-sdk'); const AdSet = bizSdk.AdSet; const AdsInsights = bizSdk.AdsInsights; const access_token = '<ACCESS_TOKEN>'; const app_secret = '<APP_SECRET>'; const app_id = '<APP_ID>'; const id = '<AD_SET_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 = [ 'impressions', ]; params = { 'breakdown' : 'publisher_platform', }; const insightss = (new AdSet(id)).getInsights( fields, params ); logApiCallResult('insightss api call complete.', insightss);
require __DIR__ . '/vendor/autoload.php'; use FacebookAds\Object\AdSet; use FacebookAds\Object\AdsInsights; use FacebookAds\Api; use FacebookAds\Logger\CurlLogger; $access_token = '<ACCESS_TOKEN>'; $app_secret = '<APP_SECRET>'; $app_id = '<APP_ID>'; $id = '<AD_SET_ID>'; $api = Api::init($app_id, $app_secret, $access_token); $api->setLogger(new CurlLogger()); $fields = array( 'impressions', ); $params = array( 'breakdown' => 'publisher_platform', ); echo json_encode((new AdSet($id))->getInsights( $fields, $params )->getResponse()->getContent(), JSON_PRETTY_PRINT);
from facebook_business.adobjects.adset import AdSet from facebook_business.adobjects.adsinsights import AdsInsights from facebook_business.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_SET_ID>' FacebookAdsApi.init(access_token=access_token) fields = [ 'impressions', ] params = { 'breakdown': 'publisher_platform', } print AdSet(id).get_insights( 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_SET_ID>\"; APIContext context = new APIContext(access_token).enableDebug(true); new AdSet(id, context).getInsights() .setParam(\"breakdown\", \"publisher_platform\") .requestField(\"impressions\") .execute(); } }
require 'facebook_ads' access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<AD_SET_ID>' FacebookAds.configure do |config| config.access_token = access_token config.app_secret = app_secret end ad_set = FacebookAds::AdSet.get(id) insightss = ad_set.insights({ fields: { 'impressions' }, breakdown: 'publisher_platform', })

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 publisher_platform 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.

Tracking Tags

View tags are not publicly available. If we permit view tags by an approved vendor on Facebook mobile campaigns, we also allow them for Instagram ads. Click tracking tags only fire when we deliver the ad on Facebook desktop and so you cannot use them with Instagram ads. You can use Ad creative's url_tag field can be with Instagram ads.

You can use third-party tracking tags for Instagram ads, however note we do not optimize ads delivery for third-party tracking tools. To make sure that the third party tracking tool can track Instagram ads properly, use Ad creative's url_tag field with utm_source=instagram.