Back to Templates

Automated Instagram Reels Posting from Airtable Content Calendar

Created by

Created by: Sulieman Said || sulieman-said

Sulieman Said

Last update

Last update 23 days ago

Share


A guide to understand, operate, and extend the workflow.


1) What this workflow does (and why it’s useful)

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:

  • Your team plans everything in Airtable (user-friendly, collaborative).
  • n8n posts for you at the right time, every time.
  • You keep full control (no third-party SaaS lock-in, no manual uploads).
  • Later, you can reuse the same queue to post YouTube Shorts / TikTok (omnichannel).

Core flow (one row = one post):

  1. Cron starts the workflow at a set time.
  2. Airtable – Search grabs due rows: status = "To Post" AND scheduled_at <= NOW() AND platform = "IG".
  3. Split Out processes each row individually.
  4. Set (Map fields) normalizes Airtable fields → video_url, caption, recordId.
  5. IG: Create Media Container registers your video as REEL.
  6. Wait 90s lets IG process the video.
  7. IG: Publish Reel publishes the container.
  8. Airtable – Update marks the row as Posted, stores ig_media_id, and timestamp.

2) Architecture at a glance

[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).


3) Airtable schema (recommended)

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.


4) Prerequisites & credentials

  • Instagram Business/Creator account connected to a Facebook Page.
  • IG User ID for the connected account.
  • Long-lived IG Access Token with permissions to create and publish content.
  • n8n environment variables (Settings → Environments):
    • IG_API_VERSION (e.g., v21.0)
    • IG_USER_ID
    • IG_ACCESS_TOKEN
  • Airtable credential in n8n using a Personal Access Token (as in your example).
  • A publicly accessible video_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.

5) Node-by-node deep dive (what each node expects/returns)

A) Cron Trigger

  • What it does: Starts the workflow on a schedule (daily at 09:00 in the template).
  • How to use: Adjust hours/minutes to your cadence (hourly, twice a day, etc.).

B) Airtable: Search records

  • Operation: search
  • Base/Table: Select from dropdowns (matches your account).
  • Options → filterByFormula: AND({status}='To Post', {scheduled_at}<=NOW(), {platform}='IG')
  • Return: An array under records[]. Each record has id and fields.

C) Split Out: records

  • What it does: Turns the records[] array into individual items.
  • Why: Downstream steps can then act on each post separately.

D) Set: Map fields

  • What it does: Normalizes data to predictable keys and keeps the recordId.
  • Outputs:
    • recordId = {{$json.id}}
    • video_url = {{$json.fields.video_url}}
    • caption = {{$json.fields.caption}}
    • scheduled_at = {{$json.fields.scheduled_at}}

E) IG: Create Media Container (REELS)

  • Endpoint: POST https://graph.facebook.com/{v}/{ig-user-id}/media
  • Body params:
    • video_url = {{$json.video_url}}
    • caption = {{$json.caption}}
    • media_type=REELS
    • share_to_feed=true (optional)
    • access_token=${IG_ACCESS_TOKEN}
  • Return: JSON with container id under id (this is your creation_id for publishing).

F) Wait 90s

  • Why: IG needs time to process the video behind video_url.
  • Tip: If your videos are large or high bitrate, you may increase to 120–180s.

G) IG: Publish Reel

  • Endpoint: POST https://graph.facebook.com/{v}/{ig-user-id}/media_publish
  • Body params:
    • creation_id = {{$json.id}} (the container id from step E)
    • access_token = ${IG_ACCESS_TOKEN}
  • Return: JSON with id = ig_media_id (the published media).

H) Airtable: Update record

  • What it does: Writes results back to the same row.
  • Fields updated (example):
    • status = "Posted"
    • ig_media_id = {{$json.id}}
    • posted_at = {{$now}}

6) First-run checklist (do this once)

  1. In both Airtable nodes, pick your Base and Table (via dropdown).
  2. Confirm filterByFormula matches your field names exactly.
  3. Add one test row in Airtable:
    • video_url = a public MP4 URL
    • caption = a small caption
    • platform = IG
    • status = To Post
    • scheduled_at = in the past (so it’s due now)
  4. Set Cron to run in the next minute (or click Execute Workflow manually).
  5. Confirm the flow:
    • Airtable search → returns 1 record
    • Container created → you get an id
    • After 90s → Publish returns a media id
    • Airtable updated → row becomes Posted with ig_media_id and posted_at

7) Daily operations (how to use it day-to-day)

  • Your team fills Airtable with upcoming posts.
  • Keep status = To Post, set accurate scheduled_at in UTC.
  • n8n’s Cron checks regularly and posts due items.
  • After publishing, the row is marked Posted (so it won’t re-post).

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.