Instagram and Threads fetch media from your URL at publish time, so anything behind auth, an expired signed URL, or a private bucket fails the target. For X and LinkedIn, Chirio downloads the bytes and re-uploads them — same requirement.
Not hosting media is deliberate: storage, CDN, scanning and retention policy are their own
product, and you almost certainly already have somewhere to put a file.
Limits
The request-level cap is 10 media items, whatever the platform allows. On top of that:
Platform
Images
Video
Notes
Instagram
1–10, **JPEG only**
1 (single video = Reel)
Media is required; 2+ items publish as a carousel
Threads
0–10
video items allowed
Text-only posts are fine
X
up to 4
1
One video **or** up to 4 images, never both
LinkedIn
up to 10
1
2+ images publish as a multi-image post; one video **or** images
Violating any of these rejects the whole request with 400 invalid_post before anything
publishes, naming the platform and the rule.
Describing the media
A media item carries more than a type and a URL. These describe the media itself, so they sit
on the item rather than the post — which is what lets each image in a carousel have its own
alt text.
A platform with no equivalent leaves these alone rather than refusing the post — that way a
field is not rejected today and accepted the week a platform's API gains it. The exceptions
are cases where the platform does support the idea by another route, and silence would
mislead: LinkedIn refuses thumbOffsetMs pointing at coverUrl, and Instagram refuses a
coverUrl inside a carousel pointing at thumbOffsetMs.Cover images are worth getting right rather than getting accepted. Instagram wants JPEG,
8 MB or less, sRGB, and 9:16 — an off-ratio cover is not rejected, it is cropped to the middle
rectangle, so anything near an edge is lost. See .
Video does not publish instantly anywhere. The platform processes the file first, and Chirio
waits for it — up to roughly four minutes per item. A video-heavy carousel can therefore take
several minutes in a single call.
Two consequences:
Set a generous client timeout, and always send an Idempotency-Key so a timeout on your
side is safe to retry. See Idempotency.
A slow item can time out and fail that target with media_processing_timeout. Retry just
that target.
Or schedule it: a publishAt post returns immediately and the dispatcher does the waiting.
Ordering
mediaItems order is preserved. For carousels it is the slide order the reader sees — see
Carousels.
Practical checklist
Serve media over HTTPS from a stable, public URL.
Keep the URL alive until the call returns — for video, that can be several minutes.
Use JPEG for Instagram images; PNG and WebP are refused.
Do not mix a video and images in one X or LinkedIn post.
Set a coverUrl on video rather than accepting whatever first frame the platform picks.
Give images an altText — it costs nothing and the people who need it cannot work around
its absence.
If media uploads fail on X while text posts work, the connection predates X media
permission — reconnect the account.