Instagram API
Schedule and automate Instagram posts with Zernio API - Feed, Stories, Reels, Carousels, collaborators, and user tags
Quick Reference
| Property | Value |
|---|---|
| Character limit | 2,200 (caption) |
| Images per post | 1 (feed), 10 (carousel) |
| Videos per post | 1 |
| Image formats | JPEG, PNG |
| Image max size | 8 MB (auto-compressed) |
| Video formats | MP4, MOV |
| Video max size | 300 MB (feed/reels), 100 MB (stories) |
| Video max duration | 90 sec (reels), 60 min (feed), 60 sec (story) |
| Post types | Feed, Carousel, Story, Reel |
| Scheduling | Yes |
| Inbox (DMs) | Yes (add-on) |
| Inbox (Comments) | Yes (add-on, reply-only) |
| Analytics | Yes |
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:
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);result = client.posts.create(
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']}")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.
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);result = client.posts.create(
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']}")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
}'Carousel
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.
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);result = client.posts.create(
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']}")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).
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);result = client.posts.create(
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']}")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.
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);result = client.posts.create(
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']}")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
| Property | Feed Post | Story | Carousel |
|---|---|---|---|
| Max images | 1 | 1 | 10 |
| Formats | JPEG, PNG | JPEG, PNG | JPEG, PNG |
| Max file size | 8 MB | 8 MB | 8 MB each |
| Recommended | 1080 x 1350 px | 1080 x 1920 px | 1080 x 1080 px |
Aspect Ratios
| Orientation | Ratio | Dimensions | Notes |
|---|---|---|---|
| Portrait | 4:5 | 1080 x 1350 px | Best engagement for feed posts |
| Square | 1:1 | 1080 x 1080 px | Standard feed and carousel |
| Landscape | 1.91:1 | 1080 x 566 px | Widest allowed for feed |
| Vertical | 9:16 | 1080 x 1920 px | Stories 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
| Property | Feed | Reel | Story |
|---|---|---|---|
| Formats | MP4, MOV | MP4, MOV | MP4, MOV |
| Max file size | 300 MB | 300 MB | 100 MB |
| Max duration | 60 min | 90 sec | 60 sec |
| Min duration | 3 sec | 3 sec | 3 sec |
| Aspect ratio | 4:5 to 1.91:1 | 9:16 | 9:16 |
| Resolution | 1080 px wide | 1080 x 1920 px | 1080 x 1920 px |
| Codec | H.264 | H.264 | H.264 |
| Frame rate | 30 fps | 30 fps | 30 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.
| Field | Type | Default | Description |
|---|---|---|---|
contentType | "story" | "reels" | (feed) | Omit for regular feed post. Set to "story" for Stories or "reels" for Reels. |
shareToFeed | boolean | true | Reel-specific. Set to false to show the Reel in the Reels tab only, not the main feed. |
collaborators | Array<string> | -- | Up to 3 usernames. Must be public Business/Creator accounts. Does not work with Stories. |
userTags | Array<{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). |
thumbOffset | number (ms) | 0 | Millisecond offset from video start to use as thumbnail. Ignored if instagramThumbnail is set. |
instagramThumbnail | string (URL) | -- | Custom thumbnail for Reels. JPEG or PNG, recommended 1080 x 1920 px. Takes priority over thumbOffset. |
audioName | string | -- | Custom audio name for Reels (replaces "Original Audio"). Can only be set at creation. |
firstComment | string | -- | 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-Typeheader - 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
Requires Analytics add-on
Available metrics via the Analytics API:
| Metric | Available |
|---|---|
| Impressions | ✅ |
| Reach | ✅ |
| Likes | ✅ |
| Comments | ✅ |
| Shares | ✅ |
| Saves | ✅ |
| Views | ✅ |
const analytics = await zernio.analytics.getAnalytics({
platform: 'instagram',
fromDate: '2024-01-01',
toDate: '2024-01-31'
});
console.log(analytics.posts);analytics = client.analytics.get(
platform="instagram",
from_date="2024-01-01",
to_date="2024-01-31"
)
print(analytics["posts"])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
- Boost or promote posts
- 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:
| Error | Meaning | Fix |
|---|---|---|
| "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 URL | Use 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 limit | Reduce posting volume. This limit includes all content types (feed, stories, reels, carousels). |
| "Instagram blocked your request." | Automation detection triggered | Reduce posting frequency, vary content. Wait before retrying. |
| "Duplicate content detected." | Identical content was already published recently | Modify the caption or media before retrying. |
| "Media fetch failed, retrying... (failed after 3 attempts)" | Zernio could not download media from the provided URL | Verify 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 expired | Reconnect the account. Subscribe to the account.disconnected webhook to catch this proactively. |
Inbox
Requires Inbox add-on — Build: +$10/mo · Accelerate: +$50/unit · Unlimited: +$1,000/mo
Instagram supports DMs and comments with some limitations.
Direct Messages
| Feature | Supported |
|---|---|
| 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:
| Field | Type | Description |
|---|---|---|
isFollower | boolean | Whether the participant follows your business account |
isFollowing | boolean | Whether your business account follows the participant |
followerCount | integer | The participant's follower count |
isVerified | boolean | Whether the participant is a verified Instagram user |
fetchedAt | datetime | When this data was last fetched (conversations only) |
Available in:
GET /v1/inbox/conversationsandGET /v1/inbox/conversations/{id}- on each participantmessage.receivedwebhook - onmessage.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
| Feature | Supported |
|---|---|
| List comments on posts | ✅ |
| Post new top-level comment | ❌ (reply-only) |
| Reply to comments | ✅ |
| Delete comments | ✅ |
| Like comments | ❌ (deprecated since 2018) |
| Hide/unhide comments | ✅ |
Webhooks
Subscribe to message.received to get notified when new DMs arrive. Messages are stored locally via webhooks.
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.
Related Endpoints
- 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