Instagram requirements
Account requirements
- The Instagram account must be a professional account — Business or Creator. Personal accounts cannot publish through the API, and Relay rejects them during the connect flow rather than letting you discover it at publish time.
- Relay connects through Instagram Login (the direct Instagram business login), so you don't need a linked Facebook Page.
- Two permissions are requested:
instagram_business_basic(read the profile) andinstagram_business_content_publish(create and publish media). - Accounts in some regions or with certain restrictions may be excluded by Meta from API publishing entirely — that surfaces as a Graph error on the first publish.
Connecting an account
Start the flow from Accounts → Connect Instagram. Relay exchanges the authorization code for a long-lived token, reads the profile, and stores:
| Stored | Why |
|---|---|
| access token | Encrypted at rest with AES-256-GCM. Required for every publish call; never returned by any API. |
| Instagram user ID | The Graph-scoped account ID every publish call is addressed to. |
| username, name, avatar | Shown in the dashboard and on targets. |
| follower & media counts | Refreshed once a day for the dashboard; never used for publishing. |
| token expiry | Drives the refresh job and the connection health badge. |
Token lifecycle
Long-lived Instagram tokens last 60 days and can be refreshed — but only while they're still alive. Relay runs a weekly refresh for every token expiring within 14 days, which in normal operation means a connection never lapses.
| Health | Meaning | Can publish? |
|---|---|---|
| connected | Token valid, more than 7 days left. | Yes |
| expiring | Under 7 days left and the refresh has not succeeded yet. | Yes — the token is valid until it actually expires. |
| reconnect_required | The token expired, was revoked, or Instagram rejected it (Graph error 190 / subcode 463). It cannot be refreshed. | No — reconnect the account. |
| disconnected | Removed from Relay, or revoked from the Instagram side via Meta’s deauthorize callback. | No |
disconnected. Meta's data-deletion request is honoured the same way — the account is deleted, or fully anonymized when post history prevents a hard delete.Post types
| Type | Media | Notes |
|---|---|---|
| feed | Exactly 1 JPEG image | Aspect ratio must be between 4:5 and 1.91:1. Relay checks this at upload. |
| carousel | 2–10 images or videos, mixed allowed | Instagram crops every slide to the first item’s ratio. Counts as one post against the quota. |
| reel | Exactly 1 video | Set share_to_feed: false to keep it off the profile grid; Instagram defaults to showing it. |
| story | 1 image or video | Instagram displays no caption on stories — a caption you send is stored on the post but not shown. Must be requested explicitly; never inferred. |
Omit the type and Relay infers it: one image is a feed post, one video is a reel, two or more items is a carousel. Stories are the exception — ask for them by name.
Media specifications
The left column is Instagram's published specification. The right is what Relay enforces before accepting an upload, so a file that passes here will not be rejected later for format reasons.
Images
| Enforced by Relay | |
|---|---|
| JPEG only — PNG, HEIC, MPO, and JPS are rejected | Yes, at upload, with an explicit message |
| 8 MB maximum | Yes |
| Aspect ratio 4:5 – 1.91:1 (feed and carousel) | Yes — parsed from the JPEG at upload |
| Width 320–1440 px (scaled outside that range) | No — Instagram rescales |
| sRGB colour space | No |
| 9:16 recommended for stories | No — stories accept any ratio |
Video (reels and stories)
| Property | Enforced by Relay | |
|---|---|---|
| Container | MP4 or MOV | Yes — image/jpeg, video/mp4, video/quicktime only |
| Video codec | H.264 or HEVC | No |
| Audio codec | AAC, ≤ 48 kHz, 1–2 channels, 128 kbps | No |
| Frame rate | 23–60 FPS | No |
| Resolution | ≤ 1920 px wide | No |
| Bitrate | VBR, ≤ 25 Mbps | No |
| Duration | Reels 3 s – 15 min; stories 3 – 60 s | No — Instagram rejects at processing time |
| File size | Reels ≤ 300 MB; stories ≤ 100 MB | 100 MB for all video — Relay's own cap, below Instagram's |
container_error with Instagram's own message.Captions
- 1–2,200 characters. Relay rejects longer captions before they reach Instagram.
- Hashtags and @mentions work inline; there is no markdown or rich text.
- Stories ignore the caption entirely.
Publishing quota
Instagram allows 100 API-published posts per account per rolling 24 hours. A carousel counts as one post regardless of how many items it holds. Relay checks the remaining quota before creating a media container.
At the ceiling, the post is not failed. The target keeps a quota_exceeded breadcrumb, the post stays in publishing, and the scheduler retries until a slot frees up — which happens automatically as older posts age out of the 24-hour window.
"error": {
"code": "quota_exceeded",
"message": "Instagram allows 100 API-published posts per 24h and this account is at the limit — Relay will keep retrying until a slot frees up"
}Timing & retries
Publishing is a two-step handshake with Instagram: create a media container, wait for it to finish processing, then publish it. Relay drives that from a scheduler tick.
| Behaviour | Value |
|---|---|
| Scheduler interval | Every minute |
| Posts claimed per tick | 10, oldest scheduled time first |
| Stalled-post reclaim | A post stuck in publishing for 2 minutes is picked up again |
| Processing poll budget | 30 ticks (~30 minutes) before a container is declared timed out |
| Transient error retries | 5 attempts for HTTP 429 / 5xx and Graph code 4, then the target fails |
| Container validity | 24 hours, per Instagram — after that it expires |
- Images normally publish within the tick that claims them — a few seconds after
schedule_at. - Video is transcoded by Instagram asynchronously. Expect a minute or several; the target sits in
processinguntil it's ready. - Carousels create one child container per item, wait for all of them to finish, then create the parent. A single bad item fails the whole carousel.
- Retries never duplicate a post: container IDs are persisted as they're created, so a reclaimed post resumes rather than starting over.
Failure reference
When a target fails, error.code tells you whose fault it was and whether to retry. Codes beginning graph_ are Instagram's own error codes, passed through with Instagram's message.
| Code | Cause | What to do |
|---|---|---|
| quota_exceeded | The account hit 100 API posts in 24 hours. | Nothing — Relay keeps retrying. Not a terminal failure. |
| auth_reconnect_required | The token expired or the user revoked access (Graph 190 / subcode 463). | Reconnect the account, then create the post again. |
| account_disconnected | The account was disconnected before the post ran. | Reconnect and recreate. |
| container_error | Instagram rejected the media while processing it — bad codec, unsupported duration, corrupt file. | Read the message, fix the file, upload again. |
| container_expired | The media container went unpublished for 24 hours. | Recreate the post. |
| child_error / child_expired | One carousel item failed or expired. | Fix that item and recreate the carousel. |
| processing_timeout | The container never finished processing within ~30 minutes. | Usually an oversized or unusual video — re-encode and retry. |
| published_unverified | The post went live but Relay lost the media ID mid-publish. | Nothing — the post is live. The permalink is missing from the timeline. |
| graph_<code> | A semantic rejection from Instagram, message included verbatim. | Read the message — it is the actionable part. |
| http_<status> | Instagram returned a non-JSON error. | Retry later; contact support if it persists. |
| internal | An unexpected error inside Relay. | Send the post ID to support@lantean.tech. |
Not supported
- PNG and other image formats. Instagram's publishing API takes JPEG only.
- Text-only posts. Every Instagram post needs at least one media item.
- Filters, shopping tags, and product tagging — not exposed by the publishing API.
- Editing or deleting a published post. Once it's live, use the Instagram app. Relay can only cancel posts that haven't published yet.
- Changing media or targets on an existing post. Cancel it and create a new one; only caption and schedule are editable.
- TikTok. The data model reserves the platform, but no TikTok publishing path is live.
For the request and response shapes behind all of this, see the REST API reference or the MCP tool reference.