Instagram API
Post to Instagram with Late API - Feed posts, Stories, Reels, and Carousels
Quick Start
Post to Instagram in under 60 seconds:
curl -X POST https://getlate.dev/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://example.com/photo.jpg"}
],
"platforms": [
{"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Check out this photo! 📸',
mediaItems: [
{ type: 'image', url: 'https://example.com/photo.jpg' }
],
platforms: [
{ platform: 'instagram', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
})
});
const { post } = await response.json();
console.log('Posted to Instagram!', post._id);import requests
response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': 'Check out this photo! 📸',
'mediaItems': [
{'type': 'image', 'url': 'https://example.com/photo.jpg'}
],
'platforms': [
{'platform': 'instagram', 'accountId': 'YOUR_ACCOUNT_ID'}
],
'publishNow': True
}
)
post = response.json()['post']
print(f"Posted to Instagram! {post['_id']}")Note: Instagram requires media for all posts. Text-only posts are not supported.
Content Types
Instagram supports multiple content types, each with different requirements:
| Type | Description | Media Required |
|---|---|---|
| Feed Post | Standard image/video post | Yes |
| Carousel | Multi-image/video post (up to 10) | Yes |
| Story | 24-hour ephemeral content | Yes |
| Reel | Short-form video (up to 90 sec) | Yes (video) |
Image Requirements
| 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 × 1350 px | 1080 × 1920 px | 1080 × 1080 px |
Aspect Ratio Requirements
This is critical for Instagram! Feed posts have strict aspect ratio requirements:
| Orientation | Ratio | Dimensions | Use Case |
|---|---|---|---|
| Portrait | 4:5 | 1080 × 1350 px | Best engagement |
| Square | 1:1 | 1080 × 1080 px | Standard |
| Landscape | 1.91:1 | 1080 × 566 px | Minimum |
Allowed range: 0.8 (4:5) to 1.91 (1.91:1)
✅ 4:5 (0.8) - Portrait
✅ 1:1 (1.0) - Square
✅ 1.91:1 (1.91) - Landscape
❌ 9:16 (0.56) - Too tall, use Story instead
❌ 16:9 (1.78) - Acceptable but croppedImportant: Images outside the 0.8-1.91 range (like 9:16 vertical videos) must be posted as Stories using
contentType: "story".
Video Requirements
Feed Videos / Reels
| Property | Requirement |
|---|---|
| Formats | MP4, MOV |
| Max File Size | 300 MB (auto-compressed if larger) |
| Max Duration | 90 seconds (Reels), 60 min (Feed) |
| Min Duration | 3 seconds |
| Aspect Ratio | 9:16 (Reels), 4:5 to 1.91:1 (Feed) |
| Resolution | 1080 × 1920 px (Reels) |
| Frame Rate | 30 fps recommended |
| Codec | H.264 |
Story Videos
| Property | Requirement |
|---|---|
| Max File Size | 100 MB (auto-compressed if larger) |
| Max Duration | 60 seconds |
| Aspect Ratio | 9:16 |
| Resolution | 1080 × 1920 px |
Carousel Posts
Create carousel posts with up to 10 items mixing images and videos:
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Check out these photos from my trip! 🌴",
"mediaItems": [
{"type": "image", "url": "https://example.com/photo1.jpg"},
{"type": "image", "url": "https://example.com/photo2.jpg"},
{"type": "video", "url": "https://example.com/video.mp4"},
{"type": "image", "url": "https://example.com/photo3.jpg"}
],
"platforms": [
{"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Check out these photos from my trip! 🌴',
mediaItems: [
{ type: 'image', url: 'https://example.com/photo1.jpg' },
{ type: 'image', url: 'https://example.com/photo2.jpg' },
{ type: 'video', url: 'https://example.com/video.mp4' },
{ type: 'image', url: 'https://example.com/photo3.jpg' }
],
platforms: [
{ platform: 'instagram', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': 'Check out these photos from my trip! 🌴',
'mediaItems': [
{'type': 'image', 'url': 'https://example.com/photo1.jpg'},
{'type': 'image', 'url': 'https://example.com/photo2.jpg'},
{'type': 'video', 'url': 'https://example.com/video.mp4'},
{'type': 'image', 'url': 'https://example.com/photo3.jpg'}
],
'platforms': [
{'platform': 'instagram', 'accountId': 'YOUR_ACCOUNT_ID'}
],
'publishNow': True
}
)Carousel Requirements
- All items should have the same aspect ratio
- First item determines the aspect ratio for all
- Mix of images and videos is allowed
- Each item: max 8 MB (images), 100 MB (videos)
Stories
To post as a Story instead of feed:
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mediaItems": [
{"type": "image", "url": "https://example.com/story-image.jpg"}
],
"platforms": [{
"platform": "instagram",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"contentType": "story"
}
}],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
mediaItems: [
{ type: 'image', url: 'https://example.com/story-image.jpg' }
],
platforms: [{
platform: 'instagram',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
contentType: 'story'
}
}],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'mediaItems': [
{'type': 'image', 'url': 'https://example.com/story-image.jpg'}
],
'platforms': [{
'platform': 'instagram',
'accountId': 'YOUR_ACCOUNT_ID',
'platformSpecificData': {
'contentType': 'story'
}
}],
'publishNow': True
}
)Story Notes:
- Stories disappear after 24 hours
- No text captions displayed (use image overlays)
- 9:16 aspect ratio recommended
Link Stickers: Instagram's tappable link stickers for Stories are not available through the API. This is a limitation of Instagram's Graph API, not Late. To add link stickers, you'll need to post Stories directly through the Instagram app.
Trial Reels
Trial Reels are initially shared only with non-followers, allowing you to test content performance before showing it to your audience. They can later be "graduated" to regular Reels visible to followers.
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mediaItems": [
{"type": "video", "url": "https://example.com/reel.mp4"}
],
"platforms": [{
"platform": "instagram",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"trialParams": {
"graduationStrategy": "SS_PERFORMANCE"
}
}
}],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
mediaItems: [
{ type: 'video', url: 'https://example.com/reel.mp4' }
],
platforms: [{
platform: 'instagram',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
trialParams: {
graduationStrategy: 'SS_PERFORMANCE'
}
}
}],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'mediaItems': [
{'type': 'video', 'url': 'https://example.com/reel.mp4'}
],
'platforms': [{
'platform': 'instagram',
'accountId': 'YOUR_ACCOUNT_ID',
'platformSpecificData': {
'trialParams': {
'graduationStrategy': 'SS_PERFORMANCE'
}
}
}],
'publishNow': True
}
)Graduation Strategies
| Strategy | Description |
|---|---|
MANUAL | Trial Reel can only be graduated manually from the Instagram app |
SS_PERFORMANCE | Trial Reel automatically graduates if it performs well with non-followers |
Note: Trial Reels only apply to video posts (Reels). They cannot be used with images, carousels, or stories.
Thumbnail Offset for Reels
You can specify a frame from your video to use as the Reel thumbnail by setting a millisecond offset from the start of the video:
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Check out my new Reel! 🎬",
"mediaItems": [
{"type": "video", "url": "https://example.com/reel.mp4"}
],
"platforms": [{
"platform": "instagram",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"thumbOffset": 5000
}
}],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Check out my new Reel! 🎬',
mediaItems: [
{ type: 'video', url: 'https://example.com/reel.mp4' }
],
platforms: [{
platform: 'instagram',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
thumbOffset: 5000
}
}],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': 'Check out my new Reel! 🎬',
'mediaItems': [
{'type': 'video', 'url': 'https://example.com/reel.mp4'}
],
'platforms': [{
'platform': 'instagram',
'accountId': 'YOUR_ACCOUNT_ID',
'platformSpecificData': {
'thumbOffset': 5000
}
}],
'publishNow': True
}
)| Property | Description |
|---|---|
thumbOffset | Millisecond offset from the start of the video (e.g., 5000 = 5 seconds) |
Note: If you provide a custom thumbnail URL (
instagramThumbnailin mediaItems), it takes priority overthumbOffset. The offset defaults to 0 (first frame) if not specified.
Custom Cover Images for Reels
You can upload a custom cover image for your Instagram Reels instead of using a frame from the video. Use the instagramThumbnail parameter in your media item:
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Check out my new Reel! 🎬",
"mediaItems": [
{
"type": "video",
"url": "https://example.com/reel.mp4",
"instagramThumbnail": "https://example.com/custom-cover.jpg"
}
],
"platforms": [
{"platform": "instagram", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Check out my new Reel! 🎬',
mediaItems: [
{
type: 'video',
url: 'https://example.com/reel.mp4',
instagramThumbnail: 'https://example.com/custom-cover.jpg'
}
],
platforms: [
{ platform: 'instagram', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': 'Check out my new Reel! 🎬',
'mediaItems': [
{
'type': 'video',
'url': 'https://example.com/reel.mp4',
'instagramThumbnail': 'https://example.com/custom-cover.jpg'
}
],
'platforms': [
{'platform': 'instagram', 'accountId': 'YOUR_ACCOUNT_ID'}
],
'publishNow': True
}
)Cover Image Requirements
| Property | Requirement |
|---|---|
| Format | JPEG, PNG |
| Recommended Size | 1080 × 1920 px (9:16) |
| Aspect Ratio | Should match your Reel (typically 9:16) |
Tip: Custom cover images are great for adding text overlays, branded thumbnails, or eye-catching visuals that encourage viewers to watch your Reel.
Collaborators
Invite up to 3 collaborators on feed posts and Reels:
{
"platforms": [
{
"platform": "instagram",
"accountId": "acc_123",
"platformSpecificData": {
"collaborators": ["username1", "username2"]
}
}
]
}Audio Name
You can set a custom name for the original audio in Reels, replacing the default "Original Audio" label. This can only be set once during creation or later from the Instagram audio page in the app.
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mediaItems": [{"type": "video", "url": "https://example.com/reel.mp4"}],
"platforms": [{
"platform": "instagram",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"audioName": "My Podcast Intro"
}
}],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
mediaItems: [
{ type: 'video', url: 'https://example.com/reel.mp4' }
],
platforms: [{
platform: 'instagram',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
audioName: 'My Podcast Intro'
}
}],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'mediaItems': [
{'type': 'video', 'url': 'https://example.com/reel.mp4'}
],
'platforms': [
{
'platform': 'instagram',
'accountId': 'YOUR_ACCOUNT_ID',
'platformSpecificData': {
'audioName': 'My Podcast Intro'
}
}
],
'publishNow': True
}
)Note: The audio name can only be set once and is applicable only for Reels (video posts).
Automatic Compression
Late automatically compresses oversized media:
| Content Type | Image Limit | Video Limit | Action |
|---|---|---|---|
| Feed/Carousel | 8 MB | 300 MB | Auto-compress |
| Story | 8 MB | 100 MB | Auto-compress |
| Reel | 8 MB | 300 MB | Auto-compress |
Original files are preserved.
Common Issues
"Invalid aspect ratio"
Your image is outside the 0.8-1.91 range. Solutions:
- Crop to 4:5 or 1:1
- Use
contentType: "story"for 9:16 content
Video rejected as Reel
Videos under 90 seconds with 9:16 aspect ratio automatically become Reels. If you want a feed video, use a different aspect ratio.
Carousel items showing differently
Ensure all carousel items have the same aspect ratio. The first item sets the ratio for all.
Inbox
Requires Inbox add-on — $1/social set/month
Instagram supports DMs and comments with some limitations.
Direct Messages
| Feature | Supported |
|---|---|
| List conversations | ✅ |
| Fetch messages | ✅ |
| Send text messages | ✅ |
| Send attachments | ❌ (API limitation) |
| Archive/unarchive | ✅ |
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
- No DM attachments — Instagram's API does not support sending media in DMs
- 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 API 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