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.

curl -X POST \ -F 'name="My campaign"' \ -F 'objective="LINK_CLICKS"' \ -F 'status="PAUSED"' \ -F 'access_token=<ACCESS_TOKEN>' \ https://graph.facebook.com/v3.0/act_<AD_ACCOUNT_ID>/campaigns
const adsSdk = require('facebook-nodejs-ads-sdk'); const AdAccount = adsSdk.AdAccount; const Campaign = adsSdk.Campaign; let access_token = '<ACCESS_TOKEN>'; let app_secret = '<APP_SECRET>'; let app_id = '<APP_ID>'; let id = '<ID>'; const api = adsSdk.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', }; let 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 = '<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 facebookads.adobjects.adaccount import AdAccount from facebookads.adobjects.campaign import Campaign from facebookads.api import FacebookAdsApi access_token = '<ACCESS_TOKEN>' app_secret = '<APP_SECRET>' app_id = '<APP_ID>' id = '<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 = \"<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 = '<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', })

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

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.11/<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.11/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.11/<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.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 = '<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 = '<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 = '<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 = \"<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 = '<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.