n8n Automation Hub
All articles
Build Breakdown

n8n Instagram & Facebook Auto-Posting, Driven by a Google Sheet

n8n workflow breakdown · Pavlo Veresiuk · August 23, 2026

I didn't want to open Meta Business Suite every day to post the same kind of content on a schedule. So the queue lives in a Google Sheet, and a Schedule Trigger in n8n does the actual posting โ€” one video a day, to both Facebook and Instagram, hands off.

Workflow at a glance
  1. Schedule Trigger fires once a day, reads the VideoQueue Sheet tab
  2. Code node filters for status == 'ready' and picks the next row
  3. Parallel branch: POST to the Facebook Page's /videos endpoint
  4. Parallel branch: POST to Instagram's /media endpoint (media_type: REELS), wait 45s, then /media_publish
  5. Sheet row gets marked published, matched by a unique video_key โ€” not row position
  6. Telegram message confirms both posts went out
Screenshot of the actual n8n auto-posting workflow canvas: Schedule Trigger, queue lookup, parallel Facebook and Instagram publishing branches, Telegram report

The actual workflow canvas, straight from my n8n instance.

The queue is just a Sheet, on purpose

The VideoQueue tab has seven columns: video_key, title, video_url, fb_caption, ig_caption, status, published_at. No dashboard, no separate app โ€” anyone who can edit a spreadsheet can queue up a week of content. The Code node's whole job is to grab the rows with status == 'ready' and take the first one, so the order in the sheet is the order it posts.

Matching by video_key, not row number

Early on I matched updates by row position, and it went wrong the first time someone (me) reordered rows while a run was in flight. Now every match โ€” reading which row is next, writing back that it published โ€” goes through video_key, a stable string I set once per video. Row position drifts. Keys don't.

The part that actually needs the Graph API

Facebook Page posting is a straightforward POST /{'{page-id}'}/videos with a file_url and a description. Instagram is a two-step dance: first POST /{'{ig-id}'}/media with media_type: REELS, the video URL, and a caption, which hands back a creation_id โ€” then a second call, POST /{'{ig-id}'}/media_publish, to actually publish it. In between, I wait 45 seconds flat rather than polling the container's processing status. That's an honest shortcut: it works fine for short 15-20 second clips, but a longer video would need real status polling instead of a fixed wait โ€” I just haven't needed it yet for what I'm posting.

The gotcha nobody mentions: video hosting

Both Graph API endpoints fetch the video by URL server-side โ€” there's no simple direct-binary-upload path through this flow. So the video needs to already be sitting somewhere with a real public HTTPS URL before either request goes out. I didn't stand up a separate video host for this: the finished MP4s just get copied into the same nginx docroot that already serves the rest of the site, so they're immediately reachable at a normal URL. One less moving part.

Closing the loop

Once both platforms confirm, a Merge node combines the results, the Sheet row flips to published with a timestamp, and a Telegram message tells me it happened โ€” or, more usefully, tells me when one side succeeded and the other didn't.

Update: what the workflow looks like now

The version described above was the first one. I re-read the current export of the same publisher (it now has 24 nodes and posts to four platforms) and three things changed or are worth knowing.

Two details that matter for the Graph API calls

The Instagram container is created with media_type: REELS, a video_url and a caption, and the publish call sends only the creation_id returned by the first call. On the Threads side the caption expression cuts the text to 500 characters, which is the limit covered in the Threads 500-character article.

How to check that a run really worked

Do not trust a green execution. Open the run and look at the output of the two publish calls: each should contain an id. Then confirm the sheet row shows published with a date, and open the post on each platform once.

Need this wired up for your own pages?

Content-queue-to-social-media is one of the most common builds I do โ€” happy to set it up against your own Sheet and accounts.