LateLate API
Platforms

Instagram API


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:

TypeDescriptionMedia Required
Feed PostStandard image/video postYes
CarouselMulti-image/video post (up to 10)Yes
Story24-hour ephemeral contentYes
ReelShort-form video (up to 90 sec)Yes (video)

Image Requirements

PropertyFeed PostStoryCarousel
Max Images1110
FormatsJPEG, PNGJPEG, PNGJPEG, PNG
Max File Size8 MB8 MB8 MB each
Recommended1080 × 1350 px1080 × 1920 px1080 × 1080 px

Aspect Ratio Requirements

This is critical for Instagram! Feed posts have strict aspect ratio requirements:

OrientationRatioDimensionsUse Case
Portrait4:51080 × 1350 pxBest engagement
Square1:11080 × 1080 pxStandard
Landscape1.91:11080 × 566 pxMinimum

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 cropped

Important: 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

PropertyRequirement
FormatsMP4, MOV
Max File Size300 MB (auto-compressed if larger)
Max Duration90 seconds (Reels), 60 min (Feed)
Min Duration3 seconds
Aspect Ratio9:16 (Reels), 4:5 to 1.91:1 (Feed)
Resolution1080 × 1920 px (Reels)
Frame Rate30 fps recommended
CodecH.264

Story Videos

PropertyRequirement
Max File Size100 MB (auto-compressed if larger)
Max Duration60 seconds
Aspect Ratio9:16
Resolution1080 × 1920 px

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
    }
)
  • 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

StrategyDescription
MANUALTrial Reel can only be graduated manually from the Instagram app
SS_PERFORMANCETrial 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
    }
)
PropertyDescription
thumbOffsetMillisecond offset from the start of the video (e.g., 5000 = 5 seconds)

Note: If you provide a custom thumbnail URL (instagramThumbnail in mediaItems), it takes priority over thumbOffset. 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

PropertyRequirement
FormatJPEG, PNG
Recommended Size1080 × 1920 px (9:16)
Aspect RatioShould 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 TypeImage LimitVideo LimitAction
Feed/Carousel8 MB300 MBAuto-compress
Story8 MB100 MBAuto-compress
Reel8 MB300 MBAuto-compress

Original files are preserved.

Common Issues

"Invalid aspect ratio"

Your image is outside the 0.8-1.91 range. Solutions:

  1. Crop to 4:5 or 1:1
  2. 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.

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

FeatureSupported
List conversations
Fetch messages
Send text messages
Send attachments❌ (API limitation)
Archive/unarchive

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

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.