A guide to understand, operate, and extend the workflow.
Goal: Turn a simple Airtable sheet into a content calendar that automatically publishes Instagram Reels via the Instagram Graph API , on a schedule you control in n8n.
Why this matters:
Core flow (one row = one post):
status = "To Post" AND scheduled_at <= NOW() AND platform = "IG".video_url, caption, recordId.ig_media_id, and timestamp.[Cron]
→ [Airtable: Search records]
→ [Split Out: records]
→ [Set: Map fields]
→ [IG: Create Media Container]
→ [Wait 90s]
→ [IG: Publish Reel]
→ [Airtable: Update record]
Sticky Notes inside the workflow explain each step (they’re rendered from parameters.content with Markdown).
Create a table (e.g., Posts) with these fields:
| Field | Type | Purpose |
|---|---|---|
video_url | URL or Text | Directly accessible (public) URL to your MP4 |
caption | Long text | Final caption (hashtags, line breaks, emojis) |
platform | Single select | Set IG for this workflow |
status | Single select | To Post → will be picked up; Posted later |
scheduled_at | Date/Time (UTC) | When to post |
ig_media_id | Text (optional) | Filled by n8n after publishing |
posted_at | Date/Time | Filled by n8n after publishing |
Filter used in the Airtable “Search records” node:
AND({status}='To Post', {scheduled_at}<=NOW(), {platform}='IG')
Tip: If you localize/rename fields, update the filter accordingly.
IG_API_VERSION (e.g., v21.0)IG_USER_IDIG_ACCESS_TOKENvideo_url (e.g., S3/GCS signed URL, public CDN, Drive/Dropbox direct link). The API pulls from your URL; it cannot fetch files behind logins.searchAND({status}='To Post', {scheduled_at}<=NOW(), {platform}='IG')records[]. Each record has id and fields.records[] array into individual items.recordId.recordId = {{$json.id}}video_url = {{$json.fields.video_url}}caption = {{$json.fields.caption}}scheduled_at = {{$json.fields.scheduled_at}}POST https://graph.facebook.com/{v}/{ig-user-id}/mediavideo_url = {{$json.video_url}}caption = {{$json.caption}}media_type=REELSshare_to_feed=true (optional)access_token=${IG_ACCESS_TOKEN}id (this is your creation_id for publishing).video_url.120–180s.POST https://graph.facebook.com/{v}/{ig-user-id}/media_publishcreation_id = {{$json.id}} (the container id from step E)access_token = ${IG_ACCESS_TOKEN}id = ig_media_id (the published media).status = "Posted"ig_media_id = {{$json.id}}posted_at = {{$now}}video_url = a public MP4 URLcaption = a small captionplatform = IGstatus = To Postscheduled_at = in the past (so it’s due now)idmedia idPosted with ig_media_id and posted_atstatus = To Post, set accurate scheduled_at in UTC.Backfilling:
If you need to post a bunch of older content, set scheduled_at in the past for those rows and let Cron pick them up. If needed, run the workflow manually.


