Platforms
Publish feed posts, carousels, Reels and Stories to Instagram professional accounts.
Overview
The instagram platform publishes to Instagram professional (Business or Creator) accounts. Personal accounts cannot publish through the API. You can post:
- A single image (JPEG) to the feed
- A single video, published as a Reel
- A carousel of 2–10 images and/or videos
- A Reel (with optional cover, thumbnail frame, trial-reel mode, and "share to feed" toggle)
- A Story (exactly one image or video)
- Up to 3 collaborators on feed posts, carousels and Reels
Instagram does not support text-only posts: every post needs at least one image or video.
Connect the account
In Integrations, choose Instagram. There are two ways to connect, and both end up as the same Instagram account in Relaycast (reconnecting through either one updates the existing account):
| Connect option | When to use it | Requirements |
|---|---|---|
| Log in with Instagram (default) | Most accounts. No Facebook Page needed. | Instagram Business or Creator account |
| Log in with Facebook | The Instagram account is linked to a Facebook Page you manage | Instagram professional account linked to a Facebook Page |
Both options use the redirect URI https://social-publish-kappa.vercel.app/api/integrations/instagram/callback (override with INSTAGRAM_REDIRECT_URI).
Instagram Login setup (self-hosted)
- In your Meta app, add the Instagram product and open API setup with Instagram login.
- Use the Instagram app ID and secret shown there (not the Facebook app ID) for
INSTAGRAM_APP_IDandINSTAGRAM_APP_SECRET. - Under Set up Instagram business login → Business login settings, add the redirect URI above.
Scopes requested: instagram_business_basic, instagram_business_content_publish, instagram_business_manage_messages, instagram_business_manage_comments, instagram_business_manage_insights. Tokens are long-lived (60 days) and refreshed automatically before they expire.
Facebook Login setup (self-hosted)
- Use the Facebook app (
FACEBOOK_APP_ID,FACEBOOK_APP_SECRET, the same one as Facebook Pages) with the Instagram use case Manage messages and content on Instagram. - Add
https://social-publish-kappa.vercel.app/api/integrations/instagram/callbackto Facebook Login → Settings → Valid OAuth Redirect URIs.
Always requested: instagram_basic, instagram_content_publish, pages_show_list, pages_read_engagement. Optional features request more permissions only when their flag is set, because Meta rejects the whole login if a requested permission isn't added to the app:
| Flag | Adds | Enables |
|---|---|---|
INSTAGRAM_FB_ENABLE_COMMENTS=true | instagram_manage_comments | Comment replies and comment-to-DM automations |
INSTAGRAM_FB_ENABLE_MESSAGES=true | instagram_manage_messages, pages_manage_metadata | DM inbox |
INSTAGRAM_FB_ENABLE_INSIGHTS=true | instagram_manage_insights | Post insights |
FACEBOOK_ENABLE_BUSINESS_MANAGEMENT=true | business_management | Pages owned through a Business portfolio |
In development mode, only accounts with a role on the Meta app (and accepted Instagram testers) can connect. Publishing for other people needs Meta App Review for the permissions above.
Post types & media rules
Choose the post type with options.publishAs:
publishAs | Media | Result |
|---|---|---|
feed (default) | 1 image | Feed photo |
feed | 1 video | Reel, also shown in the feed |
feed | 2–10 images and/or videos | Carousel |
reel | Exactly 1 video | Reel (shareToFeed controls whether it also appears in the feed grid) |
story | Exactly 1 image or video | Story |
| Rule | Limit |
|---|---|
| Caption | 2,200 characters |
| Hashtags | 30 per caption |
| @mentions | 20 per caption |
| Image format | JPEG only (URLs ending in .png, .gif, .webp, .heic, .avif, .bmp, .tiff are rejected) |
| Alt text | Single-image feed posts only, up to 1,000 characters |
| Collaborators | Up to 3; not on Stories |
| Publishing quota | Instagram's rolling 24-hour publishing limit (100 posts by default). When it is used up, the target is retried later. |
Media is fetched by Instagram from its URL, so it must be publicly reachable (media library files are served as signed URLs automatically). Size, duration and aspect-ratio checks are done by Instagram while it processes the media.
Options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
publishAs | feed \ | reel \ | story | No |
shareToFeed | boolean | No | true | Also share reel to feed Only when publishAs is reel. |
collaborators | string[] | No | Collaborators. Invite accounts to co-author this post. They must accept the invitation in Instagram before it appears on their profile. Only when publishAs is feed or reel. | |
coverUrl | string (URL) | No | Reel cover image URL. Videos only. Public JPEG used as the cover in the Reels tab. Only when publishAs is feed or reel. | |
thumbOffset | number | No | Thumbnail frame (ms). Videos only. Millisecond offset of the frame used as the thumbnail. Ignored when a cover URL is set. Only when publishAs is feed or reel. | |
trialReel | off \ | MANUAL \ | SS_PERFORMANCE | No |
coverUrl and thumbOffset apply to videos only. trialReel requires publishAs: "reel". |
Examples
Reel with collaborators and a cover
curl -X POST https://social-publish-api.onrender.com/v1/posts \
-H "x-access-key: $RELAYCAST_API_KEY" \
-H "content-type: application/json" \
-d '{
"content": "Behind the scenes of our spring shoot #bts",
"media": [{ "url": "https://cdn.example.com/bts.mp4", "type": "video" }],
"targets": [{
"accountId": "1d7e3f52-8c4a-4b9e-a0f1-7e6d5c4b3a29",
"options": {
"publishAs": "reel",
"shareToFeed": true,
"collaborators": ["studio.north"],
"coverUrl": "https://cdn.example.com/bts-cover.jpg"
}
}],
"scheduledFor": "2026-11-03T17:30:00Z"
}'const res = await fetch("https://social-publish-api.onrender.com/v1/posts", {
method: "POST",
headers: {
"x-access-key": process.env.RELAYCAST_API_KEY!,
"content-type": "application/json",
},
body: JSON.stringify({
content: "Behind the scenes of our spring shoot #bts",
media: [{ url: "https://cdn.example.com/bts.mp4", type: "video" }],
targets: [
{
accountId: "1d7e3f52-8c4a-4b9e-a0f1-7e6d5c4b3a29",
options: {
publishAs: "reel",
shareToFeed: true,
collaborators: ["studio.north"],
coverUrl: "https://cdn.example.com/bts-cover.jpg",
},
},
],
scheduledFor: "2026-11-03T17:30:00Z",
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://social-publish-api.onrender.com/v1/posts",
headers={"x-access-key": os.environ["RELAYCAST_API_KEY"]},
json={
"content": "Behind the scenes of our spring shoot #bts",
"media": [{"url": "https://cdn.example.com/bts.mp4", "type": "video"}],
"targets": [{
"accountId": "1d7e3f52-8c4a-4b9e-a0f1-7e6d5c4b3a29",
"options": {
"publishAs": "reel",
"shareToFeed": True,
"collaborators": ["studio.north"],
"coverUrl": "https://cdn.example.com/bts-cover.jpg",
},
}],
"scheduledFor": "2026-11-03T17:30:00Z",
},
)
data = res.json()["data"]Carousel
{
"content": "Three looks from the new collection",
"media": [
{ "url": "https://cdn.example.com/look-1.jpg", "type": "image" },
{ "url": "https://cdn.example.com/look-2.jpg", "type": "image" },
{ "url": "https://cdn.example.com/look-3.mp4", "type": "video" }
],
"targets": [{ "accountId": "1d7e3f52-8c4a-4b9e-a0f1-7e6d5c4b3a29" }],
"publishNow": true
}Story
{
"content": "Story",
"media": [{ "url": "https://cdn.example.com/story.jpg", "type": "image" }],
"targets": [{ "accountId": "1d7e3f52-8c4a-4b9e-a0f1-7e6d5c4b3a29", "options": { "publishAs": "story" } }],
"publishNow": true
}The request still needs content (it is required for every post), but Stories have no caption.
Analytics & comments
- Analytics: likes and comment counts for every post, plus Instagram insights (views, reach, shares, saves and more, depending on the media type). With Facebook Login, insights need
INSTAGRAM_FB_ENABLE_INSIGHTS=true. - Inbox: Instagram DMs can be read and answered from the dashboard inbox.
- Comments: comment-to-DM automations and public comment replies. See Inbox & Comment Automation.
Accounts connected before a permission was added must reconnect to use the feature.
Limitations & common errors
| Error | Cause |
|---|---|
Instagram posts need at least one image or video | Text-only posts are not supported. |
A reel needs exactly one video | publishAs: "reel" with images or several items. |
A story needs exactly one image or video | Stories take a single item. |
A carousel allows 2-10 images and/or videos | Too many items for a carousel. |
Instagram only accepts JPEG images | Convert PNG/WebP/HEIC images to JPEG. |
Collaborators are not supported on stories | Remove collaborators for Stories. |
Trial reels require post type Reel | Set publishAs: "reel" to use trialReel. |
| Connect fails: only professional accounts | Switch the account to Business or Creator in the Instagram app. |
| Connect fails: no linked Instagram account (Facebook Login) | Link the Instagram account to a Facebook Page and reconnect. |
| Target retried: publishing limit reached | The account hit Instagram's 24-hour publishing quota. |
| Target fails with a media error | Instagram could not fetch or process the media (format, size, duration or aspect ratio). |
This page is also available as Markdown.