Skip to main content

1. Overview

Instagram processes media before it can be published, so every post type (image, video, reel, carousel, story) goes live once Instagram reports the media ready.

2. Supported media

content_type is required to publish or schedule an Instagram post, including a draft you publish later. Without it, the post fails validation with 400 publishing.validation_failed. A draft can be saved without it, because drafts skip validation. On an account connected through Facebook Login, content_type is optional: see Differences from Instagram Login. File limits for media you upload to SocialAPI (source_type: "media_id"): images must be JPEG and at most 8 MB. Videos must be MP4 or QuickTime, at most 300 MB, and 3 seconds to 15 minutes long. A Story video has the tighter limits of 3 to 60 seconds and 100 MB. A file outside these limits fails validation with 400 publishing.validation_failed. A url item is not checked in advance, so a PNG sent by URL reaches Instagram and can fail there. Caption limits: 2,200 characters, 30 hashtags, and 20 mentions. More returns 400 publishing.validation_failed. Carousel limits: 2 to 10 items, images and videos mixed. Only the carousel itself carries the caption. A caption on an item is ignored. Stories expire 24 hours after posting and carry no caption.
Attach media through the media array. Each item sets a source_type: media_id for a file you uploaded to SocialAPI, or url for a public http(s) URL SocialAPI fetches at publish time. There is no separate image_url, video_url, or media_urls field. To upload first, use POST /v1/media/upload (multipart) or the presigned-URL flow (GET /v1/media/upload-url → PUT the file → POST /v1/media/:id/verify); the verify step is required, without it the media stays pending, and a post that names media that is not ready returns 400 publishing.validation_failed.Instagram also accepts source_type: "platform_attachment_id" for a container you created through the Graph API yourself. It must then be the only item in media: Instagram cannot combine a pre-built container with other media, and doing so returns 422 publishing.media.mixing_unsupported.

3. Create post

Use POST /v1/posts with targets targeting an Instagram account. The text field becomes the caption.
Response (HTTP 201):
SocialAPI publishes in the background. The target moves from pending to publishing, then to published or failed. Poll GET /v1/posts/{pid} or subscribe to the post.published, post.partial, and post.failed webhook events. Publishing or scheduling costs 1 post credit; a draft is free (see Plans and credits).

Platform data fields

Send these keys in platform_data.instagram on the post, which applies them to every Instagram target. To set them for one target, send them flat in that target’s platform_data (targets[].platform_data, with no instagram key). When both set the same key, the target’s value wins. SocialAPI forwards any additional keys in platform_data.instagram that the table above lists for the post type you publish, and no others. Any other key, including a key listed for another post type, is not sent, and the response lists each one as a validation warning naming the key. location_id must be a string of digits, share_to_feed and is_ai_generated must be booleans, and alt_text on a Reel is not sent.

AI-generated content

Set is_ai_generated: true to add Instagram’s AI info label. It applies to feed posts, Reels, stories and carousels. On a carousel it labels the whole post: there is no per-slide option. Per Meta, the label is for AI-generated media, not AI-written captions.

Scheduling

Set scheduled_at to an ISO 8601 timestamp (UTC) to defer publishing:

First comment

Set first_comment to a string to post an automated comment immediately after publish. This is best-effort: if the comment fails (for example due to a temporary rate limit), the post is still considered published.

4. Update post

A published Instagram post cannot be edited through SocialAPI. PATCH /v1/posts/{pid} returns 409 post.state_invalid for any post whose status is not draft, scheduled, or failed, with the message “Only draft, scheduled, or failed posts can be edited”. A post in draft, scheduled, or failed status can be edited with PATCH /v1/posts/{pid} before it publishes. To correct a published post, delete it in the Instagram app and publish a new post.

5. Delete post

On an account connected through Facebook Login, DELETE /v1/posts/{pid} deletes the post from Instagram, and POST /v1/posts/{pid}/unpublish removes it from Instagram and keeps it in SocialAPI. The rest of this section covers Instagram Login. On Instagram Login, DELETE /v1/posts/{pid} returns HTTP 200 with results[].outcome: "not_supported" for an Instagram target, and the post stays live on Instagram. POST /v1/posts/{pid}/unpublish returns 501 resource.not_supported for an Instagram target. Instagram Login gives apps no media deletion call, so SocialAPI refuses rather than report a deletion that did not happen. Delete the post in the Instagram app. For a cross-platform post, the other platforms are still deleted or unpublished. The response names every platform whose content is still live, so an Instagram target refusing does not undo the targets that succeeded. See Delete a post.

6. Retrieve posts

Use GET /v1/posts to list posts. Filter by platform or account:
Each post includes a targets array with per-platform status and engagement metrics:
Metrics notes:
  • likes and comments come from the media object itself. shares, saves and the insight metrics below are read live from GET /v1/posts/{pid}/metrics.
  • metrics_synced_at reflects when SocialAPI last refreshed the metrics from Instagram.

Live metrics and insights

GET /v1/posts/{pid}/metrics reads the post’s current counts and insights from Instagram:
The metric names are the same on every platform, so code written against them does not change per platform. Which metrics Instagram reports depends on the media type:
  • A metric Instagram has not reported is absent rather than returned as 0, so a missing key means “not reported”, not “zero”.
  • interactions is Instagram’s own total of likes, comments, saves and shares. skip_rate is a percentage.
  • If an Instagram target returns likes and comments but none of the insight metrics, the account has no instagram_business_manage_insights scope. Reconnect the account to enable them.
  • Instagram’s insight data can lag by up to 48 hours.

Settings on published posts

An Instagram target can include a settings object with the options Instagram reports back for the published post: settings is absent when Instagram reported none of these for the post. Instagram does not report location, user tag, audio name, or trial settings back, so those never appear here even when you set them at publish time. settings is also returned for posts published outside SocialAPI, since it reflects what Instagram reports at the time of the read for the post rather than what was sent through this API.

Media on published posts

An Instagram target includes platform_media: every image and video on the published post, carousel slides in order. It is returned for posts published outside SocialAPI too, so you can read the photos and videos of an account’s existing posts. The links expire. Download the files when you read the post rather than storing the links. To get fresh links, call POST /v1/accounts/{id}/sync, then read the post again. url is absent when Instagram withholds the file, for example a video with licensed audio. The cover image is still included. POST /v1/accounts/{id}/sync covers up to the 500 most recent posts on an Instagram account. Each account can be synced once every 5 minutes; an earlier call returns 429 accounts.sync_rate_limited with a Retry-After header. The account needs the posts.read permission: without it the call returns 403 account.permission_missing. platform_media is separate from media, which lists what you sent when publishing through SocialAPI and is empty for posts published elsewhere.

7. Quirks, errors, and recovery

Media processing delay

A target still processing after about 5 minutes fails with 504 platform.instagram.container_timeout.

First comment is non-blocking

If first_comment is set and the comment fails after publish, the post stays published. Nothing in the post response reports the failed comment.

Stories expire

Instagram removes a Story 24 hours after posting. The post stays in SocialAPI.

Posting caps

SocialAPI refuses a publish that would pass 25 posts in an hour or 45 posts in 24 hours for one account, with 429 publishing.velocity_limit. A scheduled post is held and published when the window clears. Instagram’s own cap is 50 posts per rolling 24 hours: GET /v1/accounts/{id}/limits returns posts_remaining. When Instagram refuses first, the target fails with 429 platform.instagram.rate_limit. See Posting velocity caps.

Errors on a failed target

A publish that fails after the request returned puts a structured error on the target, in targets[].error:
  • 401 platform.instagram.auth: the access token expired or was revoked. Reconnect the account.
  • 429 platform.instagram.rate_limit: Instagram’s own limit. Wait, then retry.
  • 422 platform.instagram.container_failed: Instagram accepted the media, then failed to process it. See “Container failures report no reason”.
  • 422 platform.instagram.container_expired: the media container was not published within 24 hours. Create the post again.
  • 504 platform.instagram.container_timeout: the media was still processing after about 5 minutes.
  • 503 platform.instagram.1, 503 platform.instagram.2: a temporary Instagram failure. Retry.
  • 502 platform.instagram.api_error: a failure reaching Instagram, or a response with no usable error.
  • platform.instagram.<code>: Instagram refused the request with its own error code and message, for example “The aspect ratio is not supported.” The HTTP status is 502 unless the code has its own mapping, such as 403 platform.instagram.200, 404 platform.instagram.100, or 409 platform.instagram.9007. Read message.
Every code is in Errors.

Container failures report no reason

When Instagram rejects media during processing, it reports only a status of ERROR, with no code and no message. platform.instagram.container_failed reports the failure and the container ID, not a cause. It almost always means the file does not meet Instagram’s specifications: for Reels, MOV or MP4 with H.264 or HEVC video, up to 300 MB and 15 minutes, 9:16 recommended.

Recovery

  • Rate limit. Wait, then send POST /v1/posts/{pid}/retry. See Delivery.
  • Auth error. Reconnect the Instagram account through the OAuth flow to get a fresh token.
  • Container timeout. A large video or a slow media URL causes it. Upload the media again and retry.
  • Container failed. A temporary fault and a rejected file look the same. Retry once with POST /v1/posts/{pid}/retry. If it fails again, re-encode the media to the specifications above.

8. Full worked example

The following example publishes a Reel with a cover image, a collaborator tag, and a first comment. Step 1: Upload media
Response:
Upload the file, then verify:
Step 2: Publish the post
Step 3: Check status
Response once published:

9. Required OAuth scopes

SocialAPI’s Meta app requests these scopes during the OAuth flow. You configure nothing. The full list is in Instagram scopes. Publishing uses instagram_business_basic, instagram_business_content_publish, and, for first_comment, instagram_business_manage_comments. The scopes a scoped API key needs, the credit cost of a post, and the MCP tools that publish are on Publishing.
Last modified on October 19, 2026