# Instagram Carousel API: Post Multi-Image Carousels

How to publish an Instagram carousel with the Graph API: child containers, the CAROUSEL container, media_publish, item limits, video, and fixes for errors.

Canonical page: https://www.tryadeli.com/blog/instagram-carousel-api
Last updated: 2026-10-19

By Mika Reyes, Co-founder, Adeli. Published 2026-10-19.

## The short answer

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](https://www.tryadeli.com/blog/instagram-api-guide). **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](https://www.tryadeli.com/blog/post-to-instagram-via-api).

## What do you need before you start?

**The same setup as any Instagram publish.** From Meta's [content publishing guide](https://developers.facebook.com/docs/instagram-platform/content-publishing):

- **A professional account.** Business or Creator. Personal accounts can't be published to.
- **The publishing permission.** `instagram_business_content_publish` with Instagram Login, or `instagram_content_publish` plus `pages_read_engagement` with Facebook Login. Our [permissions guide](https://www.tryadeli.com/blog/instagram-api-permissions) covers both.
- **Public media URLs.** Meta downloads each item from your server. Images have to be JPEG.
- **The account's Instagram user ID** (`IG_ID` below).

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:

```bash
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`):

```bash
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](https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media) 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.**

```bash
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.**

```bash
# 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](https://developers.facebook.com/docs/instagram-platform/content-publishing) and the [media reference](https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media):

| 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](https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/error-codes):

| 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.** `children` takes container IDs, not media URLs. Create the children first.
- **Using `media_type=REELS` on 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](https://www.tryadeli.com/product/instagram) 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](https://www.tryadeli.com/docs/api/posts/publish-instagram) 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:

```bash
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](https://www.tryadeli.com/social-media-posting-api) publishes to TikTok, YouTube, Facebook, X, and Bluesky, and your **first 3 connected accounts are free** on [Adeli's pricing](https://www.tryadeli.com/pricing).

## Frequently asked questions

### Can an Instagram carousel mix photos and videos through the API?

Yes. Meta says carousels can hold up to 10 images, videos, or a mix of the two. Each video item is a regular video container with is_carousel_item set to true. Reels can't be carousel items, so don't set media_type to REELS on a child.

### Can I add a caption to each carousel slide?

No. Instagram carousels have one caption, set on the parent CAROUSEL container. Meta's reference says caption and location_id aren't supported on carousel items. Alt text can still be set on each image item.

### Does a carousel count as one post against the Instagram API limit?

Yes. Meta says publishing a carousel counts as a single post against the publishing limit. Every child container still counts toward the 400 containers an account can create in a rolling 24 hours, so a 10-item carousel uses 11 containers.

### Can I get insights for each slide of a carousel?

No. Meta's media insights reference says insights aren't available for any media inside an album. Request insights on the carousel's own media ID instead.

## Related Adeli pages

- [Instagram API for Developers](https://www.tryadeli.com/product/instagram.md): Publish, schedule, and read analytics for Instagram through one API. Adeli owns the Instagram developer app, token refresh, and Meta review. Free to start.
- [Social Media Posting & Scheduling API](https://www.tryadeli.com/social-media-posting-api.md): Adeli's social media posting and scheduling API publishes to Instagram, TikTok, YouTube, Facebook, X, and Bluesky in one request, now or at a set time. Free to start.
- [All posts on the Adeli blog](https://www.tryadeli.com/blog.md)

## About the author

Mika Reyes is the CEO behind Adeli and a tech and AI content creator. She was previously co-founder and CEO of Parallax, which was acquired by Phantom, and a product lead at LinkedIn. She is a Forbes 30 Under 30 honoree.
