# Instagram carousels through the API: the limits nobody documents together

If you have ever built against the Instagram Graph API, you already know the
documentation is spread across four pages that never quite agree. Carousels are
the worst of it: the limits, the container lifecycle and the failure modes are
each documented somewhere, and never in the same place.

Here is the whole thing in one page.

## A carousel is three API calls, not one

There is no endpoint that takes an array of images. Publishing a carousel is:

1. **One container per slide.** `POST /{ig-user-id}/media` with `image_url` (or
   `video_url`) and, critically, `is_carousel_item=true`.
2. **One parent container.** `POST /{ig-user-id}/media` with
   `media_type=CAROUSEL`, the caption, and `children` set to the comma-separated
   list of child container ids.
3. **Publish.** `POST /{ig-user-id}/media_publish` with the parent's id.

Miss `is_carousel_item=true` on a child and Meta happily creates a normal
single-image container. You will not find out until step two rejects it.

## Ten slides, not twenty

The app lets someone swipe together twenty images. The API stops at ten.

This catches people out because it is a product limit expressed as an API
error, and the error does not say "the app allows more". If your source of truth
is what you can do on your phone, you will build a feature that fails for your
users on the eleventh slide.

There is no flag, permission or review process that raises it.

## Containers are asynchronous, and that is the real work

The call that creates a container returns an id immediately. The container is
not ready.

```
GET /{container-id}?fields=status_code
```

`status_code` moves through `IN_PROGRESS` to `FINISHED`, or to `ERROR`. You
cannot publish until every child and the parent report `FINISHED`. Publishing
early returns an error about media not being ready, which reads like a bug in
your code and is actually a race you have to wait out.

For images this is usually a second or two. For video it can be minutes, and a
carousel mixing both is as slow as its slowest video.

### Polling has to have a budget

This is the part that turns a working integration into a broken one at 3am.

If you poll forever, a stuck container holds a worker open. If your scheduler
also has a "this job looks stuck, reclaim it" timeout, and your polling budget
is longer than that timeout, a second worker picks up the same post while the
first is still waiting. Both eventually publish. The customer gets the same
carousel twice, on their real audience, and there is no undo.

The fix is arithmetic, not cleverness: **your total polling budget must be
comfortably less than your stuck-job timeout.** Half is a reasonable choice. If
your scheduler reclaims after five minutes, stop polling at two and a half and
fail the job cleanly.

## What to do when a slide fails

A child container that reaches `ERROR` cannot be repaired. The parent cannot be
created without it, and the ids are not reusable.

The honest behaviour is to fail the whole post with a message naming the slide,
rather than publishing a carousel with a hole in it. A four-slide carousel
posted as three slides is not a partial success; it is a different post, and
usually one the author would not have approved.

## The short version

Build the children, wait for all of them, build the parent, wait for it,
publish. Cap at ten. Budget your polling against your own reclaim timeout, not
against Meta's patience.

Everything else about Instagram publishing is easier than this.
