Instagram Reel container status_code: wait for FINISHED, not 200
An Instagram Reel container is not publishable until status_code is FINISHED, and it EXPIRES after 24 hours. A polling loop and the 100-post daily limit.

After you create a Reel container, Instagram processes the video asynchronously. You read GET /IG_CONTAINER_ID?fields=status_code and publish only when it returns FINISHED. An unpublished container goes EXPIRED after 24 hours, and a failed run reports ERROR. The same page limits an account to 100 API-published posts within a 24-hour moving period, a limit it ties to the media_publish endpoint, and it suggests checking a container once per minute for no more than five minutes.
Status values on the page
| `status_code` | Meaning on the page |
|---|---|
IN_PROGRESS | Still processing |
FINISHED | Ready for publication |
PUBLISHED | Media successfully published |
ERROR | Publishing process failed |
EXPIRED | Container not published within 24 hours |
A polling loop
Poll with a delay, stop on any terminal value and give up before the 24-hour expiry. The get_status function is yours: it calls the Graph endpoint with your token.
import time
def wait_finished(get_status, every=60, limit=300):
waited = 0
while waited < limit:
code = get_status()
if code in ("FINISHED", "PUBLISHED"):
return code
if code in ("ERROR", "EXPIRED"):
raise RuntimeError("container " + code)
time.sleep(every)
waited += every
raise TimeoutError("still IN_PROGRESS after %d s" % limit)
print(wait_finished(lambda: "FINISHED"))
Avoid creating a second container
If the loop times out, check the status once more later rather than creating a new container for the same file. The 100-post limit is tied to publishing, so the cost of a duplicate container is wasted processing and a second file to track, not quota. With Sume, keep the finished video URL from the job result and reuse it: video trim returns a durable MP4 URL, and a job can be read again by id without paying twice.
Order of operations
Create the container, store its id, poll until it is FINISHED, then publish and record the returned media id. Write each step to your log with the container id so a stuck item can be found by hand. If you must retry after ERROR, change the cause first (length, aspect, codec) and then create a new container; repeating the same file usually repeats the failure. Check the video before step one with a probe: width, height, duration and fps are all in the response of video inspect.
Trial Reels
The same page documents trial_params on Reels with one field, graduation_strategy, set to MANUAL (graduate in the app) or SS_PERFORMANCE (automatic graduation if performance is strong). See Trial Reels and graduation strategy.
Limits
The loop follows the page's suggestion of one check per minute for up to five minutes; lengthen it only if your own tests show Reels need longer. The page does not say whether Reels have their own daily cap beyond the shared 100 posts, so treat 100 as the ceiling for all API-published posts.
Sources
Related posts
More in Developers
- Instagram Reels 300 MB file limit: bitrate budget by length
Instagram's Reels API caps files at 300 MB and 25 Mbps. Past about 96 seconds the file size binds. The average bitrate per length, and what Sume can check.
- Instagram Reels API frame rate 23 to 60: set fps in Sume
Meta's Reels API accepts 23 to 60 fps. Sume trim and Timeline output fps takes 24, 25, 30 or 60, and warns when it resamples. What to pick for a Reel.
- Instagram Reels API codec rules: HEVC or H.264, 4:2:0, closed GOP
Meta's Reels API wants MOV or MP4, HEVC or H.264, progressive, closed GOP, 4:2:0. What Sume's exact and keyframe trims produce, and what the docs leave open.
- Instagram Reels audio_name: you can rename the audio only once
Meta lets you rename a Reel's original audio once, in the container or on the audio page. Decide the name before publish and detach the track with Sume.
Written by Sume