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
OAuth 2.0
200 calls/hour per user; 100 posts/24h
JSON
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
https://graph.instagram.com/v25.0Setting 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.
- 1Go to developers.facebook.com and create a new App (type: Business)
- 2Add the Instagram product to your app from the App Dashboard
- 3Under Instagram > API setup with Instagram Business Login, note your App ID and App Secret
- 4Request instagram_business_basic and instagram_business_content_publish permissions via App Review > Permissions and Features
- 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
- 6Exchange the authorization code for a short-lived token at https://graph.facebook.com/v25.0/oauth/access_token
- 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}
- 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
1import requests2import os34APP_ID = os.environ['INSTAGRAM_APP_ID']5APP_SECRET = os.environ['INSTAGRAM_APP_SECRET']67def 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']1920def 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']3031def 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
/{ig-user-id}/mediaCreates 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
media_type | string | required | Must be 'REELS' for Reels content. |
video_url | string | required | Publicly 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_url | string | optional | HTTPS URL of the Reel cover image. If omitted, Instagram uses the first frame. |
caption | string | optional | Caption text with optional hashtags and @mentions. Max 2,200 characters. |
share_to_feed | boolean | optional | Set to true to also show the Reel in the main feed. Defaults to true. |
Request
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
1{"id": "17889615691580904"}/{container-id}?fields=status_codePolls 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
fields | string | required | Must include 'status_code'. Values: IN_PROGRESS (processing), FINISHED (ready to publish), ERROR (rejected), EXPIRED (>24h old), PUBLISHED. |
access_token | string | required | Same long-lived token used to create the container. |
Response
1{"status_code": "FINISHED", "id": "17889615691580904"}/{ig-user-id}/media_publishPublishes a FINISHED Reels container. The Reel goes live immediately. Returns the published media ID for later analytics queries.
| Parameter | Type | Required | Description |
|---|---|---|---|
creation_id | string | required | The container ID returned from the /media creation step. |
access_token | string | required | Long-lived User Access Token with instagram_business_content_publish scope. |
Request
1{"creation_id": "17889615691580904", "access_token": "EAAB..."}Response
1{"id": "17895695668004550"}/{ig-user-id}/content_publishing_limitReturns current publishing quota usage. Reels count toward the same 100 posts/24h cap as feed posts and Stories.
| Parameter | Type | Required | Description |
|---|---|---|---|
fields | string | optional | Use 'config,quota_usage' to get both the limit and current usage. |
access_token | string | required | Long-lived User Access Token. |
Response
1{"data": [{"quota_usage": 3, "config": {"quota_total": 100, "quota_duration": 86400}}]}Step-by-step automation
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.
1# Check that the video URL is accessible before creating the container2curl -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.
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.
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.
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.
1# Poll until status_code is FINISHED2while true; do3 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" ] && break6 [ "$STATUS" = "ERROR" ] && echo "Processing failed" && exit 17 sleep 158donePro 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.
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.
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.
1#!/usr/bin/env python32"""Instagram Reels Scheduler - cron job or queue worker."""3import os4import time5import logging6import requests78logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')9log = logging.getLogger(__name__)1011IG_USER_ID = os.environ['IG_USER_ID']12ACCESS_TOKEN = os.environ['IG_ACCESS_TOKEN']13BASE = 'https://graph.instagram.com/v25.0'1415def api(method, path, **kwargs):16 kwargs.setdefault('data', {})['access_token'] = ACCESS_TOKEN17 kwargs.setdefault('params', {})['access_token'] = ACCESS_TOKEN18 r = getattr(requests, method)(f'{BASE}{path}', timeout=30, **kwargs)19 r.raise_for_status()20 return r.json()2122def 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']2728def 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_url37 return api('post', f'/{IG_USER_ID}/media', data=body)['id']3839def wait_until_ready(container_id, timeout=600):40 deadline = time.time() + timeout41 delay = 1042 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 return47 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')5253def publish(container_id):54 return api('post', f'/{IG_USER_ID}/media_publish', data={'creation_id': container_id})['id']5556def schedule_reel(video_url, caption, cover_url=None):57 if not check_quota():58 log.warning('Daily publishing quota exhausted. Skipping.')59 return None60 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_id6768if __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
{"error":{"code":24,"message":"(#24) Invalid media type or format","type":"OAuthException"}}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.
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.
Do not retry — fix the video format first.
{"error":{"code":9004,"message":"(#9004) The media container has expired","type":"OAuthException"}}The container was created more than 24 hours ago. Containers expire whether or not the video finished processing.
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.
Create a new container and restart the flow.
{"error":{"code":17,"message":"User request limit reached","type":"OAuthException"}}Exceeded 200 API calls per hour (usually from aggressive status polling of long-processing videos) or hit the 100 posts/24h publishing cap.
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.
Exponential backoff starting at 60 seconds. The rate limit is a rolling 1-hour window.
{"error":{"code":190,"message":"Invalid OAuth access token - Cannot parse access token","type":"OAuthException"}}The long-lived access token has expired (60 days), been revoked, or the user removed your app's permissions.
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.
Do not retry — refresh the token first.
Container status remains IN_PROGRESS for more than 10 minutesThe video may be corrupted, too large, have an unsupported resolution, or Instagram's processing pipeline may be experiencing delays.
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.
Create a new container with a re-encoded version of the video.
Rate Limits for Instagram Graph API (Reels)
| Scope | Limit | Window |
|---|---|---|
| Per user (app+user pair) | 200 API calls | per hour (rolling) |
| Content publishing | 100 API-published posts (Reels + feed posts + Stories combined) | per 24-hour sliding window per account |
| Status polling safe zone | ~120 status polls maximum | per hour (at 30-second intervals) before impacting your 200/hr quota |
1import time2import requests34def 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_delay7 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=3012 )13 # Check rate limit headers14 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 continue19 resp.raise_for_status()20 status = resp.json()['status_code']21 if status == 'FINISHED':22 return True23 if status in ('ERROR', 'EXPIRED'):24 raise ValueError(f'Container failed: {status}')25 delay = min(delay * 1.5, 60) # Gentle growth, cap at 60s26 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
intermediateStore 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
advancedAutomatically 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
advancedAutomatically 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
advancedPublish 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/monthZapier's Instagram for Business integration supports Reels creation, triggered by new items in Google Drive, Dropbox, or custom webhooks — no API knowledge required.
- + Zero setup for non-developers
- + Visual workflow builder
- + 500+ integrations for triggers
- - 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/monthMake's Instagram module wraps the Graph API Reels flow with drag-and-drop scenario building, including status polling and conditional logic.
- + More affordable than Zapier
- + Better data transformation tools
- + Precise scheduling with custom intervals
- - Steeper learning curve
- - Instagram module requires Meta App Review regardless
- - Limited free operations
n8n
Free (self-hosted); Cloud from $20/monthSelf-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.
- + Free self-hosted, no per-operation cost
- + Full API access via HTTP node
- + Runs on any VPS
- - 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.
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.
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.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation