Instagram API · publish · DMs · analyze

The Instagram API, explained - and made one call

Everything the Instagram Graph API asks of you: access prerequisites, the create-container → poll → publish flow, the 50-post daily ceiling, the error codes and the 60-day tokens. Then the shortcut - one POST that publishes Reels, carousels, Stories and photos for you. And now the same API reads and replies to Instagram DMs.

ReelsCarouselsStoriesSchedulingAnalyticsNew: Direct messages

✓ 5 minutes to first post ✓ 99.92% uptime ✓ Reels, carousels & Stories ✓ DMs

bash
curl https://api.outstand.so/v1/posts/ \
  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "New drop is live 🔥 #launch",
    "accounts": ["YOUR_INSTAGRAM_ACCOUNT_ID"],
    "media": [{ "url": "https://media.outstand.so/reel.mp4", "filename": "reel.mp4" }]
  }'

A video publishes as a Reel automatically. One image → a feed photo. Two-to-ten → a carousel.

Instagram's official API is powerful - and painful

Instagram has no simple "post" endpoint. Publishing through the Instagram Graph API means a Business or Creator account, Meta App Review, a two-step create-container → poll → publish flow, publicly-hosted media URLs and tokens that quietly expire. Messaging adds its own permission, webhooks and reply windows on top. Outstand turns all of it into a single API.

Business/Creator account, Facebook Page link and Meta App Review
Connect once via OAuth - we manage the app and permissions, or bring your own
Create a media container, poll its status, then publish - per item
One POST /v1/posts/ - we create, poll and publish the container for you
Media must be a public HTTPS URL you host yourself
Pass a URL; we fetch it and hand it to Instagram correctly per media type
60-day long-lived tokens that expire silently
Automatic token refresh - you never see a 401
Carousels need N child containers assembled in order
Send 2–10 media items in one array; we assemble the carousel
Undocumented rate limits and 429s
We queue, throttle and retry - you never see a 429
DMs need their own permission, a webhook subscription and 24-hour window bookkeeping
Request the messaging scope - we subscribe the webhooks, store every thread and check the window before you send
New

Instagram DMs, now in the same API

Publishing is half of Instagram. The other half happens in the inbox. The Outstand Conversations API reads inbound direct messages on connected accounts, replies with text or media, schedules replies and reports delivery through webhooks - with the same API key and the same connected accounts you already publish to.

Read every conversation

List threads by most recent activity with unread counts, then page through the messages in each one.

Reply with text or media

Send text, one or more media attachments, or both. Delivery is async - you get a pending message back straight away.

Schedule replies

Pass scheduled_at to send later, and cancel a scheduled reply any time before it goes out.

Real-time webhooks

conversation.started, message.received, message.sent and message.failed keep your inbox in sync without polling.

Read receipts

Mark a conversation as read to clear its unread count and send a read receipt on Instagram.

List, read and reply

bash
# List the connected account's DM threads, most recent first
curl "https://api.outstand.so/v1/conversations?social_account_id=YOUR_SOCIAL_ACCOUNT_ID" \
  -H "Authorization: Bearer $OUTSTAND_API_KEY"

# Read a thread
curl "https://api.outstand.so/v1/conversations/{id}/messages" \
  -H "Authorization: Bearer $OUTSTAND_API_KEY"

# Reply with text and an image - returns 202 with a pending message
curl https://api.outstand.so/v1/conversations/{id}/messages \
  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Thanks for reaching out! Here is the size guide 👇",
    "media_urls": ["https://media.outstand.so/size-guide.jpg"]
  }'

What to know

Reply-only: a conversation starts when someone DMs the connected account. There is no endpoint to cold-message an Instagram user.
Free-form replies must land within 24 hours of the contact's last message. A human support reply can use the HUMAN_AGENT tag for up to 7 days, subject to Meta's approval.
The connection needs the instagram_business_manage_messages permission - request it alongside the publishing scopes when your user connects.
Business or Creator accounts only, the same as publishing.

Instagram Graph API reference

What the official API actually requires, in the order you hit it. Written against the endpoints our own integration calls in production (Graph API v24.0 at the time of writing) - Meta moves fast, so check their changelog before you rely on a version.

Two routes into the same API

Instagram API with Instagram Login

graph.instagram.com

Instagram Professional accounts that log in with Instagram directly. This is the route Outstand uses.

instagram_business_basic, instagram_business_content_publish, instagram_business_manage_comments, instagram_business_manage_insights, instagram_business_manage_messages

Instagram API with Facebook Login

graph.facebook.com

Apps that already sit on the Meta Business stack and need Page-level access alongside Instagram.

instagram_basic, instagram_content_publish, pages_show_list, pages_read_engagement, business_management

Prerequisites before your first post

An Instagram Professional account - Business or Creator. Personal accounts cannot publish through any Instagram API.
A linked Facebook Page. Required for the Facebook Login route; Meta still expects a Page relationship for most business features.
A Meta app in the Meta Developer dashboard, with the Instagram product added and a valid OAuth redirect URI.
App Review for the publishing permissions. Development-mode apps can only act on accounts with a role on the app.
Business Verification for the Meta business that owns the app before advanced access is granted.
Publicly reachable HTTPS media. Meta downloads your image or video from a URL you host - there is no multipart post endpoint.

The content publishing flow

There is no single "post to Instagram" endpoint. Publishing is always two calls with a wait in between, and carousels multiply that by the number of items.

1. Create a media container

POST /{ig-user-id}/media with image_url or video_url, the caption, and media_type=REELS for video. Returns a creation ID.

2. Poll the container

GET /{container-id}?fields=status_code until it reports FINISHED. ERROR and EXPIRED are terminal - large videos can transcode for ten minutes or more.

3. Publish it

POST /{ig-user-id}/media_publish with creation_id set to the container. Only then does the post exist on Instagram.

Carousels

Create one child container per item with is_carousel_item=true, then a parent container with media_type=CAROUSEL and the children array, then publish the parent. Meta documents a maximum of 10 items.

Reels

media_type=REELS. You can set cover_url or thumb_offset (not both - Meta ignores thumb_offset when cover_url is present) and invite up to 3 collaborators.

Stories

media_type=STORIES with a single image or video, no caption and no carousel. In our own integration Meta rejects Stories for Creator accounts - Business only.

bash
# 1. Create the container
curl -X POST "https://graph.instagram.com/v24.0/{ig-user-id}/media" \
  -d "media_type=REELS" \
  -d "video_url=https://yourcdn.com/launch.mp4" \
  -d "caption=Our Q2 launch is live" \
  -d "access_token=$IG_LONG_LIVED_TOKEN"
# → { "id": "17..." }

# 2. Poll until the container finishes transcoding
curl "https://graph.instagram.com/v24.0/17...?fields=status_code&access_token=$IG_LONG_LIVED_TOKEN"
# → IN_PROGRESS … FINISHED | ERROR | EXPIRED

# 3. Publish it
curl -X POST "https://graph.instagram.com/v24.0/{ig-user-id}/media_publish" \
  -d "creation_id=17..." \
  -d "access_token=$IG_LONG_LIVED_TOKEN"

# Check what is left of today's 50-post allowance
curl "https://graph.instagram.com/v24.0/{ig-user-id}/content_publishing_limit?access_token=$IG_LONG_LIVED_TOKEN"

Large videos can stay IN_PROGRESS for ten minutes or more, so poll with backoff rather than a fixed sleep. Meta also offers a resumable upload endpoint for reels when you would rather push bytes than host a public URL.

Instagram API rate limits

Content Publishing Limit
50 published posts per rolling 24 hours, per Instagram account
Carousels count as one post. Check remaining quota with GET /{ig-user-id}/content_publishing_limit before you queue.
Platform rate limiting
Per app, not per account (Business Use Case rate limits)
Meta meters calls against your app across every account it serves, so one noisy tenant can throttle all of them.
Container lifetime
Meta documents container expiry at 24 hours
A container you never publish expires and comes back as EXPIRED on the status poll - you have to rebuild it.
Long-lived token
60 days, refreshable
Short-lived tokens exchange for a 60-day token; refreshing requires a token at least 24 hours old and not yet expired.

None of this carries a licence fee, which is why the real budget line is engineering time - we costed that out in Instagram API pricing, App Review and the publishing ceiling.

Common Instagram API error codes

190
Invalid or expired access token
The long-lived token lapsed or the user revoked access. Re-run OAuth, and refresh tokens before day 60 rather than after.
4 / 613
Application request limit reached / rate limit hit
Back off exponentially. These are app-level, so throttle across all your tenants, not just the one that tripped it.
100
Invalid parameter
The catch-all. Usually an unreachable media URL, a bad field name, or a media object that no longer exists (expired Stories return this).
200 / 10
Permission denied
The permission was never approved in App Review, or the user did not grant it. Re-check the scopes on the token.
9007
Media not ready to publish
You called media_publish before the container reached FINISHED. Poll status_code first instead of sleeping a fixed interval.
2207026
Unsupported video format
Re-encode to what Meta accepts for the media type - Reels and feed video have different codec, aspect-ratio and duration rules.

Meta returns these inside an error object with an optional error_subcode, and the message text is often more useful than the code. Log both - the same code 100 covers a typo in a field name and a Story that has already expired.

Access tokens and expiry

OAuth hands you a short-lived token. You exchange it for a long-lived token that lasts 60 days, then refresh that token to get another 60 - a refresh only works on a token that is at least 24 hours old and has not already expired. Miss the window and every call comes back as error 190 and your user has to reauthorize. This is the single most common way an Instagram integration breaks in production, because nothing fails until two months after you ship. Outstand refreshes tokens for every connected account so you never see a 190.

Instagram messaging (DMs)

Direct messages run on a separate permission, instagram_business_manage_messages, which has its own line in App Review. Messages do not come from polling: the account has to be subscribed to Meta's messaging webhooks, and your server has to receive, verify and store every event before there is an inbox to show. Replies are only allowed within 24 hours of the contact's last message - or up to 7 days with the HUMAN_AGENT tag, which Meta reserves for human support replies - and nothing lets you start a conversation cold. With Outstand, requesting the messaging scope at connect time is the whole setup: we subscribe the account, store the threads and check the window before a reply goes out.

Or skip Meta App Review entirely

Instagram is one of the networks Outstand ships a reviewed OAuth app for. Your users authorize against it, and every requirement above becomes our problem.

Going direct to Meta

  1. 1Convert the account to Instagram Professional and link a Facebook Page
  2. 2Create a Meta app, add the Instagram product, configure OAuth redirects
  3. 3Submit App Review for the publishing permissions and pass Business Verification
  4. 4Build the OAuth exchange, store the long-lived token and refresh it before 60 days
  5. 5Host the media on public HTTPS and build create-container → poll → publish, per item
  6. 6Handle 190s, 4/613 rate limits, EXPIRED containers and the 50-post daily ceiling

Weeks, and the review clock is not yours to control.

Going through Outstand

bash
curl -X POST https://api.outstand.so/v1/posts/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "containers": [
      {
        "content": "Our Q2 launch is live 🚀",
        "media": [
          { "url": "https://media.outstand.so/org_abc/med_abc.mp4", "filename": "launch.mp4" }
        ]
      }
    ],
    "accounts": ["YOUR_INSTAGRAM_ACCOUNT_ID"]
  }'

One call. We create the container, poll it, publish it, count it against the daily limit and refresh the token. Already through App Review yourself? Drop your own Meta app credentials in and the OAuth screen says your brand instead of ours - the same setup behind our white label social media management platform.

Building an agent instead of an app? The same account works with our Instagram MCP server and our social media API for AI agents.

Everything Instagram supports, through one endpoint

Every content type and capability available on Instagram via Outstand today.

Feed photos

Carousels (2–10)

Reels

Stories (24h)

Direct messages

PublishingSchedulingAnalyticsMedia handlingComments

Plus collaborators (invite up to 3), user tags, location and alt text, and DM replies with text or media. Reposting and deleting feed posts are not supported by Instagram's API.

Copy, paste, ship

The same accounts array also fans out to X, LinkedIn, TikTok and 8 more - one request, many platforms.

Publish a Reel

bash
curl https://api.outstand.so/v1/posts/ \
  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Behind the scenes of the shoot 🎬 #reels",
    "accounts": ["YOUR_INSTAGRAM_ACCOUNT_ID"],
    "media": [{ "url": "https://media.outstand.so/bts.mp4", "filename": "bts.mp4" }],
    "instagram": { "reelThumbOffset": 1500 }
  }'

Publish a carousel (2–10 items)

bash
curl https://api.outstand.so/v1/posts/ \
  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Swipe for the full lookbook →",
    "accounts": ["YOUR_INSTAGRAM_ACCOUNT_ID"],
    "media": [
      { "url": "https://media.outstand.so/1.jpg", "filename": "1.jpg" },
      { "url": "https://media.outstand.so/2.jpg", "filename": "2.jpg" },
      { "url": "https://media.outstand.so/3.jpg", "filename": "3.jpg" }
    ],
    "instagram": { "collaborators": ["partner_brand"] }
  }'

Publish a Story

bash
curl https://api.outstand.so/v1/posts/ \
  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "",
    "accounts": ["YOUR_INSTAGRAM_ACCOUNT_ID"],
    "media": [{ "url": "https://media.outstand.so/story.jpg", "filename": "story.jpg" }],
    "instagram": { "publishAsStory": true }
  }'

Schedule a post (up to 30 days out)

bash
curl https://api.outstand.so/v1/posts/ \
  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Drops Friday at noon ⏰",
    "accounts": ["YOUR_INSTAGRAM_ACCOUNT_ID"],
    "media": [{ "url": "https://media.outstand.so/teaser.jpg", "filename": "teaser.jpg" }],
    "scheduledAt": "2026-07-03T16:00:00Z"
  }'

Read analytics

bash
curl https://api.outstand.so/v1/posts/{id}/analytics \
  -H "Authorization: Bearer $OUTSTAND_API_KEY"
# → impressions, reach, likes, comments, saves, shares, views, engagement_rate

Read & reply to comments

bash
# Read the comments on a published Instagram post
curl "https://api.outstand.so/v1/posts/{id}/replies?network=instagram" \
  -H "Authorization: Bearer $OUTSTAND_API_KEY"

# Reply to a comment (or comment on your own post)
curl https://api.outstand.so/v1/posts/{id}/replies \
  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "Thanks for the love! 🙌", "account_username": "yourbrand" }'

Read & reply to DMs

bash
# List the connected account's DM threads, most recent first
curl "https://api.outstand.so/v1/conversations?social_account_id=YOUR_SOCIAL_ACCOUNT_ID" \
  -H "Authorization: Bearer $OUTSTAND_API_KEY"

# Read a thread
curl "https://api.outstand.so/v1/conversations/{id}/messages" \
  -H "Authorization: Bearer $OUTSTAND_API_KEY"

# Reply with text and an image - returns 202 with a pending message
curl https://api.outstand.so/v1/conversations/{id}/messages \
  -H "Authorization: Bearer $OUTSTAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Thanks for reaching out! Here is the size guide 👇",
    "media_urls": ["https://media.outstand.so/size-guide.jpg"]
  }'

Live in three steps

From signup to your first published Reel in about five minutes.

1

Connect Instagram

Your user authorizes via OAuth. We store and refresh the long-lived token. Use our Managed Keys, or bring your own Meta app for white-label.

2

Call POST /v1/posts/

Send text and media with accounts: ["YOUR_INSTAGRAM_ACCOUNT_ID"]. Add scheduledAt to schedule it for later.

3

We do the Graph API work

Container creation, status polling, retries and rate-limit handling - returned as a unified response with the published post ID.

Instagram API pricing and access

Meta does not bill you for the Instagram Graph API. It bills you in review cycles, account prerequisites and container plumbing - here is what each route actually costs.

What Meta charges
Nothing. The Instagram Graph API carries no licence fee or per-request cost. What gates you is App Review and account setup, not price.
Going direct to Meta
A Business or Creator account, a linked Facebook Page, a Meta app, and App Review for instagram_content_publish before you can post at all. Then you build the create-container → poll → publish flow and refresh long-lived tokens every 60 days.
Going through Outstand
An Outstand API key. Your users authorize with OAuth against our reviewed app, and one POST /v1/posts/ publishes a photo, carousel, Reel or Story. The same key reads and replies to DMs through /v1/conversations.
Outstand pricing
$19/month including 3,000 posts, then $0.007/post - dropping to $0.005/post at volume. No per-seat fees and no annual lock-in.
Bringing your own app
Already through App Review? Drop your Meta app credentials in and Outstand runs OAuth under your brand, so your users never see ours.

Instagram caps content publishing at 50 posts per rolling 24 hours per account. Outstand queues against that ceiling rather than burning your quota on retries.

More than an Instagram wrapper

One integration that grows with everything you ship next.

One API, 12 platforms

The same request publishes to Instagram, X, LinkedIn, TikTok and the rest. Same JSON shape everywhere.

Managed Keys or BYOK

Skip Meta App Review with our keys, or bring your own app for white-label OAuth under your brand.

Built for scale

Intelligent rate limiting, automatic token refresh, webhook events and media processing.

Honest pricing

$19/mo includes 3,000 posts, then from $0.007/post. No seats, no annual lock-in.

What you can build

Same endpoints, same data shapes - pick what you ship.

Social schedulers

Let your users queue and auto-publish Instagram content from your app.

AI agents

Give Claude or GPT a tool to post to Instagram. MCP server included.

Analytics dashboards

Pull Instagram reach and engagement alongside every other platform.

Agencies & white-label

Run Instagram posting for clients under your own brand and Meta app.

Social inboxes & support

Build a unified inbox or helpdesk that reads and answers Instagram DMs next to the content you publish.

Instagram API FAQ

The questions developers ask before they build.

Is the Instagram API free?

Meta charges no licence fee and no per-request price for the Instagram API - in that sense it is free. The cost is access: an Instagram Professional account, a Meta app, App Review for the publishing permissions and Business Verification before your first post goes out, then the engineering time around containers, polling and 60-day tokens. Outstand removes that setup - you get an API key and publish the same day.

How do I get Instagram API access?

Convert the account to Instagram Professional (Business or Creator), link a Facebook Page, create a Meta app with the Instagram product, then submit App Review for the publishing permissions and complete Business Verification. Until review passes, your app can only act on accounts that hold a role on it. Outstand ships an already-reviewed app, so your users can authorize with OAuth today.

Can I post to Instagram without app review?

Only to accounts that have a role on your own Meta app while it is in development mode - fine for testing, useless for customers. To publish for anyone else you either pass App Review yourself or authorize against an app that already has, which is what Outstand provides.

What is the Instagram Graph API rate limit?

Two separate ceilings. The Content Publishing Limit is 50 published posts per rolling 24 hours per Instagram account, readable at GET /{ig-user-id}/content_publishing_limit. On top of that, Meta applies platform rate limiting per app (Business Use Case rate limits), so all of your tenants share one budget. Exceeding either returns error 4 or 613.

Can I post Reels via the API?

Yes. Create the container with media_type=REELS and a public video_url, poll until status_code is FINISHED, then publish it. You can set a cover_url or a thumb_offset - not both - and invite up to 3 collaborators. Outstand does the whole sequence behind one POST /v1/posts/.

Does the Instagram API support Stories?

Yes, with media_type=STORIES and a single image or video - no captions and no carousels, and the story expires after 24 hours. In our own integration Meta rejects Story publishing for Creator accounts, so a Business account is the safe requirement.

Does Instagram have an official API?

Yes - the Instagram Graph API (part of the Meta Graph API) lets approved apps publish posts, Reels, Stories and read insights for Business and Creator accounts. Outstand is a managed layer on top of it, so you skip the setup.

Can I post Instagram Reels via API?

Yes. Send a video file to POST /v1/posts/ with accounts: ["YOUR_INSTAGRAM_ACCOUNT_ID"] and Outstand publishes it as a Reel - you can set a thumbnail offset and invite collaborators.

Can I schedule Instagram posts via the API?

Yes. Add a scheduledAt ISO-8601 timestamp (up to 30 days ahead) and Outstand publishes it automatically - no cron or queue on your side.

Do I need a Business or Creator account?

Yes - Instagram only allows API publishing for Business or Creator accounts (a Meta requirement, not ours). Personal accounts cannot publish via any Instagram API.

Can I post Instagram Stories via API?

Yes. Set instagram.publishAsStory: true with a single image or video. Stories do not support carousels or captions and expire after 24 hours.

How do I get an Instagram access token?

With Outstand you do not manage tokens manually - your user connects through OAuth and we store and auto-refresh the long-lived token. Bring your own Meta app for white-label, or use our Managed Keys.

What are the Instagram API rate limits?

Instagram caps content publishing at 50 published posts per rolling 24 hours per account, and separately applies app-level platform rate limits. Outstand queues and throttles requests under the hood, so you never handle a 429 yourself.

Can I read and reply to Instagram comments via the API?

Yes. Read the comments on a published post with GET /v1/posts/{id}/replies and post a reply with POST /v1/posts/{id}/replies. Comment management requires a Business or Creator account.

Can I read and reply to Instagram DMs via the API?

Yes. The Outstand Conversations API lists the DM threads of a connected Instagram account with GET /v1/conversations, reads each thread with GET /v1/conversations/{id}/messages and replies with POST /v1/conversations/{id}/messages - text, media or both, sent now or scheduled for later. The connection needs the instagram_business_manage_messages permission.

Can I send the first DM to an Instagram user via the API?

No. Instagram messaging is reply-only: a conversation starts when someone messages the connected account, and you reply inside that thread. There is no endpoint to cold-message an arbitrary Instagram user, through Outstand or directly through Meta.

What is the Instagram 24-hour messaging window?

Free-form replies must be sent within 24 hours of the contact's last inbound message - your own replies do not extend it. For a human replying to a support issue, the HUMAN_AGENT tag extends the window to 7 days, which requires Meta approval on the app. Outstand checks the window when you send, including for scheduled replies, so an out-of-window message is rejected immediately instead of failing later.

Do Instagram DMs work with my own Meta app?

Messaging works on our managed Meta app today. If you bring your own app for white-label OAuth, it needs its own approval for the instagram_business_manage_messages permission before your users can grant it.

How do I know when a new Instagram DM arrives?

Subscribe to the conversation.started and message.received webhooks. Outstand receives the message from Instagram, stores it and forwards the event to your endpoint; message.sent and message.failed report the outcome of your replies.

Can I post to Instagram without the Graph API?

Not officially - the Graph API is the only sanctioned way to publish. Outstand uses it for you so you get a clean REST endpoint instead of the container-and-polling flow.

How much does the Instagram API cost?

Meta charges no licence fee for the Instagram Graph API - the cost is App Review, a linked Facebook Page and the engineering time around the container flow. Outstand costs $19/month including 3,000 posts, then $0.007/post dropping to $0.005 at volume, on an app that is already reviewed.

How do I get an Instagram API key?

Instagram does not issue a plain API key. You create a Meta app, request the instagram_content_publish permission, pass App Review, then exchange OAuth codes for long-lived tokens that you refresh every 60 days. Outstand gives you one bearer token that works across Instagram and 11 other networks and refreshes the Meta tokens behind it.

What happened to the Instagram Basic Display API?

Meta shut it down in December 2024. Apps that relied on it had to move to the Instagram Graph API or the Instagram API with Instagram Login. Publishing has always run through the Graph API, which is what Outstand calls on your behalf.

Can I post an Instagram carousel via the API?

Yes. Send 2 to 10 media items in the media array and Outstand assembles the child containers in order and publishes them as one carousel - including mixed images and video.

Ship Instagram posting and DMs today

Grab an API key and publish your first Reel in the next five minutes - then answer your DMs from the same API. 3,000 posts included, then $0.007/post - dropping to $0.005 at volume.