DutaSign in

Notes · · 5 minute read

The Instagram publishing rules that have nothing to do with file size

Instagram’s content publishing API refuses images that are not JPEG, refuses any post without a photo or video, requires feed photos between 4:5 and 1.91:1, and rejects captions with more than 30 hashtags or 20 mentions. None of these produce an error message that names the actual cause.

A two-ink print of square photo tiles on a conveyor belt reaching a gate with one exactly square slot. One tile passes cleanly through; two tiles of the wrong shape are tipped back and turned away.

Instagram shares a Meta app with Facebook, which makes it sound like a small addition once Facebook works. The login is. Everything after it is not, and the rules below are the ones that cost time because the error messages describe a symptom rather than a cause.

Images must be JPEG

A two-ink print of two file slabs on one platform. The left sits flush in its matching recess and carries a tick; the right hovers above its recess at a tilt and carries a cross.
A PNG uploads, passes every size check, and then fails at publish. Only JPEG fits.

Meta documents JPEG, and means it. A PNG uploads without complaint, passes every size and dimension check, and then fails when the post is published, with a message that does not mention the file format.

If you are building anything that posts to Instagram, reject non-JPEG images at the point the file is chosen, and say so. The person can export it again in ten seconds; discovering it at 3am is a lost post.

There is no text-only Instagram post

Not a short one, not an empty one. A post with no photo or video cannot be created at all. Every other major platform treats media as optional, so this is the difference most likely to be assumed away.

Two aspect ratios, and they do not overlap

A two-ink print of two empty picture frames on one base, the left wide and short and the right narrow and tall, with a stretch of empty ground between them and nothing bridging it.
Feed and Reels do not overlap. A file shaped for one is refused by the other.
Instagram aspect ratio windows, verified against Meta documentation August 2026
FormatAllowedAs a decimal
Feed photo4:5 to 1.91:10.80 – 1.91
Reel9:16 recommended0.5625

A 9:16 Reel is outside the window a feed photo must sit in. If your tool stores one aspect-ratio range per platform, it must either reject every Reel or accept portrait photos Instagram will refuse. There is no single range that is correct.

Caption limits that are not length

Instagram caption limits
RuleLimit
Caption length2,200 characters
Hashtags30
@ mentions20
Alt text1,000 characters, images only

A short caption fails on its thirty-first hashtag. If you count hashtags with a pattern like \w+, Malay and Chinese hashtags are missed entirely and the limit is never reached. Use a Unicode letter class instead.

Publishing is three steps, not one

A two-ink print of three platforms rising in height from left to right, each joined to the next by a short arrow, with one card standing upright on the tallest.
Create the container, wait for it to finish processing, then publish. Skipping the wait is the usual mistake.
  1. Create a media container pointing at a publicly reachable URL.
  2. Poll the container until its status_code reads FINISHED, because Instagram fetches and processes the file first.
  3. Publish the container.

Publishing before processing finishes fails. Not publishing at all looks like success and posts nothing. Note also that Instagram calls the field status_code while Threads calls the same idea status. Asking for the wrong one returns nothing and a polling loop waits out its entire budget.

One hundred posts a day

Instagram allows 100 API-published posts per rolling 24 hours per account. This is worth treating as permanent rather than retryable: a retry loop measured in minutes cannot clear a window measured in a day, and retrying only delays telling the person.

Written while building Duta, a social media scheduling tool. Everything above was observed first-hand and last checked on .