Publishing to Instagram via API: A technical guide
Understanding permissions, the container model, video uploads, and API endpoints for Instagram content publishing.
Before you start: App review
Before your app can publish to Instagram professional accounts in production, your permissions need to pass Meta’s app review:
- App Review - Each permission requires a separate app review submission with a screencast demonstrating how your app uses that specific permission
- Advanced Access - Required if your app publishes on behalf of Instagram accounts you don’t own or manage
The review process takes 2-4 weeks per Meta’s documentation. Each screencast must show the complete user journey for that permission in your app.
Required permissions
To publish content and access insights on an Instagram professional account, your app needs these permissions approved:
instagram_business_basic- Access basic account informationinstagram_business_content_publish- Create and publish mediainstagram_business_manage_comments- Manage comments on mediainstagram_business_manage_insights- Access account and media analytics
Each of these permissions needs its own app review submission with a screencast.
The container model
Instagram publishing uses a two-step process that differs from Facebook’s direct endpoints:
- Create a container — POST to
/{ig-user-id}/mediato create the media container and upload the content - Publish the container — POST to
/{ig-user-id}/media_publishwith the container ID to make it live
All media must be hosted on a publicly accessible URL at the time of the create request. Meta will cURL your media directly from that URL.
Publishing images
POST to /{ig-user-id}/media:
{ "image_url": "https://example.com/photo.jpg", "caption": "Your caption text"}Returns a container ID on success. Publish it with a POST to /{ig-user-id}/media_publish:
{ "creation_id": "<IG_CONTAINER_ID>"}Only JPEG is supported. Extended JPEG formats such as MPO and JPS are not.
Publishing videos
Videos use the same create-then-publish flow with an additional media_type parameter:
POST to /{ig-user-id}/media:
{ "video_url": "https://example.com/video.mp4", "media_type": "VIDEO", "caption": "Your caption text"}For large videos or unreliable connections, use the resumable upload flow instead:
Step 1: Create a resumable upload container
POST to /{ig-user-id}/media with upload_type set to resumable:
{ "media_type": "VIDEO", "upload_type": "resumable"}Returns: {"id": "<IG_CONTAINER_ID>"}
Step 2: Upload the video
POST to https://rupload.facebook.com/ig-api-upload/{api-version}/{ig-container-id} with headers:
Authorization: OAuth <ACCESS_TOKEN>offset: 0file_size: <file_size_in_bytes>Include the video as binary data in the request body.
Returns: {"success": true, "message": "Upload successful."}
Step 3: Publish the container
POST to /{ig-user-id}/media_publish with the container ID from step 1.
Publishing carousels
Carousels allow up to 10 images and/or videos in a single post. This requires three steps: create containers for each item, create a parent carousel container, then publish.
Step 1: Create item containers
Create a container for each image or video with is_carousel_item set to true:
{ "image_url": "https://example.com/photo1.jpg", "is_carousel_item": true}Repeat for each item, up to 10.
Step 2: Create the carousel container
POST to /{ig-user-id}/media:
{ "media_type": "CAROUSEL", "children": "<CONTAINER_1>,<CONTAINER_2>,<CONTAINER_3>", "caption": "Carousel caption"}Step 3: Publish
POST to /{ig-user-id}/media_publish with the carousel container ID.
All carousel images are cropped to match the aspect ratio of the first image, defaulting to 1:1.
Reels and stories
Reels and stories use the same create-then-publish flow with different media_type values:
- Reels: Set
media_typetoREELSand providevideo_url - Stories: Set
media_typetoSTORIESand provideimage_urlorvideo_url
Trial reels — shared only to non-followers initially — are supported via the trial_params parameter:
{ "media_type": "REELS", "video_url": "https://example.com/video.mp4", "trial_params": { "graduation_strategy": "MANUAL" }}graduation_strategy accepts MANUAL (graduated in-app by the user) or SS_PERFORMANCE (automatically graduated based on performance).
Checking container status
If a publish request doesn’t return a media ID, check the container’s status:
GET /{ig-container-id}?fields=status_code
Possible values:
IN_PROGRESS— Still processingFINISHED— Ready to publishPUBLISHED— Successfully publishedEXPIRED— Container was not published within 24 hoursERROR— Publishing failed
Query once per minute, for no more than 5 minutes.
Rate limits
Instagram accounts are limited to 100 API-published posts per 24-hour moving period. Carousels count as a single post. The limit is enforced at the media_publish endpoint.
To check current usage before publishing, GET /{ig-user-id}/content_publishing_limit.
The same video, through Postproxy
Here’s how it’s done with Postproxy. One simple request with only what matters:
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "3 tips that changed how we approach customer onboarding" }, "profiles": ["instagram"], "media": ["https://example.com/video.mp4"] }'One request. Postproxy handles the container creation, the resumable upload, the status polling, and the publish step.
What Postproxy handles
Postproxy maintains approved permissions and handles the complexity:
- All required permissions approved through app review
- Instagram User access token exchange and refresh
- The two-step container creation and publishing flow
- Resumable video uploads via rupload.facebook.com
- Unified API for images, videos, carousels, reels, and stories
- Rate limit monitoring and request pacing
Your system sends content. Postproxy handles the Instagram-specific implementation.
Connect your Instagram account and start publishing through the Postproxy API.