Platforms

Instagram

Publish feed posts, carousels, Stories and Reels to Instagram, read account and post insights, and answer DMs and comments.

Connect an Instagram professional account and you can publish every format Instagram's API allows: feed images, carousels, Stories and Reels. The same account gives you post and account insights, the DM inbox, comment moderation and comment-to-DM automations. The platform id is instagram.

#What you can do

Capability Supported Where
Publish Feed images, carousels (up to 10 items), Stories, Reels, trial Reels Create a post
Schedule Yes, every format including Stories Scheduling
Analytics Post metrics, account insights, follower history, demographics, online followers, Story insights Platform insights
DMs Read and reply, attachments, quick replies, buttons, product cards, reactions Messages
Comments List, reply, hide, delete, private reply Comments
Automations Comment, live comment, Story reply and Story mention triggers Comment to DM
Ads Through a Meta Ads connection Meta Ads

#Connect

Instagram needs a Business or Creator account. Personal accounts cannot publish through any third-party API, so ask the user to switch their account type in the Instagram app first (it is free and takes a minute).

Start the connection with GET /v1/connect/instagram and a profileId. There are two login methods, chosen with the loginMethod query parameter:

loginMethod What the user signs in to Use it when
instagram_login (default) Their Instagram professional account directly. No Facebook Page needed. You only need publishing, insights and the inbox.
facebook_login A Facebook Page that has the Instagram account linked. Calls run through that Page. You also need ads, catalog audio on Reels, the paid partnership label or liking comments.

With facebook_login the user picks the Page in a second step after OAuth. Pozy can host that picker for you, or you can build it yourself with listInstagramPages and selectInstagramAccount. See Headless connect.

curl "https://api.pozy.ai/v1/connect/instagram?profileId=6720c0f24f3b2e0018d4a6e0&loginMethod=facebook_login&redirect_url=https://app.example.com/connected" \
  -H "Authorization: Bearer $POZY_API_KEY"
Ask for less

Pass scopes to request only the permission areas you need (for example publishing and analytics, without ads). Fewer permissions on the consent screen means fewer users who stop halfway. See Scopes.

#Post types

Instagram decides the format from the media you send, with one field to override it. Every post needs media: there are no text-only Instagram posts.

You send Instagram publishes Extra fields
One image Feed post altText, userTags, collaborators, locationId, firstComment
2 to 10 images or videos Carousel Same as feed. Every item takes the aspect ratio of the first one.
One video Reel shareToFeed, instagramThumbnail or thumbOffset, audioName, audioConfiguration, trialParams
Any single image or video with contentType: "story" Story userTags (coordinates optional)

#Feed post

curl -X POST https://api.pozy.ai/v1/posts \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "First light over the harbour. #morning",
    "mediaItems": [
      { "type": "image", "url": "https://cdn.example.com/harbour.jpg", "altText": "Fishing boats at dawn" }
    ],
    "platforms": [
      { "platform": "instagram", "accountId": "6720c1a94f3b2e0018d4a7f1" }
    ],
    "publishNow": true
  }'

altText applies to feed images and carousel images. Instagram cuts it at 1,000 characters and ignores it on Reels, Stories and video slides.

Put up to 10 images and videos in mediaItems. Mixed media is fine. Crop everything to the same ratio before you upload, because Instagram uses the first item's ratio for all of them.

Carousel media
"mediaItems": [
  { "type": "image", "url": "https://cdn.example.com/menu-1.jpg" },
  { "type": "image", "url": "https://cdn.example.com/menu-2.jpg" },
  { "type": "video", "url": "https://cdn.example.com/kitchen.mp4" }
]

#Reel

A single video is always a Reel. Set shareToFeed: false to keep it in the Reels tab only, and give it a cover with instagramThumbnail (a JPEG or PNG URL, 1080 x 1920 px works best) or a frame offset in milliseconds with thumbOffset.

Reel settings
{
  "platform": "instagram",
  "accountId": "6720c1a94f3b2e0018d4a7f1",
  "platformSpecificData": {
    "shareToFeed": true,
    "instagramThumbnail": "https://cdn.example.com/reel-cover.jpg",
    "audioName": "Studio session"
  }
}

Trial Reels are shown to non-followers first. Add trialParams.graduationStrategy: MANUAL (you promote it yourself in the app) or SS_PERFORMANCE (Instagram shares it with followers if it performs well).

Catalog audio. audioConfiguration attaches a track from Instagram's audio library. Find an audioId with Search Instagram audio (leave out q to get what is trending), then send it with optional audioVolume and videoVolume from 0 to 100. This needs facebook_login. If the track is pulled before a scheduled Reel goes out, the post fails with a message naming the cause rather than publishing with different sound. Get Instagram audio metadata re-checks a stored id before publish time.

muteAudio: true removes the video's own sound track before upload, so the result is permanently silent. Videos over 200 MB cannot be muted.

#Story

Add contentType: "story". Stories show no caption, last 24 hours and cannot carry link stickers through the API. A Story publishes one media item, so give a Story entry its own customMedia when the post has several.

To publish a feed post and a Story of the same campaign at once, list the account twice:

Feed post and Story in one request
"platforms": [
  { "platform": "instagram", "accountId": "6720c1a94f3b2e0018d4a7f1" },
  {
    "platform": "instagram",
    "accountId": "6720c1a94f3b2e0018d4a7f1",
    "customMedia": [{ "type": "image", "url": "https://cdn.example.com/story-1080x1920.jpg" }],
    "platformSpecificData": { "contentType": "story" }
  }
]

Each entry publishes and reports its status separately.

#Platform fields

All of these go in platformSpecificData on the Instagram entry (schema: InstagramPlatformData).

Field Type Applies to Notes
contentType "story" Story Leave out for feed posts and Reels.
shareToFeed boolean, default true Reels false keeps the Reel off the main grid.
collaborators array of usernames Feed, carousel, Reel Up to 3 public Business or Creator accounts. They get an invite to co-author.
userTags array of { username, x, y, mediaIndex } All x and y run from 0 to 1 and are required on images. mediaIndex picks the carousel slide.
firstComment string Feed, carousel, Reel Posted right after publishing. Captions cannot hold clickable links, so this is where links usually go.
locationId string Feed, carousel, Reel The numeric id of a Facebook Page that has an address. Not a place name. Stories reject it.
trialParams { graduationStrategy } Reels MANUAL or SS_PERFORMANCE.
instagramThumbnail / reelCover URL Reels Custom cover. Wins over thumbOffset.
thumbOffset integer (ms) Reels Frame to use as the cover.
audioName string Reels Renames the original audio. Set at creation only.
audioConfiguration { audioId, audioVolume, videoVolume } Reels Catalog track. Needs facebook_login.
muteAudio boolean Reels, Stories, video slides Strips the sound before upload.
isAiGenerated boolean All Adds Instagram's AI label for generated images or video.
isPaidPartnership boolean Feed, carousel, Reel Shows the "Paid partnership" label. Needs facebook_login.
brandedContentSponsors array, up to 2 Feed, carousel, Reel Usernames or numeric ids of the brands. Turns the label on by itself. The brand is named once they approve the request in Instagram.
commentsEnabled boolean, default true Feed, carousel, Reel false turns comments off right after publishing.

If a collaborator cannot be invited, the post still publishes and the name comes back in droppedCollaborators with a reason.

#Media requirements

Pozy compresses images over 8 MB and keeps your original. Media URLs must return the file itself: sharing links from Google Drive, Dropbox, OneDrive or iCloud return a web page and fail. Upload files with Media uploads when you do not have a direct URL.

Images

Feed and carousel Story
Formats JPEG, PNG JPEG, PNG
Max size 8 MB each 8 MB
Aspect ratio 4:5 (1080 x 1350) to 1.91:1 (1080 x 566); 1:1 and 9:16 also accepted 9:16 (1080 x 1920)
Count 1, or up to 10 in a carousel 1

Videos

Reel Story
Formats MP4, MOV (H.264) MP4, MOV (H.264)
Max size 300 MB 100 MB
Duration 3 seconds to 15 minutes 3 to 60 seconds
Aspect ratio 9:16, 1080 x 1920 9:16, 1080 x 1920
Frame rate 30 fps recommended 30 fps recommended

#Limits

Limit Value
Caption 2,200 characters. About 125 show before "more".
Posts per account 100 per rolling 24 hours, all formats combined
Collaborators 3 per post
Branded content sponsors 2 per post
Alt text 1,000 characters

Read how much of the 24-hour allowance is left with Get Instagram publishing limit. Compare quotaUsage with the returned quotaTotal instead of hardcoding 100, since Instagram can change the cap.

Instagram's API does not support Story stickers (polls, links, countdowns), product tags, filters, Guides or going live. Published Instagram posts cannot be edited or deleted through the API; change them in the Instagram app.

#Analytics

GET /v1/analytics?platform=instagram returns per-post impressions, reach, likes, comments, shares, saves and views, plus Reel watch-time fields (igReelsAvgWatchTime, igReelsVideoViewTotalTime) where Instagram reports them.

Account-level endpoints go further:

Endpoint What you get
Instagram insights Reach, views, accounts engaged, interactions, follows and unfollows, profile views, link taps. Only reach comes as a time series.
Follower history Daily follower count with followers_gained and followers_lost, built from daily snapshots.
Demographics Audience by age, gender, city or country. Needs at least 100 followers.
Online followers When followers are online, by hour. Useful with Best time to post.
Active Stories and Story insights Live Stories with views, reach, replies, taps forward and back, exits.

Story numbers are only readable while the Story is live. After 24 hours you get final numbers only if Instagram sent them before expiry, which happens far more reliably on accounts connected with facebook_login. If you need final Story metrics, read them before the Story expires. Counts under 5 can come back as 0 because of Instagram's privacy floor.

Image and cover URLs in analytics responses are served from Pozy storage and do not expire, so you can cache them.

#Inbox

Direct messages. List conversations, read messages and reply with Send message. You can send text, images (PNG, JPEG up to 8 MB), video and audio (up to 25 MB), PDFs, up to 13 quick replies, up to 3 buttons (the text must then be 640 characters or less) and product cards of up to 10 elements. Reactions work both ways.

The 24-hour window

You can message someone freely for 24 hours after their last message. After that, a human-typed reply can still go out for up to 7 days with messagingType: "MESSAGE_TAG" and messageTag: "HUMAN_AGENT". Instagram does not allow that tag on automated messages, so workflows and broadcasts refuse it.

Instagram does not expose the Message Requests folder separately, so GET /v1/inbox/conversations?folder=requests returns 400 with code PLATFORM_LIMITATION. Requests still reach you as message.received once the person writes, and Accept a message request works on them.

When another app owns a thread through Instagram's handover protocol, inbound messages arrive with metadata.standby: true, automations stay quiet and sends return 409 not_thread_owner. Move the thread with thread control.

Conversations and senders can carry an instagramProfile object (isFollower, isFollowing, followerCount, isVerified). Instagram only reveals the follow relationship for people who have messaged the account, so treat a missing value as unknown, not as "not a follower".

When you connect an account, Pozy imports its existing DM history (up to 500 conversations, the newest 500 messages in each). The import runs in the background, arrives already read and fires no webhooks.

Comments. List comments, post a top-level comment, reply, hide, unhide and delete. Send private reply opens a DM from a comment, once per comment, within 7 days. Liking a comment is in limited release on Instagram's side and only works on facebook_login accounts; other accounts get 403 PLATFORM_BETA_RESTRICTED or 400 instagram_likes_require_facebook_login.

Conversation starters. Set up to 4 ice breakers (80 characters each) and a persistent menu (3 top-level items, 5 nested). A menu postback with payload pozy:workflow:<workflowId> starts that workflow. See Conversation starters.

#Errors you may see

Message Cause Fix
Cannot process video from this URL A cloud-drive sharing link instead of a direct file URL Use a direct URL or upload through Media.
Maximum of 100 posts per day reached The rolling 24-hour cap Spread posts out. Check the publishing limit.
Instagram blocked your request Instagram's automation detection Post less often, vary the content, wait before retrying.
Duplicate content detected Same caption and media posted recently Change the caption or media.
instagram_audio_requires_facebook_login audioConfiguration on an Instagram Login account Reconnect with loginMethod=facebook_login.
instagram_paid_partnership_requires_facebook_login Paid partnership label on an Instagram Login account Reconnect with loginMethod=facebook_login.
Instagram rejected locationId The Page id has no address or does not exist Use the id of a Page with location data.
Access token expired The user's session ended or they removed access Reconnect the account. Listen for account.disconnected.

When a post sent with publishNow: true is rejected, the response is 207 with post.status: "failed" and the reason in platforms[].errorMessage. A 207 is still a 2xx, so check post.status rather than only the HTTP status.

#Endpoints

Endpoint What it does
GET /v1/accounts/{accountId}/instagram/stories List active Instagram stories
GET /v1/accounts/{accountId}/instagram/publishing-limit Get Instagram publishing limit
GET /v1/accounts/{accountId}/instagram/audio Search Instagram audio
GET /v1/accounts/{accountId}/instagram/audio/{audioId} Get Instagram audio metadata
GET /v1/accounts/{accountId}/instagram/business-discovery Look up a public Instagram Business account
GET /v1/accounts/{accountId}/instagram/media/{mediaId}/collaborators List the collaborators of an Instagram post
GET /v1/accounts/{accountId}/instagram/live-media List the live broadcasts of an Instagram account
GET /v1/accounts/{accountId}/instagram/tags List the posts an Instagram account is tagged in
GET /v1/accounts/{accountId}/instagram/mentions/media/{mediaId} Get a post that mentions an Instagram account in its caption
GET /v1/accounts/{accountId}/instagram/mentions/comments/{commentId} Get a comment that mentions an Instagram account
GET /v1/accounts/{accountId}/instagram/stories/{storyId}/insights Get Instagram story insights
GET /v1/analytics/instagram/account-insights Get Instagram insights
GET /v1/analytics/instagram/follower-history Get Instagram follower history
GET /v1/analytics/instagram/demographics Get Instagram demographics
GET /v1/analytics/instagram/online-followers Get Instagram online followers
GET /v1/connect/instagram/select-account List Pages with a linked Instagram account
POST /v1/connect/instagram/select-account Select the Page whose Instagram account to connect
GET /v1/accounts/{accountId}/instagram-ice-breakers Get IG ice breakers
PUT /v1/accounts/{accountId}/instagram-ice-breakers Set IG ice breakers
DELETE /v1/accounts/{accountId}/instagram-ice-breakers Delete IG ice breakers

#Webhook events

Event When it fires
post.published Post published.
post.failed Post failed.
post.platform.published Post platform published.
post.platform.failed Post platform failed.
post.platform.deleted Post platform deleted.
account.disconnected Account disconnected.
message.received Message received.
conversation.control_changed Conversation control changed.
conversation.started Conversation started.
message.sent Message sent.
message.edited Message edited.
message.deleted Message deleted.
message.delivered Message delivered.
message.read Message read.
message.played Message played.
message.failed Message delivery failed.
reaction.received Reaction received.
comment.received Comment received.