Skip to content

Instagram API

Schedule and automate Instagram posts with Zernio API - Feed, Stories, Reels, Carousels, collaborators, and user tags

Quick Reference

PropertyValue
Character limit2,200 (caption)
Images per post1 (feed), 10 (carousel)
Videos per post1
Image formatsJPEG, PNG
Image max size8 MB (auto-compressed)
Video formatsMP4, MOV
Video max size300 MB (feed/reels), 100 MB (stories)
Video max duration90 sec (reels), 60 min (feed), 60 sec (story)
Post typesFeed, Carousel, Story, Reel
SchedulingYes
Inbox (DMs)Yes
Inbox (Comments)Yes (reply-only)
AnalyticsYes

Before You Start

Instagram requires a Business or Creator account. Personal accounts cannot post via API.

Google Drive, Dropbox, OneDrive, and iCloud links do not work as media URLs. These services return HTML pages, not media files. Instagram's servers cannot fetch media from them. Use direct CDN URLs or upload via Zernio's media endpoint.

Additional requirements:

  • Media is required for all posts (no text-only)
  • 100 posts per 24-hour rolling window (all content types combined)
  • First 125 characters of caption are visible before the "more" fold

Quick Start

Post an image to Instagram in under 60 seconds:

typescript
const { post } = await zernio.posts.createPost({
  content: 'Check out this photo!',
  mediaItems: [
    { type: 'image', url: 'https://cdn.example.com/photo.jpg' }
  ],
  platforms: [
    { platform: 'instagram', accountId: 'YOUR_ACCOUNT_ID' }
  ],
  publishNow: true
});
console.log('Posted to Instagram!', post._id);
python
result = client.posts.create_post(
    content="Check out this photo!",
    media_items=[
        {"type": "image", "url": "https://cdn.example.com/photo.jpg"}
    ],
    platforms=[
        {"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    publish_now=True
)
post = result.post
print(f"Posted to Instagram! {post['_id']}")
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Check out this photo!",
    "mediaItems": [
      {"type": "image", "url": "https://cdn.example.com/photo.jpg"}
    ],
    "platforms": [
      {"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    "publishNow": true
  }'

Content Types

Feed Post

A single image or video in the main feed. Best aspect ratio is 4:5 (portrait), but 1:1 (square) and 1.91:1 (landscape) are also supported. No contentType field is needed -- feed is the default.

typescript
const { post } = await zernio.posts.createPost({
  content: 'Beautiful sunset today #photography',
  mediaItems: [
    { type: 'image', url: 'https://cdn.example.com/sunset.jpg' }
  ],
  platforms: [
    { platform: 'instagram', accountId: 'YOUR_ACCOUNT_ID' }
  ],
  publishNow: true
});
console.log('Feed post created!', post._id);
python
result = client.posts.create_post(
    content="Beautiful sunset today #photography",
    media_items=[
        {"type": "image", "url": "https://cdn.example.com/sunset.jpg"}
    ],
    platforms=[
        {"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    publish_now=True
)
post = result.post
print(f"Feed post created! {post['_id']}")
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Beautiful sunset today #photography",
    "mediaItems": [
      {"type": "image", "url": "https://cdn.example.com/sunset.jpg"}
    ],
    "platforms": [
      {"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    "publishNow": true
  }'

Up to 10 mixed image/video items. All items should share the same aspect ratio -- the first item determines the ratio for the entire carousel.

typescript
const { post } = await zernio.posts.createPost({
  content: 'Trip highlights from last weekend',
  mediaItems: [
    { type: 'image', url: 'https://cdn.example.com/photo1.jpg' },
    { type: 'image', url: 'https://cdn.example.com/photo2.jpg' },
    { type: 'video', url: 'https://cdn.example.com/clip.mp4' },
    { type: 'image', url: 'https://cdn.example.com/photo3.jpg' }
  ],
  platforms: [
    { platform: 'instagram', accountId: 'YOUR_ACCOUNT_ID' }
  ],
  publishNow: true
});
console.log('Carousel posted!', post._id);
python
result = client.posts.create_post(
    content="Trip highlights from last weekend",
    media_items=[
        {"type": "image", "url": "https://cdn.example.com/photo1.jpg"},
        {"type": "image", "url": "https://cdn.example.com/photo2.jpg"},
        {"type": "video", "url": "https://cdn.example.com/clip.mp4"},
        {"type": "image", "url": "https://cdn.example.com/photo3.jpg"}
    ],
    platforms=[
        {"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    publish_now=True
)
post = result.post
print(f"Carousel posted! {post['_id']}")
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Trip highlights from last weekend",
    "mediaItems": [
      {"type": "image", "url": "https://cdn.example.com/photo1.jpg"},
      {"type": "image", "url": "https://cdn.example.com/photo2.jpg"},
      {"type": "video", "url": "https://cdn.example.com/clip.mp4"},
      {"type": "image", "url": "https://cdn.example.com/photo3.jpg"}
    ],
    "platforms": [
      {"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    "publishNow": true
  }'

Story

Set contentType: "story" to publish to Stories. Stories disappear after 24 hours, text captions are not displayed, and link stickers are not available via the API (this is a limitation of Instagram's Graph API, not Zernio).

typescript
const { post } = await zernio.posts.createPost({
  mediaItems: [
    { type: 'image', url: 'https://cdn.example.com/story.jpg' }
  ],
  platforms: [{
    platform: 'instagram',
    accountId: 'YOUR_ACCOUNT_ID',
    platformSpecificData: {
      contentType: 'story'
    }
  }],
  publishNow: true
});
console.log('Story posted!', post._id);
python
result = client.posts.create_post(
    media_items=[
        {"type": "image", "url": "https://cdn.example.com/story.jpg"}
    ],
    platforms=[{
        "platform": "instagram",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "contentType": "story"
        }
    }],
    publish_now=True
)
post = result.post
print(f"Story posted! {post['_id']}")
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mediaItems": [
      {"type": "image", "url": "https://cdn.example.com/story.jpg"}
    ],
    "platforms": [{
      "platform": "instagram",
      "accountId": "YOUR_ACCOUNT_ID",
      "platformSpecificData": {
        "contentType": "story"
      }
    }],
    "publishNow": true
  }'

Reel

Set contentType: "reels" to publish a Reel, or let Zernio auto-detect it from vertical 9:16 video under 90 seconds. Reels must be vertical (9:16) and no longer than 90 seconds.

typescript
const { post } = await zernio.posts.createPost({
  content: 'New tutorial is up!',
  mediaItems: [
    { type: 'video', url: 'https://cdn.example.com/reel.mp4' }
  ],
  platforms: [{
    platform: 'instagram',
    accountId: 'YOUR_ACCOUNT_ID',
    platformSpecificData: {
      contentType: 'reels',
      shareToFeed: true
    }
  }],
  publishNow: true
});
console.log('Reel posted!', post._id);
python
result = client.posts.create_post(
    content="New tutorial is up!",
    media_items=[
        {"type": "video", "url": "https://cdn.example.com/reel.mp4"}
    ],
    platforms=[{
        "platform": "instagram",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "contentType": "reels",
            "shareToFeed": True
        }
    }],
    publish_now=True
)
post = result.post
print(f"Reel posted! {post['_id']}")
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "New tutorial is up!",
    "mediaItems": [
      {"type": "video", "url": "https://cdn.example.com/reel.mp4"}
    ],
    "platforms": [{
      "platform": "instagram",
      "accountId": "YOUR_ACCOUNT_ID",
      "platformSpecificData": {
        "contentType": "reels",
        "shareToFeed": true
      }
    }],
    "publishNow": true
  }'

Media Requirements

Images

PropertyFeed PostStoryCarousel
Max images1110
FormatsJPEG, PNGJPEG, PNGJPEG, PNG
Max file size8 MB8 MB8 MB 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 0.8 (4:5) and 1.91 (1.91:1). Images outside that range must be posted as Stories or Reels.

Videos

PropertyFeedReelStory
FormatsMP4, MOVMP4, MOVMP4, MOV
Max file size300 MB300 MB100 MB
Max duration60 min90 sec60 sec
Min duration3 sec3 sec3 sec
Aspect ratio4:5 to 1.91:19:169:16
Resolution1080 px wide1080 x 1920 px1080 x 1920 px
CodecH.264H.264H.264
Frame rate30 fps30 fps30 fps

Oversized media is auto-compressed. Images above 8 MB, videos above 300 MB (feed/reels) or 100 MB (stories) are compressed automatically. Original files are preserved.

Platform-Specific Fields

All fields go inside platformSpecificData on the Instagram platform entry.

FieldTypeDefaultDescription
contentType"story" | "reels"(feed)Omit for regular feed post. Set to "story" for Stories or "reels" for Reels.
shareToFeedbooleantrueReel-specific. Set to false to show the Reel in the Reels tab only, not the main feed.
collaboratorsArray<string>--Up to 3 usernames. Must be public Business/Creator accounts. Does not work with Stories.
userTagsArray<{username, x, y, mediaIndex?}>--Tag users in images (not videos). Coordinates are 0.0 to 1.0. mediaIndex targets a specific carousel slide (0-based, defaults to 0).
trialParams{graduationStrategy}--Trial Reels, shown only to non-followers. graduationStrategy is "MANUAL" or "SS_PERFORMANCE" (auto-graduate if it performs well).
thumbOffsetnumber (ms)0Millisecond offset from video start to use as thumbnail. Ignored if instagramThumbnail is set.
instagramThumbnailstring (URL)--Custom thumbnail for Reels. JPEG or PNG, recommended 1080 x 1920 px. Takes priority over thumbOffset.
audioNamestring--Custom audio name for Reels (replaces "Original Audio"). Can only be set at creation.
firstCommentstring--Auto-posted as the first comment. Works with feed posts and carousels, not Stories. Useful for links since captions do not have clickable links.

Media URL Requirements

These do not work as media URLs:

  • Google Drive -- returns an HTML download page, not the file
  • Dropbox -- returns an HTML preview page
  • OneDrive / SharePoint -- returns HTML
  • iCloud -- returns HTML

Test your URL in an incognito browser window. If you see a webpage instead of the raw image or video, it will not work.

Media URLs must be:

  • Publicly accessible (no authentication required)
  • Returning actual media bytes with the correct Content-Type header
  • Not behind redirects that resolve to HTML pages
  • Hosted on a fast, reliable CDN

Supabase URLs: Zernio auto-proxies Supabase storage URLs, so they work without additional configuration.

Analytics

> Included — Analytics is bundled with every paid account on the Usage plan.

Available metrics via the Analytics API:

MetricAvailable
Impressions
Reach
Likes
Comments
Shares
Saves
Views

Instagram also provides dedicated analytics endpoints for deeper insights:

  • Account Insights -- Account-level reach, views, accounts engaged, total interactions, follows_and_unfollows, profile links taps. Business or Creator accounts only. Note: only reach supports metricType=time_series; all other metrics (including follows_and_unfollows) are total_value only (Instagram API limitation).
  • Follower History -- Daily running follower count time series plus followers_gained and followers_lost deltas. Served from Zernio's daily snapshotter since Instagram removed follower_count from its /insights endpoint in Graph API v22+ and never exposed a historical daily series.
  • Demographics -- Audience breakdowns by age, city, country, or gender. Requires at least 100 followers.
typescript
const analytics = await zernio.analytics.getAnalytics({
  platform: 'instagram',
  fromDate: '2024-01-01',
  toDate: '2024-01-31'
});
console.log(analytics.posts);
python
analytics = client.analytics.get_analytics(
    platform="instagram",
    from_date="2024-01-01",
    to_date="2024-01-31"
)
print(analytics["posts"])
bash
curl "https://zernio.com/api/v1/analytics?platform=instagram&fromDate=2024-01-01&toDate=2024-01-31" \
  -H "Authorization: Bearer YOUR_API_KEY"

What You Can't Do

These features are not available through Instagram's API:

  • Add music to Reels
  • Use story stickers (polls, questions, links, countdowns)
  • Add location tags
  • Go Live
  • Create Guides
  • Apply filters
  • Tag products
  • Post to personal accounts (Business or Creator only)
  • Create top-level comments (reply-only through the API)

Common Errors

Instagram has a 10.2% failure rate across Zernio's platform (35,634 failures out of 348,438 attempts). Here are the most frequent errors and how to fix them:

ErrorMeaningFix
"Cannot process video from this URL. Instagram cannot fetch videos from Google Drive, Dropbox, or OneDrive."A cloud storage sharing link was used instead of a direct media URLUse a direct CDN URL. Test in an incognito window -- if you see a webpage, it will not work.
"You have reached the maximum of 100 posts per day."Instagram's hard 24-hour rolling limitReduce posting volume. This limit includes all content types (feed, stories, reels, carousels).
"Instagram blocked your request."Automation detection triggeredReduce posting frequency, vary content. Wait before retrying.
"Duplicate content detected."Identical content was already published recentlyModify the caption or media before retrying.
"Media fetch failed, retrying... (failed after 3 attempts)"Zernio could not download media from the provided URLVerify the URL is publicly accessible and returns actual media bytes, not an HTML page.
"Instagram access token expired."The OAuth token for this account has expiredReconnect the account. Subscribe to the account.disconnected webhook to catch this proactively.

Inbox

> Included — Inbox (DMs, comments, reviews) is bundled with every paid account on the Usage plan.

Instagram supports DMs and comments with some limitations.

Direct Messages

FeatureSupported
List conversations
Fetch messages
Send text messages
Send attachments✅ (images, videos, audio via URL)
Quick replies✅ (up to 13, Meta quick_replies)
Buttons✅ (up to 3, generic template)
Carousels✅ (generic template, up to 10 elements)
Message tags✅ (HUMAN_AGENT only)
Archive/unarchive

Attachment limits: 8 MB images, 25 MB video/audio. Attachments are automatically uploaded to temp storage and sent as URLs.

Message tags: Use messageTag: "HUMAN_AGENT" with messagingType: "MESSAGE_TAG" to send messages outside the 24-hour messaging window.

Instagram Profile Data

Instagram conversations include an optional instagramProfile object on participants and webhook senders, useful for routing and automation:

FieldTypeDescription
isFollowerbooleanWhether the participant follows your business account
isFollowingbooleanWhether your business account follows the participant
followerCountintegerThe participant's follower count
isVerifiedbooleanWhether the participant is a verified Instagram user
fetchedAtdatetimeWhen this data was last fetched (conversations only)

Available in:

  • GET /v1/inbox/conversations and GET /v1/inbox/conversations/{id} - on each participant
  • message.received webhook - on message.sender

Ice Breakers

Manage ice breaker prompts shown when users start a new Instagram DM conversation. Max 4 ice breakers, question max 80 characters.

See Account Settings for the GET/PUT/DELETE /v1/accounts/{accountId}/instagram-ice-breakers endpoints.

Comments

FeatureSupported
List comments on posts
Post new top-level comment❌ (reply-only)
Reply to comments
Delete comments
Like comments❌ (deprecated since 2018)
Hide/unhide comments
Send private reply (DM after comment)✅ (text + up to 13 quick replies OR 1-3 inline buttons, 7-day window, one per comment)

Webhooks

Instagram emits the full set of message lifecycle webhooks:

EventWhen it fires
message.receivedNew incoming DM
message.sentOutgoing DM is sent
message.editedThe sender edits a previously-sent message
message.deletedThe sender unsends a message
message.readThe customer reads an outgoing DM

Messages are stored locally via webhooks. See the Webhooks page for payload details.

message.deleted note: the payload retains the original text and attachments so API consumers can access pre-delete content for moderation or compliance use cases. The Zernio dashboard UI hides that content.

Limitations

  • Reply-only comments - Cannot post new top-level comments, only replies to existing comments
  • No comment likes - Liking comments was deprecated in 2018

See Messages and Comments API Reference for endpoint details.

  • Connect Instagram Account - OAuth flow via Facebook Business
  • Create Post - Post creation and scheduling
  • Upload Media - Image and video uploads
  • Analytics - Post performance metrics
  • Messages and Comments - Inbox API
  • Account Settings - Ice breakers configuration