Instagram Carousel API: Post Multi-Image Carousels
The Instagram carousel API is a three-step Graph API flow. Create a child container for each image or video with is_carousel_item=true, create a parent container with media_type=CAROUSEL and the child IDs in children, then call media_publish. A carousel holds 2 to 10 items, can mix images and videos, and counts as one post. Adeli publishes image carousels in one request.
The Instagram carousel API is the carousel path of Meta's content publishing flow in the Instagram API. You create one child container per item with is_carousel_item=true, then one parent container with media_type=CAROUSEL that lists the children, then publish the parent with media_publish. A carousel takes 2 to 10 images, videos, or a mix, and publishes as a single post.
Below is each call, the rules for carousel items, and the errors behind most "carousel post not working" threads. For single images, Reels, and Stories, see our guide to posting to Instagram via API.
What do you need before you start?
The same setup as any Instagram publish. From Meta's content publishing guide:
- A professional account. Business or Creator. Personal accounts can't be published to.
- The publishing permission.
instagram_business_content_publishwith Instagram Login, orinstagram_content_publishpluspages_read_engagementwith Facebook Login. Our permissions guide covers both. - Public media URLs. Meta downloads each item from your server. Images have to be JPEG.
- The account's Instagram user ID (
IG_IDbelow).
The examples use Instagram Login and v26.0, which is what Meta's carousel examples use. With Facebook Login, swap the host to graph.facebook.com.
How does the Instagram carousel API work?
Three steps, and step 1 runs once per item.
| Step | Request | What you send | What you get |
|---|---|---|---|
| 1. Child containers | POST /<IG_ID>/media |
image_url or video_url, is_carousel_item=true |
One container ID per item |
| 2. Carousel container | POST /<IG_ID>/media |
media_type=CAROUSEL, children, caption |
The carousel container ID |
| 3. Publish | POST /<IG_ID>/media_publish |
creation_id = carousel container ID |
The published media ID |
A 3-image carousel takes 5 calls, plus status checks, and any one item can fail the post.
Step 1: Create a container for each item
Send each image or video with is_carousel_item set to true. For an image:
curl -X POST "https://graph.instagram.com/v26.0/<IG_ID>/media" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-d '{"image_url":"https://www.example.com/images/slide-1.jpg","is_carousel_item":true}'For a video item, swap image_url for video_url and set media_type to VIDEO, the video type Meta's parameter list gives (not REELS):
curl -X POST "https://graph.instagram.com/v26.0/<IG_ID>/media" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-d '{"media_type":"VIDEO","video_url":"https://www.example.com/videos/slide-2.mp4","is_carousel_item":true}'Each call returns { "id": "<CHILD_ID>" }. Keep the IDs in the order you want the slides to appear.
What the IG User media reference allows on a child:
| Field | On a carousel item? |
|---|---|
image_url / video_url |
Yes, one per item |
user_tags |
Yes |
alt_text |
Yes, on image items only |
caption |
No, set it on the parent |
location_id |
No, set it on the parent |
is_ai_generated |
No |
media_type=REELS |
No. Reels can't appear in carousels |
Step 2: Create the CAROUSEL container
Send media_type=CAROUSEL, the child IDs in children, and the caption.
curl -X POST "https://graph.instagram.com/v26.0/<IG_ID>/media" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-d '{"media_type":"CAROUSEL","caption":"Three slides, one post","children":"<CHILD_ID_1>,<CHILD_ID_2>,<CHILD_ID_3>"}'Meta's guide describes children as "a comma separated list of up to 10 container IDs." The parent also accepts:
caption: up to 2,200 characters, 30 hashtags, and 20 @ tags.collaborators: up to 3 usernames invited as co-authors.location_id: a location for the whole post.
Step 3: Check the status and publish
Wait for FINISHED, then publish the carousel container.
# Check status
curl "https://graph.instagram.com/v26.0/<IG_CAROUSEL_CONTAINER_ID>?fields=status_code" \
-H "Authorization: Bearer <ACCESS_TOKEN>"
# Publish
curl -X POST "https://graph.instagram.com/v26.0/<IG_ID>/media_publish" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-d '{"creation_id":"<IG_CAROUSEL_CONTAINER_ID>"}'Meta recommends checking status once per minute for no more than 5 minutes. Meta's carousel section doesn't say whether to check the children first. With video items, we'd poll each video child until it reads FINISHED before creating the parent, since video processes on Meta's side and an unfinished item can block the publish.
The publish call returns the live post's media ID. Store it for comments, permalinks, and insights.
What are the Instagram carousel limits?
2 to 10 items, one caption, and cropping set by the first item. From the content publishing guide and the media reference:
| Limit | Rule |
|---|---|
| Item count | 2 to 10 images, videos, or a mix |
| Image format | JPEG only. MPO and JPS aren't supported |
| Image aspect ratio | 4:5 to 1.91:1 |
| Cropping | Every image is cropped to the first image's aspect ratio, 1:1 by default |
| Reels | Can't be carousel items |
| Shopping tags and filters | Not supported through the API |
| Containers | Expire after 24 hours; 400 per account per rolling 24 hours, children included |
| Publishing limit | A carousel counts as one post |
Meta's guide gives two numbers for the publishing limit. The carousel section says 50 posts per 24 hours, and the rate limit section says 100. Read quota_usage from GET /<IG_ID>/content_publishing_limit instead of hardcoding either.
Meta's reference doesn't publish a separate length or file size limit for carousel videos. Test the longest and largest file you plan to support before you promise it to users.
Why is my Instagram carousel post not working?
Most carousel failures trace back to one item or to timing. These come from Meta's error code reference:
| Code / subcode | Meta's message (short) | Usual cause and fix |
|---|---|---|
| 100 / 2207028 | Won't work as a carousel | Fewer than 2 or more than 10 items. Count the children list. |
| 9007 / 2207027 | Media is not ready for publishing | A child or the parent hasn't finished processing. Poll each one for FINISHED. |
| 9004 / 2207052 | Media could not be fetched from this URI | One item's URL isn't public or expired. Meta names the URL in the message. |
| 36003 / 2207009 | Invalid aspect ratio | An image is outside 4:5 to 1.91:1. Crop or pad it. |
| 352 / 2207026 | Video format is not supported | Re-encode the video item as MP4 or MOV. |
| 24 / 2207008 | Media builder does not exist or has expired | Retry once or twice within 2 minutes, then rebuild the container. |
| -2 / 2207020 | Media has expired | A child is older than 24 hours. Create a new one. |
| -1 / 2207053 | Unknown upload error | Mostly video items. Create a new container and retry. |
| 9 / 2207042 | Reached maximum number of posts | The account hit its publishing limit. Retry after the window rolls. |
Three mistakes that don't show up as clean error codes:
- Sending an array of URLs to the parent.
childrentakes container IDs, not media URLs. Create the children first. - Using
media_type=REELSon a video child. Reels aren't allowed in carousels. Use a plain video container. - Expecting slide order from completion order. The post follows the order of IDs in
children, so track IDs by slide position, not by which upload finished first.
Is there a faster way to post Instagram carousels?
Yes. With Adeli, a carousel is one request. Adeli's Instagram API holds the Meta developer app, carried Meta's review, and refreshes every connected account's tokens. You hold one API key, and you don't host media or poll containers.
The publish endpoint takes a carousel as an images array of 2 to 10 images, sent as base64 JPEG, PNG, or WebP. Adeli converts them to JPEG and stages them where Instagram can fetch them. Alt text and people tags go on each image; the caption, collaborators, and AI label go on the post:
curl -X POST "https://app.tryadeli.com/api/v1/posts" \
-H "Authorization: Bearer $ADELI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "instagram",
"accountId": "<ADELI_ACCOUNT_ID>",
"caption": "Three slides, one post",
"collaborators": ["partneraccount"],
"images": [
{ "contentType": "image/webp", "base64": "...", "altText": "Slide one" },
{ "contentType": "image/png", "base64": "...", "userTags": [{ "username": "teammate", "x": 0.4, "y": 0.6 }] },
{ "contentType": "image/jpeg", "base64": "..." }
]
}'A successful call returns 201 with the normalized post, including the permalink. A tag or collaborator Instagram can't resolve fails with 422 provider_rejected and Instagram's own explanation.
The documented carousel request takes images. For a carousel that mixes images and video, use Meta's flow above. The same posting API publishes to TikTok, YouTube, Facebook, X, and Bluesky, and your first 3 connected accounts are free on Adeli's pricing.