DEV Community

Perufitlife
Perufitlife

Posted on Originally published at postwire.io

How to post one video to TikTok, Instagram Reels and YouTube Shorts from n8n (works on n8n Cloud)

Disclosure: I'm Renzo, and I build PostWire, the posting API this tutorial uses. It has a free plan, and the workflow below runs on it. If you'd rather call each network's API yourself, the first section explains what that involves.

You have a vertical video and you want it on TikTok, Instagram Reels and YouTube Shorts, posted from n8n, ideally with a caption that suits each network instead of one caption pasted three times.

n8n has no core node that publishes to TikTok. Calling each network's API yourself means:

  • TikTok: a developer app with the Content Posting API. Until TikTok audits it, it can only post privately (SELF_ONLY), to at most 5 users in 24 hours, and those accounts must be private.
  • Instagram: a Meta app that has been through App Review, then for each post you create a media container, poll until the video has processed, and publish it.
  • YouTube: a Google Cloud project, with Google verification of the youtube.upload scope before strangers can authorize it.

The shortcut is to call an API that already has those approvals. Below, that means four nodes and two HTTP requests. They only use the core HTTP Request node, so the workflow runs on n8n Cloud too, where community nodes that n8n hasn't verified can't be installed.

What you'll build

Manual Trigger → Set (idea, video, networks) → HTTP Request: /api/generate → HTTP Request: /api/post
Enter fullscreen mode Exit fullscreen mode
  1. You give it one idea and one video link.
  2. /api/generate writes a TikTok caption, an Instagram caption, and a YouTube title, description and tags. Nothing is published at this step.
  3. /api/post publishes the video to all three networks in one call, each with its own text, and returns one result per network.

I ran this exact workflow on Oct 1, 2026 in n8n 2.40.7 against the live API. The output further down comes from that run.

Before you start

  • n8n (Cloud or self-hosted).
  • A free PostWire account: one brand, 30 posts a month, no card. One video posted to three networks counts as 3 posts.
  • A vertical MP4 at a public URL that points straight at the file. A Google Drive or Dropbox share link won't work, because it opens a web page, not the file. (If your video isn't public, see "No public URL?" below.)

Step 1: Connect the three accounts

Create the account at postwire.io, open Accounts, press Connect on TikTok, Instagram and YouTube, and sign in on each network. Instagram needs a Business or Creator account; "Log in with Instagram" works without a Facebook Page. You don't need a TikTok, Meta or Google developer app of your own.

Then open API & MCP and press Copy key. The key starts with pw_live_.

Step 2: One credential in n8n

Credentials → Add credential → Header Auth:

  • Name: Authorization
  • Value: Bearer pw_live_your_key

Both HTTP Request nodes use it.

Step 3: The input node

Add a Set node called Your idea, video and platforms with four fields:

Name Type Value
idea String Announce that our AI meal planner now builds a full week of recipes from one photo of your fridge. Friendly, build-in-public tone.
video_url String https://example.com/your-vertical-video.mp4
platforms Array {{ ["tiktok", "instagram", "youtube"] }}
brand_voice String Plain language, first person, no hype, at most one emoji.

In a real workflow these come from the previous node: a new file in a folder, a row marked "ready" in a sheet, or the output of an AI video step.

Step 4: Write a caption per network

Add an HTTP Request node:

  • Method: POST
  • URL: https://postwire.io/api/generate
  • Authentication: Generic Credential Type → Header Auth → the credential from step 2
  • Send Body: on, Body Content Type JSON, Specify Body Using JSON:
{{ JSON.stringify({ prompt: $json.idea, platforms: $json.platforms, media_url: $json.video_url, brand_voice: $json.brand_voice }) }}
Enter fullscreen mode Exit fullscreen mode

It takes 10–30 seconds. Here is what came back in my run (YouTube trimmed):

{
  "drafts": {
    "tiktok": {
      "text": "One photo of your fridge → a full week of recipes. No more \"what do I even make?\" 😅 We just shipped this in our meal planner and I'm honestly still a bit shocked it works this well. #mealplanning #AIcooking #buildinpublic #mealprep #foodtech Drop a 🙋 if you want early access.",
      "title": "We taught our AI to read your fridge 🧠"
    },
    "instagram": {
      "text": "One fridge photo. Seven days of recipes. 📸 We just shipped this — save this if you want to try it.\n\n#mealplanning #buildinpublic #AIcooking #mealprep #foodtech"
    },
    "youtube": {
      "text": "We just shipped a feature I've been wanting to build for a long time — our AI meal planner can now generate a full week of recipes f…",
      "title": "…",
      "tags": ["…"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The three texts really are different. They're also not perfect: the brand voice asked for "at most one emoji" and the TikTok caption has two. Read the drafts before you publish anything unattended. The free plan allows 15 of these generations a day.

Step 5: Publish to all three

Add a second HTTP Request node with the same method, credential and body settings, URL https://postwire.io/api/post, and this body:

{{ JSON.stringify({ platforms: $('Your idea, video and platforms').item.json.platforms, video_url: $('Your idea, video and platforms').item.json.video_url, per_platform: $json.drafts, options: { tiktok: { privacy_level: 'SELF_ONLY' } } }) }}
Enter fullscreen mode Exit fullscreen mode

per_platform is the drafts object from step 4, passed back unchanged. Each network gets its own text, and YouTube gets the title and tags too.

About privacy, before you go live. This body posts the TikTok video as private (SELF_ONLY) so a first test isn't public. YouTube uploads with no privacy value are private too. Instagram has no private mode: the Reel is public as soon as it posts. When you're ready, replace the options line with privacy: 'public', which makes both TikTok and YouTube public.

Step 6: Read the result

The answer has posted (how many networks published) and results, one per network. On success, each result has ok: true and the network's id. YouTube also returns a youtu.be link. TikTok returns status: "processing", because TikTok keeps processing the video after upload. GET https://postwire.io/api/post/status?platform=tiktok&id=<id> tells you when it's live.

My test account had no networks connected, so this is what my run returned. It also shows what a failure looks like:

{
  "posted": 0,
  "results": [
    {
      "ok": false,
      "platform": "tiktok",
      "error": "tiktok is not connected — open Accounts in the dashboard and connect it, then post again",
      "code": "not_connected",
      "what": "TikTok was not connected to your account when the post was sent.",
      "fix": "Connect TikTok in Accounts, or leave it out of the post. Only the networks you have connected can be published to."
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

(Instagram and YouTube returned the same.) One network failing doesn't stop the others. Check posted, not just the HTTP status: the response is a 200 even when a network fails.

Making it safe to run unattended

  • Retries don't double-post. Add idempotency_key to the publish body, set to something unique per video (the file id, the sheet row). A retry with the same key is refused for 24 hours with 409 duplicate_post. That matters if you turn on n8n's "Retry On Fail". Without a key, an identical payload is still refused for 2 minutes. If nothing went out, the key is released, so a real retry still works.
  • Swap the trigger. Replace the Manual Trigger with a Google Drive, Google Sheets or Schedule trigger, and map idea and video_url from it.
  • Publish later. POST the same body plus run_at (ISO 8601 with offset, e.g. 2026-10-09T09:00:00-05:00) to https://postwire.io/api/schedule. The connections and media are checked when you schedule, not at 7 a.m. when the post is due.

No public URL?

If the video is a binary item in n8n (from a Drive download, an AI video node, a form upload), use three more HTTP Request nodes:

  1. POST https://postwire.io/api/media/upload-url with {"content_type": "video/mp4", "size_bytes": <size>} returns an upload_url and a path.
  2. PUT to that upload_url, Body Content Type n8n Binary File, field data. No auth header: the URL is signed.
  3. GET https://postwire.io/api/media/finalize?path=<path> (with the credential) returns media_url, which you use as video_url.

Through n8n that path takes files up to 50 MB. From a script the API accepts up to 1 GB in parts.

Limits, honestly

  • TikTok, Instagram and YouTube never take a text-only post. They need a video (or photos for TikTok and Instagram).
  • PostWire covers TikTok, Instagram, YouTube, LinkedIn (personal profiles), Facebook Pages, Bluesky, Mastodon, Telegram and Discord. Not X, Threads, Reddit or Pinterest. If you need those, use another tool.
  • PostWire's own n8n community node (n8n-nodes-postwire, with a "Smart Distribute" option that does steps 4 and 5 in one node) isn't verified by n8n yet, so it installs on self-hosted n8n only. That's why this tutorial uses plain HTTP Request nodes.
  • PostWire publishes the file you give it. It doesn't edit or clip videos.

The importable workflow JSON, plus versions that start from Google Sheets, OneDrive and Veo 3.1, are in the Perufitlife/postwire-examples repository on GitHub. The full guide, with a comparison against Blotato, Upload-Post and calling TikTok's API directly, is here: n8n: post to TikTok, Instagram and YouTube in one node.

Top comments (0)