Skip to main content
RapidDev - Software Development Agency
API AutomationsInstagramOAuth 2.0

How to Automate Instagram Reels Scheduling using the API

Schedule Instagram Reels via the Graph API using the same two-step container model as feed posts, but with media_type=REELS and video_url instead of image_url. Video processing takes 30 seconds to several minutes, so you must poll the container status until FINISHED before calling media_publish. API-published Reels are capped at 90 seconds; only MP4/MOV with H.264 codec work — other formats silently fail with error code 24.

Need help automating? Talk to an expert
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read30-60 minutesInstagramMay 2026RapidDev Engineering Team
TL;DR

Schedule Instagram Reels via the Graph API using the same two-step container model as feed posts, but with media_type=REELS and video_url instead of image_url. Video processing takes 30 seconds to several minutes, so you must poll the container status until FINISHED before calling media_publish. API-published Reels are capped at 90 seconds; only MP4/MOV with H.264 codec work — other formats silently fail with error code 24.

API Quick Reference

Auth

OAuth 2.0

Rate limit

200 calls/hour per user; 100 posts/24h

Format

JSON

SDK

Available

Understanding the Instagram Graph API for Reels

The Instagram Graph API v25.0 supports Reels publishing through the same container model used for feed posts, with the container specifying media_type=REELS and providing a video_url instead of image_url. The base URL is https://graph.instagram.com/v25.0 and all operations require a long-lived User Access Token with the instagram_business_content_publish scope.

The key difference between Reels and photo posts is processing time: images finish in seconds, but video containers typically take 30 seconds to several minutes depending on file size and server load. Your automation must poll the container's status_code field in a loop until it returns FINISHED. Polling too aggressively will burn through your 200 calls/hour quota — use exponential backoff with a minimum 10-second interval between polls.

Critical API limitation: Reels published via the API are capped at 90 seconds maximum duration, even though the native Instagram app supports Reels up to 3+ minutes. Only MP4 or MOV container format with H.264 video codec and AAC audio will be accepted. Other formats return error code 24 without a helpful message. Official docs: https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media

Base URLhttps://graph.instagram.com/v25.0

Setting Up Instagram Graph API Authentication

The Instagram Graph API uses OAuth 2.0 long-lived User Access Tokens. Short-lived tokens last 1 hour and must be exchanged for 60-day long-lived tokens immediately after the OAuth flow. There is no automatic refresh — you must call the refresh endpoint before the 60-day expiry, but only after the token is at least 24 hours old.

  1. 1Go to developers.facebook.com and create a new App (type: Business)
  2. 2Add the Instagram product to your app from the App Dashboard
  3. 3Under Instagram > API setup with Instagram Business Login, note your App ID and App Secret
  4. 4Request instagram_business_basic and instagram_business_content_publish permissions via App Review > Permissions and Features
  5. 5Implement the OAuth flow: redirect to https://www.facebook.com/v25.0/dialog/oauth?client_id={app-id}&redirect_uri={uri}&scope=instagram_business_basic,instagram_business_content_publish
  6. 6Exchange the authorization code for a short-lived token at https://graph.facebook.com/v25.0/oauth/access_token
  7. 7Exchange the short-lived token for a 60-day long-lived token: GET https://graph.instagram.com/access_token?grant_type=fb_exchange_token&client_id={app-id}&client_secret={app-secret}&fb_exchange_token={short-token}
  8. 8Schedule a refresh job to call GET https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token={token} every 50 days
auth.py
1import requests
2import os
3
4APP_ID = os.environ['INSTAGRAM_APP_ID']
5APP_SECRET = os.environ['INSTAGRAM_APP_SECRET']
6
7def exchange_for_long_lived_token(short_lived_token):
8 resp = requests.get(
9 'https://graph.instagram.com/access_token',
10 params={
11 'grant_type': 'fb_exchange_token',
12 'client_id': APP_ID,
13 'client_secret': APP_SECRET,
14 'fb_exchange_token': short_lived_token,
15 }
16 )
17 resp.raise_for_status()
18 return resp.json()['access_token']
19
20def refresh_long_lived_token(long_lived_token):
21 resp = requests.get(
22 'https://graph.instagram.com/refresh_access_token',
23 params={
24 'grant_type': 'ig_refresh_token',
25 'access_token': long_lived_token,
26 }
27 )
28 resp.raise_for_status()
29 return resp.json()['access_token']
30
31def get_ig_user_id(access_token):
32 resp = requests.get(
33 'https://graph.instagram.com/v25.0/me',
34 params={'fields': 'id,username', 'access_token': access_token}
35 )
36 resp.raise_for_status()
37 return resp.json()['id']

Security notes

  • Never hardcode access tokens or App Secrets — use environment variables or a secrets manager
  • Long-lived tokens expire after 60 days; automate renewal every 50 days
  • Never expose your App Secret in client-side code or mobile app bundles
  • Store tokens encrypted at rest — treat them with the same security as passwords
  • Each long-lived token is tied to one Instagram user; manage them per-account in your database
  • Revoke compromised tokens immediately via the Token Debugger at developers.facebook.com/tools/debug/accesstoken

Key endpoints

POST/{ig-user-id}/media

Creates a Reels container by specifying media_type=REELS, a publicly accessible video_url, and optional cover_url for the thumbnail. The container processes asynchronously — do not attempt to publish until status_code returns FINISHED.

ParameterTypeRequiredDescription
media_typestringrequiredMust be 'REELS' for Reels content.
video_urlstringrequiredPublicly accessible HTTPS URL of the video. Must be MP4 or MOV with H.264 video codec and AAC audio. Maximum 90 seconds via API.
cover_urlstringoptionalHTTPS URL of the Reel cover image. If omitted, Instagram uses the first frame.
captionstringoptionalCaption text with optional hashtags and @mentions. Max 2,200 characters.
share_to_feedbooleanoptionalSet to true to also show the Reel in the main feed. Defaults to true.

Request

json
1{"media_type": "REELS", "video_url": "https://example.com/video.mp4", "caption": "Check this out #reels", "cover_url": "https://example.com/cover.jpg", "share_to_feed": true, "access_token": "EAAB..."}

Response

json
1{"id": "17889615691580904"}
GET/{container-id}?fields=status_code

Polls the processing status of the Reels container. Video processing takes significantly longer than images — poll every 10-30 seconds with exponential backoff. Do not publish until status_code is FINISHED.

ParameterTypeRequiredDescription
fieldsstringrequiredMust include 'status_code'. Values: IN_PROGRESS (processing), FINISHED (ready to publish), ERROR (rejected), EXPIRED (>24h old), PUBLISHED.
access_tokenstringrequiredSame long-lived token used to create the container.

Response

json
1{"status_code": "FINISHED", "id": "17889615691580904"}
POST/{ig-user-id}/media_publish

Publishes a FINISHED Reels container. The Reel goes live immediately. Returns the published media ID for later analytics queries.

ParameterTypeRequiredDescription
creation_idstringrequiredThe container ID returned from the /media creation step.
access_tokenstringrequiredLong-lived User Access Token with instagram_business_content_publish scope.

Request

json
1{"creation_id": "17889615691580904", "access_token": "EAAB..."}

Response

json
1{"id": "17895695668004550"}
GET/{ig-user-id}/content_publishing_limit

Returns current publishing quota usage. Reels count toward the same 100 posts/24h cap as feed posts and Stories.

ParameterTypeRequiredDescription
fieldsstringoptionalUse 'config,quota_usage' to get both the limit and current usage.
access_tokenstringrequiredLong-lived User Access Token.

Response

json
1{"data": [{"quota_usage": 3, "config": {"quota_total": 100, "quota_duration": 86400}}]}

Step-by-step automation

1

Validate Video Format Before Upload

Why: The API silently rejects unsupported video formats with a generic error code 24 — catching format issues upfront saves you a failed API call and the time to debug it.

Before creating the container, verify the video meets Instagram's requirements: MP4 or MOV container, H.264 video codec, AAC audio codec, 9:16 aspect ratio (recommended), at least 360px width, 23-60 FPS, and maximum 90 seconds duration. Also check that the video URL is publicly accessible via HTTPS — Instagram downloads the video from your URL.

request.sh
1# Check that the video URL is accessible before creating the container
2curl -I "https://example.com/video.mp4"

Pro tip: Use ffmpeg to re-encode problematic videos: `ffmpeg -i input.mp4 -c:v libx264 -c:a aac -t 90 output.mp4`. This ensures H.264+AAC compliance and trims to 90 seconds.

Expected result: Validation passes or throws a descriptive error identifying exactly which requirement failed. Fix video format issues before proceeding to avoid wasted API calls.

2

Create the Reels Container

Why: Creating the container triggers Instagram's asynchronous video processing pipeline — without this step, there is nothing to publish.

POST to /{ig-user-id}/media with media_type=REELS, a publicly accessible video_url, and optionally a cover_url for the thumbnail. The video must be hosted at an HTTPS URL that Instagram's servers can download from. Store the returned container ID — you'll need it to poll status and eventually publish.

request.sh
1curl -X POST \
2 "https://graph.instagram.com/v25.0/${IG_USER_ID}/media" \
3 -d "media_type=REELS" \
4 -d "video_url=https://example.com/reel.mp4" \
5 -d "cover_url=https://example.com/cover.jpg" \
6 -d "caption=Check this out %23reels" \
7 -d "share_to_feed=true" \
8 -d "access_token=${ACCESS_TOKEN}"

Pro tip: Host your videos on S3, GCS, or Cloudflare R2 with public read access. Avoid localhost or signed URLs that expire — Instagram downloads the video during processing, not at the time of the API call.

Expected result: JSON response: {"id": "17889615691580904"} — the container ID. Instagram begins processing the video immediately.

3

Poll Container Status Until FINISHED

Why: Video processing takes 30 seconds to several minutes — publishing before FINISHED will fail, and the container expires after 24 hours if not published.

Poll GET /{container-id}?fields=status_code in a loop using exponential backoff. Start with a 10-second wait (not 5 — video takes longer than images), doubling up to 60 seconds maximum interval. Most Reels finish within 1-2 minutes. If you get ERROR, the video format or specs are wrong. Critically: each poll uses one of your 200 calls/hour — with a 10-second base interval, 120 polls/hour is safe.

request.sh
1# Poll until status_code is FINISHED
2while true; do
3 STATUS=$(curl -s "https://graph.instagram.com/v25.0/${CONTAINER_ID}?fields=status_code&access_token=${ACCESS_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin)['status_code'])")
4 echo "Status: $STATUS"
5 [ "$STATUS" = "FINISHED" ] && break
6 [ "$STATUS" = "ERROR" ] && echo "Processing failed" && exit 1
7 sleep 15
8done

Pro tip: Set a 10-minute timeout (max_wait=600s) for Reels polling — if it hasn't finished by then, something is wrong with the video file, and you should stop polling and investigate rather than holding the process open indefinitely.

Expected result: After 30 seconds to several minutes, status_code returns FINISHED. Short Reels (under 15 seconds) typically finish in 30-60 seconds; longer ones can take 2-5 minutes.

4

Publish the Reel

Why: The media_publish call makes the Reel live — it cannot be undone from the API (use the Instagram app to delete if needed).

Once processing is FINISHED, POST to /{ig-user-id}/media_publish with creation_id set to the container ID. The Reel goes live immediately. Store the returned media_id to query analytics later. This call counts toward the 100 posts/24h cap.

request.sh
1curl -X POST \
2 "https://graph.instagram.com/v25.0/${IG_USER_ID}/media_publish" \
3 -d "creation_id=${CONTAINER_ID}" \
4 -d "access_token=${ACCESS_TOKEN}"

Pro tip: After publishing, store the media_id and use GET /{media-id}/insights?metric=plays,reach,likes,comments,shares,saved to pull Reel performance metrics after 24 hours (some metrics need time to populate).

Expected result: JSON response: {"id": "17895695668004550"} — the published media ID. The Reel is immediately visible on Instagram.

Complete working code

This script implements a complete Reels scheduling pipeline: validates video format, checks publishing quota, creates the container, polls until FINISHED with appropriate backoff for video processing times, then publishes. Designed to run as a cron job or task queue worker.

automate_ig_reels.py
1#!/usr/bin/env python3
2"""Instagram Reels Scheduler - cron job or queue worker."""
3import os
4import time
5import logging
6import requests
7
8logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
9log = logging.getLogger(__name__)
10
11IG_USER_ID = os.environ['IG_USER_ID']
12ACCESS_TOKEN = os.environ['IG_ACCESS_TOKEN']
13BASE = 'https://graph.instagram.com/v25.0'
14
15def api(method, path, **kwargs):
16 kwargs.setdefault('data', {})['access_token'] = ACCESS_TOKEN
17 kwargs.setdefault('params', {})['access_token'] = ACCESS_TOKEN
18 r = getattr(requests, method)(f'{BASE}{path}', timeout=30, **kwargs)
19 r.raise_for_status()
20 return r.json()
21
22def check_quota():
23 d = api('get', f'/{IG_USER_ID}/content_publishing_limit', params={'fields': 'config,quota_usage'})
24 item = d['data'][0]
25 log.info(f'Quota: {item["quota_usage"]}/{item["config"]["quota_total"]}')
26 return item['quota_usage'] < item['config']['quota_total']
27
28def create_reel_container(video_url, caption, cover_url=None):
29 body = {
30 'media_type': 'REELS',
31 'video_url': video_url,
32 'caption': caption,
33 'share_to_feed': 'true',
34 }
35 if cover_url:
36 body['cover_url'] = cover_url
37 return api('post', f'/{IG_USER_ID}/media', data=body)['id']
38
39def wait_until_ready(container_id, timeout=600):
40 deadline = time.time() + timeout
41 delay = 10
42 while time.time() < deadline:
43 status = api('get', f'/{container_id}', params={'fields': 'status_code'})['status_code']
44 log.info(f'Container {container_id}: {status}')
45 if status == 'FINISHED':
46 return
47 if status in ('ERROR', 'EXPIRED'):
48 raise ValueError(f'Container failed: {status}. Check H.264/AAC format and duration <=90s.')
49 delay = min(delay * 1.5, 60)
50 time.sleep(delay)
51 raise TimeoutError('Container processing timed out after 10 minutes')
52
53def publish(container_id):
54 return api('post', f'/{IG_USER_ID}/media_publish', data={'creation_id': container_id})['id']
55
56def schedule_reel(video_url, caption, cover_url=None):
57 if not check_quota():
58 log.warning('Daily publishing quota exhausted. Skipping.')
59 return None
60 log.info(f'Creating Reels container: {video_url}')
61 container_id = create_reel_container(video_url, caption, cover_url)
62 log.info(f'Waiting for video processing (container: {container_id})...')
63 wait_until_ready(container_id)
64 media_id = publish(container_id)
65 log.info(f'Reel published! Media ID: {media_id}')
66 return media_id
67
68if __name__ == '__main__':
69 media_id = schedule_reel(
70 video_url='https://your-cdn.com/reel.mp4',
71 caption='Your Reel caption here #reels',
72 cover_url='https://your-cdn.com/cover.jpg'
73 )
74 print(f'Published media_id: {media_id}')

Error handling

400{"error":{"code":24,"message":"(#24) Invalid media type or format","type":"OAuthException"}}
Cause

The video is not in a supported format (must be MP4 or MOV), doesn't use H.264 video codec, doesn't use AAC audio codec, or is not a valid 9:16 aspect ratio video. This error code also fires for images with wrong aspect ratios.

Fix

Re-encode the video: `ffmpeg -i input.mp4 -c:v libx264 -c:a aac -vf scale=1080:1920 -t 90 output.mp4`. Check codec with `ffprobe -v quiet -show_streams input.mp4`. Ensure duration is between 3 and 90 seconds.

Retry strategy

Do not retry — fix the video format first.

400{"error":{"code":9004,"message":"(#9004) The media container has expired","type":"OAuthException"}}
Cause

The container was created more than 24 hours ago. Containers expire whether or not the video finished processing.

Fix

Create a new container. Design your scheduler to create containers at publish time, not days in advance. Store your scheduling data (video URL, caption, target time) in your database and generate the container fresh when it's time to publish.

Retry strategy

Create a new container and restart the flow.

400{"error":{"code":17,"message":"User request limit reached","type":"OAuthException"}}
Cause

Exceeded 200 API calls per hour (usually from aggressive status polling of long-processing videos) or hit the 100 posts/24h publishing cap.

Fix

Increase poll interval to at least 10 seconds with exponential backoff. Check X-Business-Use-Case-Usage header on every response. For the 100 posts/day cap, check content_publishing_limit before creating containers.

Retry strategy

Exponential backoff starting at 60 seconds. The rate limit is a rolling 1-hour window.

400{"error":{"code":190,"message":"Invalid OAuth access token - Cannot parse access token","type":"OAuthException"}}
Cause

The long-lived access token has expired (60 days), been revoked, or the user removed your app's permissions.

Fix

Call GET https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token={token} to refresh. If fully expired, the user must re-authorize via your OAuth flow.

Retry strategy

Do not retry — refresh the token first.

500Container status remains IN_PROGRESS for more than 10 minutes
Cause

The video may be corrupted, too large, have an unsupported resolution, or Instagram's processing pipeline may be experiencing delays.

Fix

Stop polling after 10 minutes and mark the container as failed. Re-encode the video with standard settings (1080x1920, H.264, AAC, under 100MB for reliable processing) and create a new container.

Retry strategy

Create a new container with a re-encoded version of the video.

Rate Limits for Instagram Graph API (Reels)

ScopeLimitWindow
Per user (app+user pair)200 API callsper hour (rolling)
Content publishing100 API-published posts (Reels + feed posts + Stories combined)per 24-hour sliding window per account
Status polling safe zone~120 status polls maximumper hour (at 30-second intervals) before impacting your 200/hr quota
retry-handler.ts
1import time
2import requests
3
4def poll_with_backoff(container_id, access_token, max_polls=40, base_delay=10):
5 """Poll container status with exponential backoff. Safe within 200/hr limit."""
6 delay = base_delay
7 for i in range(max_polls):
8 resp = requests.get(
9 f'https://graph.instagram.com/v25.0/{container_id}',
10 params={'fields': 'status_code', 'access_token': access_token},
11 timeout=30
12 )
13 # Check rate limit headers
14 usage = resp.headers.get('X-Business-Use-Case-Usage', '{}')
15 if resp.status_code == 400 and resp.json().get('error', {}).get('code') == 17:
16 print(f'Rate limited. Waiting {delay * 4}s')
17 time.sleep(delay * 4)
18 continue
19 resp.raise_for_status()
20 status = resp.json()['status_code']
21 if status == 'FINISHED':
22 return True
23 if status in ('ERROR', 'EXPIRED'):
24 raise ValueError(f'Container failed: {status}')
25 delay = min(delay * 1.5, 60) # Gentle growth, cap at 60s
26 time.sleep(delay)
27 raise TimeoutError('Max polls exceeded')
  • Use a minimum 10-second polling interval for Reels (not 5 seconds like images) — video processing legitimately takes longer
  • Implement a 10-minute hard timeout on polling — if video hasn't processed in 10 minutes, it likely has a format problem
  • Monitor X-Business-Use-Case-Usage header and reduce poll frequency when call_count exceeds 60%
  • Host videos on CDN storage (S3, Cloudflare R2) to ensure consistent download speeds for Instagram's processing servers
  • Pre-validate video format with ffprobe before creating the container to avoid wasting calls on rejected videos

Security checklist

  • Store access tokens in environment variables — never in source code, logs, or version control
  • Never embed your App Secret in client-side or mobile code
  • Use HTTPS URLs for all video_url and cover_url parameters — Instagram rejects HTTP URLs
  • Validate video content before uploading to avoid accidentally publishing sensitive or incorrect videos
  • Implement an audit log of published Reels with container IDs, media IDs, and timestamps for debugging and compliance
  • Restrict OAuth redirect URIs to only your production and staging domains in the Meta App dashboard
  • Rotate long-lived tokens automatically every 50 days; alert when refresh fails
  • Ensure video storage URLs are access-controlled during upload; make them publicly accessible only for the duration of Instagram's processing window

Automation use cases

Video Content Calendar for Reels

intermediate

Store a schedule of Reels with video URLs, captions, cover images, and target publish times in a database; a cron job runs every minute to trigger the publish flow for due posts.

Automated Product Demo Reels

advanced

Automatically publish new product demo videos as Reels whenever a new product is added to your e-commerce store, with auto-generated captions from product data.

Repurposed Long-Form Video Clipper

advanced

Automatically clip 60-90 second segments from longer videos (YouTube exports, recordings) and schedule them as Reels via the API using ffmpeg preprocessing.

Multi-Account Reels Distributor

advanced

Publish the same Reel across multiple Instagram Business accounts from a single script, each with its own access token and staggered timing to avoid appearing coordinated.

No-code alternatives

Don't want to write code? These platforms can automate the same workflows visually.

Zapier

Free tier (limited); Starter from $19.99/month

Zapier's Instagram for Business integration supports Reels creation, triggered by new items in Google Drive, Dropbox, or custom webhooks — no API knowledge required.

Pros
  • + Zero setup for non-developers
  • + Visual workflow builder
  • + 500+ integrations for triggers
Cons
  • - Limited control over processing status polling
  • - Per-task pricing adds up for high volume
  • - Less reliable timing precision

Make (formerly Integromat)

Free tier (1,000 ops/month); Core from $9/month

Make's Instagram module wraps the Graph API Reels flow with drag-and-drop scenario building, including status polling and conditional logic.

Pros
  • + More affordable than Zapier
  • + Better data transformation tools
  • + Precise scheduling with custom intervals
Cons
  • - Steeper learning curve
  • - Instagram module requires Meta App Review regardless
  • - Limited free operations

n8n

Free (self-hosted); Cloud from $20/month

Self-hosted n8n lets you build a complete Reels scheduling pipeline with HTTP request nodes hitting the Graph API directly, with full control over polling logic.

Pros
  • + Free self-hosted, no per-operation cost
  • + Full API access via HTTP node
  • + Runs on any VPS
Cons
  • - Requires server infrastructure to host
  • - More setup than Zapier/Make
  • - Instagram node less polished than direct API

Best practices

  • Always pre-validate video with ffprobe before creating a container — H.264 codec and AAC audio are non-negotiable, and format errors return unhelpful error code 24
  • Keep API-published Reels under 90 seconds — longer videos must be trimmed since the API hard-caps at 90s even though the native app supports 3+ minutes
  • Use a cover_url thumbnail — Reels without a specified cover use the first frame, which is often a loading screen or awkward freeze frame
  • Poll status with a minimum 10-second interval using exponential backoff — polling too fast burns your 200 calls/hour quota without speeding up processing
  • Create containers only at publish time (2-5 minutes before target) — storing containers days ahead will result in expiry errors
  • Implement idempotency: store container_id per scheduled Reel so a crashed worker doesn't create duplicate containers on restart
  • Track the media_id of published Reels in your database to enable later analytics queries and content performance reporting

Ask AI to help

Copy one of these prompts to get a personalized, working implementation.

ChatGPT / Claude Prompt

I'm building an Instagram Reels scheduler using the Instagram Graph API v25.0. The flow is: POST /{ig-user-id}/media with media_type=REELS and video_url, poll GET /{container-id}?fields=status_code until FINISHED, then POST /{ig-user-id}/media_publish. My video polling sometimes times out after 10 minutes without finishing. Help me: 1) diagnose what video properties cause long processing times, 2) implement a ffmpeg preprocessing step that ensures H.264+AAC compliance before upload, 3) add proper exponential backoff that respects the 200 calls/hour limit by reading X-Business-Use-Case-Usage headers.

Lovable / V0 Prompt

Build a React dashboard for scheduling Instagram Reels. It needs: a queue view showing upcoming Reels with video thumbnails, captions, and scheduled times; a form to add new Reels with video URL, caption (with hashtag suggestions), cover image URL, and datetime picker; real-time status tracking showing container processing status (IN_PROGRESS, FINISHED, ERROR, PUBLISHED) with color indicators; a 100 posts/day quota meter; and a log of recently published Reels with their media IDs. Use Supabase as the backend and trigger the Graph API via Supabase Edge Functions on a cron schedule.

Frequently asked questions

Why are my Reels publishing as private (visible only to me)?

This is most likely a permissions issue. Ensure your app has the instagram_business_content_publish permission approved via Meta App Review. In Development mode, only test users (added via App Dashboard > Roles > Test Users) can publish successfully. The video also needs to be from a Business or Creator account — personal accounts cannot use the API.

What is the maximum Reel duration via API?

90 seconds via the Instagram Graph API. The native Instagram app supports Reels up to 3+ minutes, but the API strictly enforces the 90-second cap. Your automation should trim or reject videos longer than 90 seconds before creating the container. Use ffmpeg: `ffmpeg -i input.mp4 -t 90 output.mp4`.

What happens when I hit the rate limit during video polling?

You'll get HTTP 400 with error code 17. The 200 calls/hour quota is shared across all API operations for that user+app pair — not just polling. If your polling loop consumes most of the 200 calls, you'll have fewer left for other operations. Use exponential backoff starting at 10 seconds, and monitor the X-Business-Use-Case-Usage header. When the call_count percentage exceeds 80%, increase your polling interval.

Why does my video fail with error code 24?

Error code 24 means your video format or aspect ratio is rejected. Check: (1) container format must be MP4 or MOV — not MKV, AVI, or WebM; (2) video codec must be H.264 — not H.265/HEVC or VP9; (3) audio codec must be AAC — not MP3 or Opus; (4) frame rate must be 23-60 FPS. Run `ffprobe -v quiet -show_streams video.mp4` to inspect your video's actual codecs, then re-encode with `ffmpeg -c:v libx264 -c:a aac`.

Can I schedule Reels more than 24 hours ahead of time?

Not by pre-creating containers — they expire in exactly 24 hours. Instead, store your scheduling data (video URL, caption, cover image URL, target publish time) in your own database. Your cron job creates the container only at publish time, typically 3-5 minutes before the target timestamp. This is the correct scheduling pattern for the Instagram Graph API.

Is the Instagram Graph API free for Reels publishing?

Yes — the API is free with no per-request charges from Meta. You need a Meta developer account (free), a Facebook App (free), and the instagram_business_content_publish permission approved via App Review (free but takes 2-4 weeks). The account publishing the Reels must be an Instagram Business or Creator account, not a personal account.

Can RapidDev help build a Reels scheduling system?

Yes — RapidDev has built video publishing automation for content teams and agencies. We handle the Meta App setup and App Review process, video validation pipeline, scheduling database, and production monitoring. Book a free consultation at rapidevelopers.com.

How do I add music or audio to Reels published via API?

You cannot add Instagram's native music library to Reels via API — that feature is only available in the native Instagram app. Via API, the audio must be included in the video file itself. If you need licensed music, embed it in the video before uploading. Be aware of copyright: Instagram may automatically mute videos with copyrighted audio even when published via API.

RapidDev

Need this automated?

Our team has built 600+ apps with API automations. We can build this for you.

Book a free consultation

Skip the coding — we'll build it for you

Our experts have built 600+ API automations. From prototype to production in days, not weeks.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.