RelayAPI

Instagram API

Schedule and automate Instagram posts with RelayAPI — feed posts, carousels, stories, reels, collaborators, and user tags.

Quick Reference

PropertyValue
Platform keyinstagram
Auth methodOAuth 2.0 (via Facebook Business)
Character limit2,200 (caption)
Images per post1 (feed), 10 (carousel)
Videos per post1
Image formatsJPEG, PNG
Image max size8 MiB
Video formatsMP4, MOV
Video max size100 MiB
Video max duration90 sec (reels), 60 min (feed), 60 sec (story)
Post typesFeed, Carousel, Story, Reel
SchedulingYes
AnalyticsYes (impressions, reach, likes, comments, shares, saves, views)

Client optionsTypeScript · Python REST/OpenAPI · Go · Java · REST API

Before You Start

Instagram requires a Business or Creator account — personal accounts cannot post via the API. Media is required for all Instagram posts; there are no text-only posts. Google Drive, Dropbox, and OneDrive URLs do not work as media sources because they return HTML pages, not raw media bytes. Always use direct CDN URLs. Instagram enforces a hard limit of 100 posts per 24-hour rolling window across all content types.

Quick Start

Post a photo to your Instagram feed:

import Relay from '@relayapi/sdk';
const client = new Relay();

const post = await client.posts.create({
  content: 'Beautiful sunset today #photography',
  targets: ['instagram'],
  media: [
    { url: 'https://cdn.example.com/sunset.jpg', type: 'image' },
  ],
  scheduled_at: 'now',
});

console.log(post.id); // post_abc123

Content Types

Feed Post (Single Image)

A single image post on the Instagram feed. Only the first 125 characters are visible before the "more" fold.

const post = await client.posts.create({
  content: 'Beautiful sunset today #photography',
  targets: ['instagram'],
  media: [
    { url: 'https://cdn.example.com/sunset.jpg', type: 'image' },
  ],
  scheduled_at: 'now',
});

Mix images and videos in a swipeable carousel. The first item determines the aspect ratio for all items.

const post = await client.posts.create({
  content: 'Trip highlights from last weekend',
  targets: ['instagram'],
  media: [
    { url: 'https://cdn.example.com/photo1.jpg', type: 'image' },
    { url: 'https://cdn.example.com/photo2.jpg', type: 'image' },
    { url: 'https://cdn.example.com/clip.mp4', type: 'video' },
    { url: 'https://cdn.example.com/photo3.jpg', type: 'image' },
  ],
  scheduled_at: 'now',
});

Story

Stories are ephemeral (24 hours). Text captions are not displayed on stories. Link stickers are not available via the API.

const post = await client.posts.create({
  targets: ['instagram'],
  media: [
    { url: 'https://cdn.example.com/story.jpg', type: 'image' },
  ],
  scheduled_at: 'now',
  target_options: {
    instagram: {
      content_type: 'story',
    },
  },
});

Reel

Vertical video (9:16), max 90 seconds. Reels appear on the Reels tab and optionally on your profile feed.

const post = await client.posts.create({
  content: 'New tutorial!',
  targets: ['instagram'],
  media: [
    { url: 'https://cdn.example.com/reel.mp4', type: 'video' },
  ],
  scheduled_at: 'now',
  target_options: {
    instagram: {
      content_type: 'reels',
      share_to_feed: true,
      first_comment: 'Link in bio!',
    },
  },
});

Trial Reel with a custom cover

Trial Reels and cover selectors apply only when content_type is reels. trial_params.graduation_strategy is either MANUAL or SS_PERFORMANCE. Choose exactly one cover source: a public cover_url, a ready Relay cover_media_id, a ready generated cover_variant_id, or thumb_offset.

const post = await client.posts.create({
  content: 'Testing this Reel with non-followers first',
  targets: ['instagram'],
  scheduled_at: 'now',
  media: [{ url: reelURL, type: 'video' }],
  target_options: {
    instagram: {
      content_type: 'reels',
      share_to_feed: false,
      cover_variant_id: 'mder_ready_cover',
      trial_params: { graduation_strategy: 'SS_PERFORMANCE' },
    },
  },
});

Relay IDs remain stable in a scheduled post. At publish time, RelayAPI verifies that the media/variant belongs to the same organization and authorized workspace, is ready and unexpired, and then generates the short-lived provider URL in memory. See Media Uploads for creating a cover variant from a video.

Post with Collaborators and User Tags

Tag collaborators (up to 3) and users in images. Collaborators receive an invite to co-author the post.

const post = await client.posts.create({
  content: 'Collab post with our partners!',
  targets: ['instagram'],
  media: [
    { url: 'https://cdn.example.com/photo.jpg', type: 'image' },
  ],
  scheduled_at: 'now',
  target_options: {
    instagram: {
      collaborators: ['brandpartner', 'creator123'],
      user_tags: [
        { username: 'friend', x: 0.5, y: 0.5 },
      ],
    },
  },
});

Post with First Comment

Automatically post a first comment after publishing. Useful for hashtag blocks or calls to action.

const post = await client.posts.create({
  content: 'New product launch!',
  targets: ['instagram'],
  media: [
    { url: 'https://cdn.example.com/product.jpg', type: 'image' },
  ],
  scheduled_at: 'now',
  target_options: {
    instagram: {
      first_comment: '#newproduct #launch #startup #tech #innovation',
    },
  },
});

First comments work on feed posts and carousels only. They are not supported on stories or reels.

Media Requirements

Images

PropertyFeedStoryCarousel
Max images1110
FormatsJPEG, PNGJPEG, PNGJPEG, PNG
Max file size8 MiB8 MiB8 MiB each
Recommended1080 x 1350 px1080 x 1920 px1080 x 1080 px

Aspect Ratios

OrientationRatioDimensionsNotes
Portrait4:51080 x 1350 pxBest engagement for feed posts
Square1:11080 x 1080 pxStandard feed and carousel
Landscape1.91:11080 x 566 pxWidest allowed for feed
Vertical9:161080 x 1920 pxStories and Reels only

Feed posts accept aspect ratios between 4:5 (0.8) and 1.91:1. Outside that range, the content must be a Story or Reel.

Videos

PropertyFeedReelStory
FormatsMP4, MOVMP4, MOVMP4, MOV
Max size100 MiB100 MiB100 MiB
Max duration60 min90 sec60 sec
Min duration3 sec3 sec3 sec
Aspect ratio4:5 to 1.91:19:169:16
Recommended1080px wide1080 x 1920 px1080 x 1920 px
CodecH.264H.264H.264

target_options Fields

All fields go inside target_options.instagram on your post request.

FieldTypeDescription
contentstringOverride caption for Instagram specifically
mediaobject[]Override media for Instagram specifically
content_typestring"story" or "reels" (default: feed post)
share_to_feedbooleanReels: also show on profile feed grid (default: true)
collaboratorsstring[]Up to 3 usernames to invite as collaborators (feed and reels only)
user_tagsobject[]{username, x, y, media_index?} — tag users in images. Coordinates are 0-1 range.
first_commentstringAuto-posted first comment (feed and carousels only)
thumb_offsetnumberMillisecond offset for a Reel cover; mutually exclusive with the three other cover selectors
cover_urlURLPublic image URL used as the Reel cover
cover_media_idmed_...Ready Relay media resolved to a fresh cover URL at publish time
cover_variant_idmder_...Ready Relay-generated cover variant resolved at publish time
trial_paramsobjectTrial Reel graduation strategy: MANUAL or SS_PERFORMANCE

Common Errors

ErrorCauseFix
Cannot process video from URLCloud storage sharing link used (Drive, Dropbox)Use a direct CDN URL that returns raw media bytes, not an HTML page
100 posts per day limitInstagram hard 24-hour rolling limit reachedReduce posting volume. This limit includes all content types.
Instagram blocked requestAutomation detection triggeredReduce frequency and vary content between posts
Duplicate contentIdentical content posted recentlyModify caption or swap media files
Media fetch failedMedia URL is inaccessible or returns HTMLVerify URL returns actual media bytes with correct Content-Type header
Token expiredOAuth token expired or revokedReconnect the account via the dashboard or Connect API

Known Quirks

  • Media is required for all posts — Instagram does not support text-only posts.
  • Business or Creator account required — personal accounts cannot post via the API.
  • First 125 characters visible before the "more" fold. Front-load your most important message.
  • Google Drive, Dropbox, OneDrive URLs do not work — they return HTML download pages, not media bytes. Always use direct CDN URLs.
  • Stories do not display text captions — text is ignored. Link stickers are not available via the API.
  • Carousel first item determines aspect ratio for all subsequent items in the carousel.
  • Images over 8 MiB are rejected by RelayAPI preflight — resize them before publishing.
  • 100 posts per 24-hour rolling limit includes feed posts, carousels, stories, and reels combined.
  • User tags only work on images — not videos or stories. For carousels, use media_index to target specific slides.

Social actions

RelayAPI can hide/unhide Instagram comments, send a provider read receipt for a persisted inbound Instagram Messaging message, and list normalized story/post mentions already received through signed webhooks. It does not edit a published Instagram media object or comment. Reauthorize an older connection if it lacks instagram_manage_comments or instagram_manage_messages. See Published Edits and Social Actions.

Automations

The automations engine supports Instagram as a Tier 1 channel for inbound conversational events, message flows, live relationship checks, and Meta profile bindings.

Entrypoints

KindFires onNotes
dm_receivedOrdinary inbound DMOptional keywords, match mode, case sensitivity, and first-message-only filter
comment_createdNew post or reel commentOptional post IDs, keywords, and reply inclusion
story_replyReply to a storyOptional story IDs and keywords
story_mentionAccount mentioned in a storyCan also resume a wait_event node
live_commentComment during a LiveTime-sensitive
ad_clickClick-to-message ad eventOptional ad IDs
ref_link_clickInstagram referral linkMatch selected referral IDs
share_to_dmContent shared into DMInstagram-only

Instagram automations also support channel-independent schedule, field_changed, tag_applied, tag_removed, conversion_event, and webhook_inbound entrypoints. Button, quick-reply, ice-breaker, and menu postbacks resume the exact waiting run or route through the configured binding; they are not separate generic trigger kinds.

Instagram does not expose a public follower webhook. The follow_to_dm preset therefore starts on a contact's first inbound DM and performs a live is_user_follow_business check before sending the welcome message. It never invents a follow event or sends an unsolicited follower DM.

Messages and actions

Outbound content uses the channel-neutral message node. Its ordered blocks can contain text, images, videos, cards, galleries, and short in-message delays. Instagram supports branch buttons and up to 13 quick replies; audio and file blocks are rejected by validation or skipped with an explicit delivery result. See the Instagram Messaging API.

SurfaceInstagram behavior
message nodeSends rendered blocks through the exact triggering account
Comment private replyUsed by comment_to_dm and follower_growth; provider shape is one button-free text block
reply_to_comment actionPosts a public reply to the triggering comment
social_profile_check nodeLive Instagram-only follow relationship branch
Merge tagscontact.name/email/phone, context.* (or state.*), and bare contact-field shorthand

Profile bindings

Instagram exposes two provider-synchronized automation bindings:

  • main_menu — one or more postback or URL items.
  • ice_breaker — up to four starter questions with postback payloads.

Creating or updating either binding records desired state and queues provider synchronization. The dashboard reports pending, synced, or failed; deleting waits for Meta's delete acknowledgement before removing the local row. Runtime-only default_reply and welcome_message bindings are also available and do not modify the Instagram profile.

Quick example: comment → DM

await client.automations.create({
  name: 'Spring launch comment → DM',
  channel: 'instagram',
  template: {
    kind: 'comment_to_dm',
    config: {
      social_account_id: 'acc_instagram_xyz',
      post_ids: ['17895...'],
      keyword_filter: ['LINK', 'INFO'],
      dm_message: {
        blocks: [{ id: 'reply', type: 'text', text: 'Hey {{contact.name}}, here is the link you asked for.' }],
      },
      public_reply: 'Check your DMs! 📨',
      once_per_user: true,
      daily_cap: 500,
    },
  },
});

Rate limits + constraints

  • Messaging admission: reactive sends require explicit automation consent or a bounded inbound customer-service conversation. A suppression or opt-out veto always wins.
  • Daily caps: quick-start presets accept daily_cap; admission is enforced atomically per entrypoint and UTC day.
  • Account scope: a preset's social_account_id must be active and match the automation's organization, workspace, and Instagram channel.
  • Comment replies: Meta controls private/public reply eligibility and volume. Wire the message node's error port when you need a fallback.
  • Provider sync: menus and ice breakers are asynchronous. Treat local pending state as not yet live on Instagram.

Found something wrong? Help us improve this page.

On this page