How to create viral shorts automation

How to create viral shorts automation

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

Creating viral short-form videos by hand takes hours: filming, editing, writing captions, uploading. This n8n viral shorts automation replaces every manual step. First, you add a subject to a Google Sheet. Next, the workflow generates a hyper-realistic AI image with Kie AI, turns it into a motion video using Google Veo 3, writes an optimized YouTube title and hashtags with Claude AI, and publishes the final video directly to your YouTube channel. As a result, you get a fully automated content pipeline that runs without you touching a single tool.

Prefer to skip the build? Grab the ready-made template and start publishing AI shorts in under 15 minutes.

What You Will Build

  1. You add a subject keyword to a Google Sheet row with Status set to pending, for example, rusted wrench.
  2. An AI agent generates a detailed image prompt: a hyper-realistic macro photo of the rusted object with a hand holding a spray bottle, foam reacting on contact.
  3. A second AI agent turns that image prompt into a video prompt describing the full transformation sequence: rusted to clean chrome in 6 to 9 seconds.
  4. Kie AI generates the image, then uses it as a reference frame to generate the video with Google Veo 3 at 9:16 aspect ratio.
  5. Claude AI writes a YouTube title (under 46 characters) and five hashtags optimized for the subject.
  6. The video binary is downloaded, uploaded to YouTube as a public Short, and added to your playlist.
  7. The Google Sheet row updates to Video Published with the exact publish timestamp.

How This n8n Viral Shorts Automation Works

The workflow uses two parallel polling loops to handle AI generation reliably. Image generation and video generation both take time, sometimes 30 seconds, sometimes several minutes. Instead of timing out, each loop checks the API status every 30 seconds and routes on the result: success moves forward, still generating loops back, and failure writes an error to the sheet.

┌────────────────────────────────────────────────────────────────────┐ │ N8N VIRAL SHORTS AUTOMATION │ │ │ │ [Manual Trigger] │ │ │ │ │ ▼ │ │ [Google Sheets: Get pending row] │ │ │ │ │ ▼ │ │ [AI Agent: Image Prompt] ←── OpenRouter Chat Model │ │ │ │ │ ▼ │ │ [AI Agent: Video Prompt] ←── OpenRouter Chat Model 1 │ │ │ │ │ ▼ │ │ [Kie AI: Generate Image] ── POST /jobs/createTask │ │ │ │ │ ┌─────▼──────────────────────────────────────────┐ │ │ │ IMAGE POLLING LOOP (every 30s) │ │ │ │ [Get Image URL] → [Switch2] │ │ │ │ success → [Update row: Image URL] │ │ │ │ waiting → [Wait 30s] → loop │ │ │ │ generating → [Wait 30s] → loop │ │ │ │ fail → [Update IMG status: Failed] │ │ │ └────────────────────────────────────────────────┘ │ │ │ (success only) │ │ ▼ │ │ [Kie AI: Generate Video] ── POST /veo/generate (Veo3 Fast) │ │ │ │ │ ┌─────▼──────────────────────────────────────────┐ │ │ │ VIDEO POLLING LOOP (every 30s) │ │ │ │ [Get Video] → [Switch] │ │ │ │ successFlag=1 → [Update Video Url] │ │ │ │ successFlag=0 → [Wait 30s] → loop │ │ │ │ successFlag=2 → [Wait 30s] → loop │ │ │ │ successFlag=3 → [Update Video status: Failed]│ │ │ └────────────────────────────────────────────────┘ │ │ │ (success only) │ │ ▼ │ │ [OpenRouter: Generate caption + hashtags] │ │ │ │ │ ▼ │ │ [Code: Separate Title from Hashtags] │ │ │ │ │ ▼ │ │ [Google Sheets: Update Video Title + Hashtags] │ │ │ │ │ ▼ │ │ [HTTP Request: Download video binary] │ │ │ │ │ ▼ │ │ [YouTube: Upload video] → [Wait 10s] → [Add to Playlist] │ │ │ │ │ ▼ │ │ [Google Sheets: Update Youtube Status + timestamp] │ └────────────────────────────────────────────────────────────────────┘

What You Will Need

  • n8n: self-hosted or n8n Cloud (v1.30+). See the n8n hosting documentation for setup options.
  • Kie AI account: for image generation (nano-banana-pro model) and video generation (Veo3 Fast). See the Kie AI API documentation.
  • OpenRouter account: to call Claude 3.7 Sonnet for prompt generation and caption writing.
  • Google Sheets: one spreadsheet with the 7-column schema shown below.
  • YouTube channel: with a YouTube Data API v3 OAuth2 credential and a playlist ID to publish into.
  • Google account: connected in n8n via OAuth2 for Google Sheets access.

Estimated build time: 60 minutes from scratch, or under 15 minutes with the ready-made template.

Part 1: Picking the Subject and Generating Prompts

Step 1: Manual Trigger and Get Pending Row

The workflow starts with a Manual Trigger, you click Execute in n8n when you want to publish a new Short. First, it reads the Google Sheet and finds the first row where Status = pending. That row contains one field: objects, a plain text subject like rusty bolt, corroded pipe fitting, or old iron padlock. This single word drives everything that follows.

// Google Sheets row — what the workflow reads
{
  "row_number": 3,
  "objects": "rusted wrench",
  "Image url": "",
  "Video Url": "",
  "Status": "pending",
  "Video Title": "",
  "Video Hashtags": "",
  "Youtube Status": ""
}

Tip: You can batch-add subjects to the sheet in advance. The workflow picks exactly one row per execution, so running it once per day keeps a steady publishing cadence without any extra configuration.

Step 2: Image Prompt Agent

The first AI Agent node receives the object name and generates a detailed image prompt. The system prompt enforces a very specific visual style: ultra-realistic macro photography, a rusted object with thick orange corrosion, one adult human hand holding a clear plastic spray bottle with blue liquid, the trigger actively pressed, and a foam reaction forming where the spray hits. These constraints are non-negotiable in the prompt. They exist to produce consistent, high-quality results that look real rather than generated.

For example, given the input rusted wrench, the agent returns a prompt like:

Ultra-realistic macro photograph of an extremely rusted adjustable wrench on a concrete 
workshop floor. Natural daylight, shallow depth of field. Thick orange corrosion, deep 
pitted metal surface, rust flakes visible. One realistic adult human hand holding a clear 
plastic spray bottle with blue cleaning liquid. Trigger spray head visible, finger pressing 
trigger, spray stream connected from nozzle to wrench surface. White-blue foam building on 
contact. Hyper-realistic 4K, cinematic macro shot. No logos, no text.

Step 3: Video Prompt Agent

The second AI Agent takes the image prompt as input and generates a video prompt. The video prompt describes motion, not a static scene. It specifies the same hand, bottle, location, and object, then adds a mandatory transformation sequence: fully rusted object shown, hand enters frame, trigger pressed, spray hits, foam builds rapidly, rust dissolves gradually, clean chrome finish revealed, water droplets drip. This sequence is what makes the videos satisfying to watch and naturally viral.

Note: The system prompt explicitly bans jump cuts and magical transitions. Veo 3 can produce unrealistic glowing effects if the prompt doesn’t constrain it. The “gradual dissolving” instruction is what keeps the physics-based animation believable.

Part 2: Generating the Image with Kie AI

Step 4: Generate Image (Kie AI)

This node posts to the Kie AI /jobs/createTask endpoint. It uses the nano-banana-pro model (Kie’s image generation model) with a 9:16 aspect ratio (vertical, for Shorts), 1K resolution, and PNG output. The image prompt from the previous agent goes directly into the input.prompt field. In return, Kie AI gives you a taskId which the polling loop uses to check completion status.

// Generate Image request body
{
  "model": "nano-banana-pro",
  "callBackUrl": "YOUR_CALLBACK_URL",
  "input": {
    "prompt": "{{ $('Image Prompt').item.json.output }}",
    "aspect_ratio": "9:16",
    "resolution": "1K",
    "output_format": "png"
  }
}

// Kie AI response
{
  "data": { "taskId": "img_task_abc123" }
}

Step 5: Image Polling Loop (Get Image URL + Switch2)

Generating an image takes between 15 and 90 seconds depending on server load. Instead of waiting a fixed amount of time and hoping it’s done, the workflow polls every 30 seconds. The Get Image URL node calls /jobs/recordInfo?taskId=... and the Switch2 node reads $json.data.state. There are four routes: success moves forward and saves the image URL to the sheet, waiting and generating both loop back through a 30-second Wait node, and fail writes “Failed To Generate” to the sheet and stops.

On success, the image URL is extracted from JSON.parse($json.data.resultJson).resultUrls[0] and written to the Image url column. This URL becomes the reference frame for the video generation step.

Tip: You can monitor generation progress in real time by opening your Kie AI dashboard while the workflow runs. The status field cycles through waiting, generating, then success, exactly matching the Switch2 routes.

Part 3: Generating the Video with Veo 3

Step 6: Generate Video (Kie AI Veo3 Fast)

With the image URL saved, the workflow posts to Kie AI’s /veo/generate endpoint. The request sends the video prompt, the generated image URL as a reference frame, and specifies veo3_fast as the model with a 9:16 vertical aspect ratio. The generationType is REFERENCE_2_VIDEO, which tells Veo 3 to animate from the reference image rather than generating from text alone. As a result, the video visually matches the generated image, keeping the object and hand consistent.

// Generate Video request body
{
  "prompt": "{{ $('Video Prompt').item.json.output }}",
  "imageUrls": ["{{ $json['Image url'] }}"],
  "model": "veo3_fast",
  "aspect_ratio": "9:16",
  "seeds": 12345,
  "generationType": "REFERENCE_2_VIDEO"
}

Note: The seeds value of 12345 is fixed in the template. Using a consistent seed helps produce repeatable visual styles across different subjects. Change it to a random value if you want more visual variety between videos.

Step 7: Video Polling Loop (Get Video + Switch)

Video generation takes longer than image generation, typically 2 to 5 minutes. The video polling loop uses the same pattern as the image loop but reads $json.data.successFlag as a number instead of a state string. A value of 1 means success, 0 means still generating, 2 is another in-progress state, and 3 means generation failed. On success, the video URL is extracted from data.response.resultUrls[0] and written to the sheet with Status = Video Generated.

Part 4: Writing the Caption and Publishing to YouTube

Step 8: Video Caption (OpenRouter + Claude)

Once the video URL is saved, the workflow calls OpenRouter with the object name as a keyword. Claude 3.7 Sonnet acts as a social media copywriter and returns a YouTube title (maximum 46 characters) followed by five hashtags separated by a blank line. No labels, no emojis, no markdown bold, just clean copy ready for use as a video title and description.

Step 9: Separate Title From Tags (Code Node)

The Code node splits Claude’s response into two separate fields using a blank line as the delimiter. The first block becomes Title and the second becomes hashtags. Both fields are then written to the Google Sheet in the Video Title and Video Hashtags columns.

const content = $json.choices[0].message.content || "";

// Split on blank line between title and hashtags
const parts = content.split(/\n\s*\n/);

const title    = parts[0] ? parts[0].trim() : "";
const hashtags = parts[1] ? parts[1].trim() : "";

return [{ json: { Title: title, hashtags: hashtags } }];
// Resulting fields written to Google Sheets
{
  "Title": "Rust Remover Works Instantly on Old Wrench",
  "hashtags": "#rustremoval #rustcleaning #satisfyingvideo #restoration #aivideo"
}

Step 10: Download Video Binary and Upload to YouTube

An HTTP Request node downloads the video binary from the Kie AI CDN URL stored in the sheet. The YouTube Upload node then receives the binary directly, using the saved Video Title as the title and Video Hashtags as the description. The video publishes as public, notifies subscribers, and is tagged with a fixed set of niche-relevant tags covering rust removal, AI video, ASMR, and satisfying content. After a 10-second wait, the Add to Playlist node links the uploaded video to your channel’s Shorts playlist.

Tip: The YouTube category is set to 22 (People and Blogs) in the template. For a rust cleaning channel, category 26 (Howto and Style) may rank better in search. Change the categoryId value in the Upload node parameters.

Step 11: Update YouTube Status in Google Sheets

Finally, the Add to Playlist response includes contentDetails.videoPublishedAt, the exact UTC timestamp when the video went live. The last Google Sheets node writes this to the Youtube Status column as Video Published 2026-03-18T14:30:00Z. This gives you a complete audit trail in the sheet: object name, image URL, video URL, title, hashtags, and publish time, all in one row.

The Data Structure: Google Sheets Schema

Create a spreadsheet named Viral with one tab named Sheet1. The column names below are case-sensitive, and the workflow uses exact string matching on all update operations.

Column NameTypeExample ValueDescription
objectsTextrusted wrenchThe subject keyword you provide. Drives all AI prompt generation.
Image urlTexthttps://cdn.kie.ai/…/output.pngFilled automatically after image generation succeeds. Set to “Failed To Generate” on failure.
Video UrlTexthttps://cdn.kie.ai/…/video.mp4Filled automatically after video generation succeeds. Set to “Failed To Generate” on failure.
StatusTextpending / Video GeneratedSet to pending by you. Updated to Video Generated by the workflow after the video URL is saved.
Video TitleTextRust Remover Works Instantly on Old WrenchAI-generated YouTube title, max 46 characters.
Video HashtagsText#rustremoval #rustcleaning …Five AI-generated hashtags, used as the YouTube video description.
Youtube StatusTextVideo Published 2026-03-18T14:30:00ZWritten automatically when the video is added to your YouTube playlist.

Note: Start each new subject by adding a row with the objects field filled in and Status set to pending. Leave all other columns empty, and the workflow fills them in automatically as it runs.

Full n8n Viral Shorts Automation Flow: End to End

You add a row: { objects: “rusty bolt”, Status: “pending” } │ ▼ [Manual Trigger] → [Google Sheets: Get pending row] │ ▼ [Image Prompt Agent] → “Ultra-realistic macro photo of extremely rusted bolt…” │ ▼ [Video Prompt Agent] → “Hand enters frame, spray hits bolt, foam builds, rust dissolves…” │ ▼ [Kie AI: Generate Image] → { taskId: “img_abc123” } │ ┌──────▼──────────────────────────────────────────────┐ │ POLL every 30s: GET /jobs/recordInfo?taskId=… │ │ state=waiting/generating → wait 30s → poll again │ │ state=success → Image URL saved to Sheet │ │ state=fail → “Failed To Generate” → STOP │ └──────────────────────────────────────────────────────┘ │ (success) ▼ [Kie AI: Generate Video] → uses image as reference frame │ { taskId: “vid_xyz789” } ┌──────▼──────────────────────────────────────────────┐ │ POLL every 30s: GET /veo/record-info?taskId=… │ │ successFlag=0 or 2 → wait 30s → poll again │ │ successFlag=1 → Video URL saved, Status updated │ │ successFlag=3 → “Failed To Generate” → STOP │ └──────────────────────────────────────────────────────┘ │ (success) ▼ [OpenRouter/Claude: Write title + hashtags] │ [Code: Split title from hashtags] │ [Google Sheets: Save Video Title + Video Hashtags] │ [HTTP Request: Download video binary from CDN] │ [YouTube: Upload video (public, title, description, tags)] │ [Wait 10s] → [YouTube: Add to Playlist] │ [Google Sheets: Update Youtube Status with publish timestamp] │ ▼ Sheet row: { objects: “rusty bolt”, Image url: “https://cdn.kie.ai/…/output.png”, Video Url: “https://cdn.kie.ai/…/video.mp4”, Status: “Video Generated”, Video Title: “Rust Vanishes Off Old Bolt in Seconds”, Video Hashtags: “#rustremoval #aivideo #satisfying …”, Youtube Status: “Video Published 2026-03-18T14:30:00Z” }

Testing Your n8n Viral Shorts Automation Workflow

  1. Add one row to your Google Sheet with objects = rusty screwdriver and Status = pending. Leave all other columns empty.
  2. In n8n, open the workflow and click Execute workflow. Watch the execution panel, nodes should light green in sequence.
  3. After the Image Prompt and Video Prompt agents run, check the output panel to confirm both prompts contain the mandatory elements: hand, spray bottle, foam, transformation sequence.
  4. After the image polling loop completes, check your Google Sheet. The Image url column should contain a valid Kie AI CDN URL ending in .png.
  5. Wait for the video polling loop to finish (2 to 5 minutes). Confirm Video Url contains an MP4 URL and Status shows Video Generated.
  6. After the caption step, verify Video Title is under 46 characters and Video Hashtags contains exactly five hashtag terms.
  7. After the YouTube upload, open your YouTube Studio and confirm the video appears as a public Short with the correct title, description, and playlist assignment.
  8. Confirm the Youtube Status column shows Video Published with a timestamp.
ProblemLikely CauseFix
Image polling never exits the loop Kie AI task is stuck or the API key has no image generation credits Check your Kie AI dashboard for the task status. Verify your API key has sufficient credits for the nano-banana-pro model.
Image URL saved as undefined The resultJson field is not JSON-parseable in some API responses Add a try-catch around the JSON.parse in the Update row expression, or use $json.data.resultUrls[0] as a fallback path.
Video generation fails immediately (successFlag=3) The image URL passed to Veo 3 is not publicly accessible Open the image URL from the sheet in a browser. If it requires authentication, switch to Kie AI’s public CDN delivery option in your account settings.
YouTube upload returns 403 Forbidden OAuth2 token expired or YouTube Data API not enabled in Google Cloud Console Reconnect the YouTube OAuth2 credential in n8n. In Google Cloud Console, confirm the YouTube Data API v3 is enabled for your project.
Title is longer than 46 characters Claude occasionally exceeds the character limit on certain keywords Add a character truncation step after the Code node: title.substring(0, 46). Alternatively, strengthen the system prompt by adding “The title MUST be 46 characters or fewer, count carefully.”
Google Sheet not updating Column name mismatch, the field names are case-sensitive Verify the headers match exactly: objects, Image url, Video Url, Status, Video Title, Video Hashtags, Youtube Status.

Frequently Asked Questions

Can I use this n8n viral shorts automation for subjects other than rusted objects?

Yes, but the AI prompt agents are currently tuned for the rust-cleaning niche specifically. The system prompts mention spray bottles, foam, corrosion, and chrome finishes. To use a different niche, for example, before-and-after car detailing or dirty sneaker cleaning, rewrite the Image Prompt and Video Prompt system prompts to describe your subject’s transformation sequence. The rest of the workflow is fully niche-agnostic and does not need changes.

How much does it cost to generate one video?

Costs depend on your Kie AI plan and OpenRouter usage. Image generation with nano-banana-pro typically costs a few cents per image. Video generation with Veo3 Fast is more expensive, around $0.10 to $0.30 per video depending on length. Claude 3.7 Sonnet via OpenRouter adds less than $0.01 per caption. For a channel publishing one video per day, total monthly API costs are typically under $15.

Why does the workflow use a manual trigger instead of a schedule?

The manual trigger is the safest starting point. Video generation can take 2 to 5 minutes, and if a scheduled trigger fires while a previous execution is still running, you can end up with duplicate uploads. Once you are confident the workflow runs cleanly end to end, replace the Manual Trigger with a Schedule Trigger set to fire once per day at a time that suits your posting schedule.

What happens if image generation fails?

The Switch2 node routes the fail state to an Update IMG status node, which writes “Failed To Generate” to the Image url column. The workflow then stops and does not proceed to video generation. The row remains in the sheet with its original Status = pending, so you can manually re-trigger the workflow to retry. For production use, consider changing the status to img_failed and adding a new filter that picks up both pending and img_failed rows.

Can I publish to TikTok or Instagram Reels in addition to YouTube?

The video is generated in 9:16 format, which is correct for all three platforms. After the HTTP Request node downloads the binary, you can add parallel branches: one for YouTube (already built), one for Instagram Reels via the Facebook Graph API, and one for TikTok via their Content Posting API. The binary is the same file, just route it to multiple upload nodes. This is the same pattern used in our Social Media Automation workflow.

How do I change the video model or aspect ratio?

Open the Generate Video node and edit the JSON body directly. Change "model": "veo3_fast" to "veo3" for higher quality at slower speed, or adjust "aspect_ratio" from "9:16" to "16:9" for standard landscape YouTube videos. For landscape videos, also update the Image Prompt system prompt to request a 16:9 aspect ratio, otherwise the reference image and video will have mismatched dimensions.

Get the n8n Viral Shorts Automation Template

Download the ready-to-import workflow JSON and the Google Sheets template with all 7 columns pre-configured, start publishing AI-generated Shorts to YouTube today.

Get the Template

Instant download, works on n8n Cloud and self-hosted

What to Build Next

  • Add a Schedule Trigger: replace the manual trigger with a daily schedule so the workflow publishes one new Short every day without any manual intervention.
  • Expand to multiple niches: duplicate the workflow and change the Image and Video Prompt system prompts for a different transformation niche: car detailing, sneaker cleaning, or power washing.
  • Add Instagram Reels publishing: after the HTTP Request node downloads the binary, add a Facebook Graph API branch to post the same 9:16 video as an Instagram Reel in the same execution.
  • Add a Telegram notification: wire a Telegram node at the end to send yourself a message with the video title and YouTube URL every time a new Short goes live.
  • Browse more automation guides on the EasyWorkflows n8n blog.

How to Automate Social Media Posting Across Instagram, Facebook & YouTube with n8n

How to Automate Social Media Posting Across Instagram, Facebook & YouTube with n8n

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

One workflow, one Google Drive folder, four platforms. Drop a video, walk away. n8n handles the AI caption, the upload, the publish, and the status tracking automatically.

If you’re posting the same video to Instagram Reels, Facebook Reels, YouTube, and TikTok, you already know the drill: download, re-upload, write a caption, find the right tags, publish, check the status, repeat, four times, for every single video. It’s not a content problem. It’s a logistics problem. And logistics problems are exactly what n8n was built to solve.

This workflow watches a Google Drive folder, picks up the next unprocessed video, uses an AI model via OpenRouter to generate a platform-optimized caption with hashtags, uploads the video to Cloudinary as a CDN host, then simultaneously publishes it as a Reel on Instagram, a Reel on Facebook, and a video on YouTube, while staging the data for TikTok in a separate sheet. Every step is tracked in a Google Sheet so your team always knows what’s been published and what’s still pending. When everything is done, the original file is moved to an archive folder so it doesn’t get picked up again.

Prefer to skip the setup? Grab the ready-made template → and be publishing cross-platform in under 20 minutes.

What You’ll Build

  1. A daily schedule trigger (fires at 6 PM) scans your Google Drive folder for the next video waiting to be published.
  2. A Code node strips the file extension and normalizes the filename into a clean public ID (spaces become underscores for Cloudinary).
  3. The video is uploaded to Cloudinary, which provides a stable public CDN URL that all four platforms can download from.
  4. An AI call to OpenRouter (Claude 3.7 Sonnet) reads the filename as a keyword and writes a Facebook-optimized caption with five hashtags, no manual copywriting needed.
  5. The Cloudinary URL and AI caption are written back to your Google Sheet tracking row.
  6. The video is published as a Reel on Facebook (via the Facebook Graph API) and simultaneously as a Reel on Instagram, with 30-second waits built in between API calls to respect rate limits.
  7. The same video is uploaded to YouTube with the AI caption as the description, then added to a playlist.
  8. A separate Google Sheets tab gets a row appended for your TikTok queue (ready for the next manual step or a future TikTok automation).
  9. The main tracking sheet updates the row status to “Video Shared”, and the original Drive file is moved to your archive folder.

How It Works: The Big Picture

┌──────────────────────────────────────────────────────────────────────────────┐
│  SOCIAL MEDIA AUTOMATION (n8n)                                               │
│                                                                              │
│  [Schedule Trigger 6PM] ──► [Google Drive: Search Folder]                   │
│                                        │                                     │
│                                        ▼                                     │
│                              [Code: Clean Filename]                          │
│                                        │                                     │
│                                        ▼                                     │
│                              [Cloudinary: Upload Video]                      │
│                                        │                                     │
│                                        ▼                                     │
│                          [Sheets: Get Pending Row]                           │
│                                        │                                     │
│                                        ▼                                     │
│                         [OpenRouter AI: Generate Caption]                    │
│                                        │                                     │
│                                        ▼                                     │
│                         [Sheets: Write URL + Caption]                        │
│                                        │                                     │
│               ┌────────────────────────┤                                     │
│               │                        │                                     │
│               ▼                        ▼                                     │
│  ── FACEBOOK REELS ──       ── INSTAGRAM REELS ──                           │
│  [Init Upload Session]       [Post Container]                               │
│  [Upload Hosted File]        [Wait 30s]                                     │
│  [Publish Reel]              [Publish to Instagram]                         │
│  [Get Upload Status]         [Wait 30s]                                     │
│  [Wait 30s]                  [HTTP: Fetch Video]                            │
│               │                        │                                     │
│               └────────────────────────┘                                     │
│                                        │                                     │
│                                        ▼                                     │
│                         ── YOUTUBE ──                                        │
│                         [Upload Video]                                       │
│                         [Wait 10s]                                           │
│                         [Add to Playlist]                                    │
│                         [Wait 10s]                                           │
│                         [Sheets: Append TikTok Row]                         │
│                                        │                                     │
│                                        ▼                                     │
│                         [Sheets: Update Status → "Video Shared"]            │
│                         [Google Drive: Move to Archive Folder]              │
└──────────────────────────────────────────────────────────────────────────────┘

What You’ll Need

  • n8n: self-hosted or n8n Cloud (v1.30+)
  • Google Drive: one folder for new videos, one for archived videos
  • Cloudinary account: free tier is sufficient; the Social_Media folder will be created automatically
  • OpenRouter account: free tier works; Claude 3.7 Sonnet is configured by default (swap to any model you prefer)
  • Meta Developer App: with a Facebook Page token and an Instagram Business account linked to that page
  • YouTube Data API v3: OAuth2 credentials connected in n8n; a playlist ID to post into
  • Google Sheets: one spreadsheet with two tabs: one for Instagram/Facebook/YouTube tracking, one for TikTok queue

Estimated build time: 90 to 120 minutes from scratch. Under 20 minutes with the template.

Part 1: Picking Up the Video

1 Schedule Trigger

The workflow fires once a day at 6 PM. You can change this to any hour. Just edit the Trigger At Hour field in the Schedule Trigger node. If you want to post multiple times a day, duplicate the trigger or switch to an interval-based schedule.

// Schedule Trigger output (no data — just fires the workflow)
{}
💡

Tip: To publish a different video every day, just make sure your Drive folder always has the next video waiting before 6 PM. The workflow picks up the first file it finds, processes it, and archives it, so the order files appear in Drive determines your posting schedule.

2 Search Files and Folders (Google Drive)

This node lists files inside your designated Google Drive folder, returning the first result it finds (limit: 1). It fetches all fields, including webContentLink, a direct download URL that Cloudinary will use to pull the video.

// Google Drive file output
{
  "id": "1aBcDeFgHiJkLmNoPqRsTuV",
  "name": "Summer Sale Promo.mp4",
  "webContentLink": "https://drive.google.com/uc?id=1aBcDeFgHiJkLmNoPqRsTuV&export=download",
  "mimeType": "video/mp4",
  "size": "48234567"
}
📌

Important: Set the folder ID to your “pending videos” folder in the Google Drive node. Leave the archive folder ID empty for now. You’ll fill it into the Move file1 node at the end of the workflow.

3 Remove Space & Extensions (Code Node)

Platform APIs and CDNs don’t love filenames with spaces or extensions. This Code node strips the .mp4 (or any extension) from the filename and produces two variants: one with underscores (for Cloudinary’s public_id) and one with the original spaces preserved (for YouTube’s video title, which looks better with natural spacing).

const baseName = $json["name"]
  .replace(/\s*\.[^/.]+$/, "")  // remove file extension
  .trim();

const public_id_underscores = baseName.replace(/\s+/g, "_"); // spaces → underscores
const public_id_no_underscores = baseName;                    // natural spacing

return [{ json: { public_id_underscores, public_id_no_underscores } }];
// Output
{
  "public_id_underscores": "Summer_Sale_Promo",
  "public_id_no_underscores": "Summer Sale Promo"
}

Part 2: Uploading to Cloudinary

4 Upload an Asset from URL (Cloudinary)

Rather than downloading the video to n8n’s memory and re-uploading it, this node passes Cloudinary the Google Drive download link directly. Cloudinary fetches it server-to-server, transcodes it if needed, and stores it in your Social_Media folder with the underscore-normalized name as the public ID. In return, Cloudinary hands you a secure_url, the stable CDN link that all four platforms will pull from.

// Cloudinary upload output (key fields)
{
  "public_id": "Social_Media/Summer_Sale_Promo",
  "secure_url": "https://res.cloudinary.com/your-cloud/video/upload/Social_Media/Summer_Sale_Promo.mp4",
  "resource_type": "video",
  "format": "mp4",
  "duration": 28.4,
  "bytes": 48234567
}
💡

Tip: Cloudinary’s free tier gives you 25 GB of storage and 25 GB of monthly bandwidth, more than enough to test with. Videos are stored permanently so you can reuse the same CDN URL if you ever need to repost.

Part 3: AI Caption Generation

5 Get Row(s) in Sheet (Google Sheets)

This node looks up the first row in your tracking sheet where the Status column is set to pending. It returns all columns for that row, including its row_number, which is used later to update the same row in place rather than creating a duplicate.

// Google Sheets row output
{
  "row_number": 4,
  "video_url": "",
  "Video Caption": "",
  "Status": "pending"
}

6 Video Caption (OpenRouter AI)

This HTTP Request node calls the OpenRouter API, sending the cleaned filename as the keyword. The system prompt tells Claude 3.7 Sonnet to act as a Facebook copywriter and return a Reel title (max 46 characters) followed by five relevant hashtags, no labels, no emojis, no markdown bold formatting. Clean, ready-to-paste copy every time.

// Request body (simplified)
{
  "model": "anthropic/claude-3.7-sonnet",
  "messages": [
    {
      "role": "system",
      "content": "You are a facebook copywriter expert. Follow formatting rules exactly."
    },
    {
      "role": "user",
      "content": "based on this keyword Summer Sale Promo Write an optimized facebook Reel title (max 46 characters), use 5 related hashtags, Do NOT include labels like (Video Reel Title, Hashtags) no emojis, don't use ** in title."
    }
  ]
}
// OpenRouter response
{
  "choices": [
    {
      "message": {
        "content": "Summer Sale — Shop the Best Deals Now\n#SummerSale #ShopNow #SaleAlert #BestDeals #LimitedTime"
      }
    }
  ]
}
💡

Tip: You can swap Claude 3.7 Sonnet for any model on OpenRouter: GPT-4o Mini, Mistral, or Llama 3 all work. For pure caption writing, GPT-4o Mini costs a fraction of a cent per request and produces great results.

7 Update Caption & Video URL (Google Sheets)

Once the AI returns the caption and Cloudinary returns the CDN URL, this node writes both values back to the pending row, matching by row_number. From this point on, every downstream node references these two fields using $('Update caption & vid url').item.json.video_url and $('Update caption & vid url').item.json['Video Caption'].

// Updated row in Google Sheets
{
  "row_number": 4,
  "video_url": "https://res.cloudinary.com/your-cloud/video/upload/Social_Media/Summer_Sale_Promo.mp4",
  "Video Caption": "Summer Sale — Shop the Best Deals Now\n#SummerSale #ShopNow #SaleAlert #BestDeals #LimitedTime",
  "Status": "pending"
}

Part 4: Publishing to Facebook Reels

Facebook’s Reels API uses a three-step upload protocol: initialize a session, upload the file, then publish. n8n handles all three calls in sequence with a 30-second wait before the publish check, giving Facebook’s servers time to process the video before you query the status.

8 Initialize an Upload Session (Facebook Graph API)

Sends a POST to your Facebook Page’s video_reels edge with upload_phase=start and the Cloudinary video URL. Facebook returns a video_id and an upload_url that the next node uses.

// Facebook API response
{
  "video_id": "740128398210437",
  "upload_url": "https://rupload.facebook.com/video-upload/v23.0/740128398210437"
}

9 Upload a Hosted File (HTTP Request)

Posts to the upload_url from the previous step, passing the Cloudinary URL in a custom file_url header. Facebook fetches the video directly from Cloudinary, no binary upload, no memory usage in n8n.

10 Publish The Reel (Facebook Graph API)

Sends the finish signal: upload_phase=finish, video_state=PUBLISHED, the AI-generated description, and the video_id. This is the moment the Reel goes live on your Facebook Page.

11 Get the Upload Status + Wait 30s

Queries the Facebook Graph API to confirm the video’s status field. The 30-second Wait node before Instagram publishing gives Facebook time to fully process the reel and updates internal state before the workflow moves on.

Part 5: Publishing to Instagram Reels

12 Post Container (Facebook Graph API: Instagram)

Instagram’s publishing API (also part of the Facebook Graph API) requires a two-step process. First, create a media container by posting the video URL, caption, and media_type=REELS to your Instagram Business account’s media edge. This returns a container id.

13 Wait 30s → Publish to Instagram

The 30-second wait is mandatory. Instagram needs time to process the video before you can publish the container. After the wait, a second API call to the media_publish edge with the creation_id makes the Reel visible on your Instagram profile.

📌

Note: You need a Facebook Page and an Instagram Business account connected to it under the same Meta Developer App. Personal Instagram accounts cannot use the Reels API.

Part 6: Publishing to YouTube

14 HTTP Request: Fetch Video Binary

This node downloads the video binary from the Cloudinary URL. YouTube’s n8n node expects a binary input (unlike the Facebook API which accepts a URL), so this step bridges the gap: the response body becomes the binary data that the YouTube Upload node reads.

15 Upload a Video (YouTube)

Uploads the binary to YouTube. The title uses the un-modified filename (public_id_no_underscores, natural spaces), the description gets the AI-generated caption, and the video is published as public with notifySubscribers: true. Category 22 is “People & Blogs”. Change this to match your niche.

// YouTube upload output (key fields)
{
  "uploadId": "abc123XYZ_youTubeVideoId",
  "status": { "privacyStatus": "public", "uploadStatus": "uploaded" },
  "snippet": { "title": "Summer Sale Promo", "categoryId": "22" }
}

16 Add to Playlist (YouTube)

After a 10-second wait (gives YouTube time to finish processing the upload), this node adds the video to your designated playlist using the uploadId from the previous step. Set your playlist ID in the node parameters.

Part 7: Tracking and Cleanup

17 Append Row to TikTok Sheet (Google Sheets)

After a second 10-second wait, this node appends a new row to a separate “TikTok” tab in your spreadsheet with the video URL and caption, ready for you to post manually or wire up to a future TikTok API automation.

18 Update Row Status (Google Sheets)

Updates the original tracking row from pending to Video Shared, matched by row_number. This prevents the same video from being picked up again on the next trigger run.

19 Move File to Archive (Google Drive)

Moves the original video file from your “pending” folder to your “archive” folder. The file ID comes from the very first node (Search files and folders). Set your archive folder ID in this node’s parameters.

💡

Tip: If you’d rather delete the file from Drive than archive it, swap this node for a Google Drive Delete node. Archiving is recommended so you have a record of what was posted and can repost if needed.

The Data Structure

Your Google Sheet needs two tabs with these exact column names. The workflow uses string matching on column headers, so capitalization and spacing matter.

Tab 1: instagram / Facebook / Youtube

Column NameTypeExample ValueDescription
video_urlTexthttps://res.cloudinary.com/…/Summer_Sale_Promo.mp4Filled automatically by the workflow after Cloudinary upload
Video CaptionTextSummer Sale, Shop the Best Deals Now
#SummerSale #ShopNow…
AI-generated caption; filled automatically by the workflow
StatusTextpendingVideo SharedSet to “pending” manually when you add a video; updated to “Video Shared” after publishing

Tab 2: Tiktok

Column NameTypeExample ValueDescription
video_urlTexthttps://res.cloudinary.com/…/Summer_Sale_Promo.mp4Same Cloudinary URL, ready for TikTok posting
Video CaptionTextSummer Sale, Shop the Best Deals Now
#SummerSale…
Same AI caption, TikTok hashtag format works identically
📌

Setup step: Before activating the workflow, add one row to Tab 1 with Status = pending and leave video_url and Video Caption blank, the workflow fills those in automatically. Make sure the video file is already in your Google Drive folder.

Full System Flow

New video dropped into Google Drive "Pending" folder
          │
          ▼
[Schedule Trigger: 6PM daily]
          │
          ▼
[Google Drive: Search Folder] → { name: "Summer Sale Promo.mp4", webContentLink: "..." }
          │
          ▼
[Code: Clean Filename] → { public_id_underscores: "Summer_Sale_Promo",
                            public_id_no_underscores: "Summer Sale Promo" }
          │
          ▼
[Cloudinary: Upload Video] → { secure_url: "https://res.cloudinary.com/.../Summer_Sale_Promo.mp4" }
          │
          ▼
[Sheets: Get Pending Row] → { row_number: 4, Status: "pending" }
          │
          ▼
[OpenRouter AI: Generate Caption] → "Summer Sale — Shop the Best Deals Now\n#SummerSale..."
          │
          ▼
[Sheets: Write URL + Caption to Row 4]
          │
    ┌─────┴──────────────────────────────────┐
    ▼                                        ▼
── FACEBOOK ──                       ── INSTAGRAM ──
[Init Upload Session]                [Post Container] → { id: "17867..." }
[Upload Hosted File]                 [Wait 30s]
[Publish Reel]                       [Publish to Instagram]
[Get Upload Status]                  [Wait 30s]
[Wait 30s]                           [Fetch Video Binary (HTTP)]
    │                                        │
    └──────────────────┬─────────────────────┘
                       ▼
                 ── YOUTUBE ──
                 [Upload Video] → { uploadId: "abc123..." }
                 [Wait 10s]
                 [Add to Playlist]
                 [Wait 10s]
                 [Sheets: Append TikTok Row]
                       │
                       ▼
                 [Sheets: Update Status → "Video Shared"]
                 [Google Drive: Move to Archive Folder]
                       │
                       ▼
            ✅ Video live on Facebook, Instagram & YouTube
               TikTok row ready in queue sheet

Testing Your Workflow

  1. Add a short test video (under 60 seconds) to your Google Drive “pending” folder.
  2. Add a row to Tab 1 of your Google Sheet with Status = pending, leave other fields blank.
  3. In n8n, click Test workflow. Watch the execution, look for green checks on each node.
  4. Confirm the Code node output shows both public_id_underscores and public_id_no_underscores.
  5. Check your Cloudinary account to verify the video was uploaded to the Social_Media folder.
  6. Confirm the Google Sheet row now has a video_url and Video Caption filled in.
  7. Check your Facebook Page and Instagram profile for the new Reel (allow 2 to 3 minutes for processing).
  8. Check your YouTube channel for the new video and confirm it was added to the correct playlist.
  9. Verify the TikTok sheet has a new row appended.
  10. Confirm the original Google Drive file has been moved to the archive folder.
ProblemLikely CauseFix
Google Drive returns no files Folder ID is empty or incorrect in the Search node Open the Google Drive node → Filter → Folder ID and enter your “pending” folder’s ID (copy from the Drive URL)
Cloudinary upload fails Google Drive webContentLink requires authentication Make sure the file in Drive is shared as “Anyone with the link can view”, Cloudinary needs a public download URL
OpenRouter returns an error Invalid or expired API key in the HTTP Header credential Check your OpenRouter API key at openrouter.ai/keys and update the n8n credential
Facebook Reel fails: “Invalid node” Facebook Page ID is still the placeholder Replace YOUR_FACEBOOK_PAGE_ID in the Facebook Graph API nodes with your actual Page ID (found in Meta Business Suite → Settings → Page Info)
Instagram publish fails: “The media container is not ready” 30-second wait wasn’t long enough for processing Increase the Wait node before “Upload to instagram” to 60 seconds for larger videos
YouTube upload returns 403 OAuth token expired or insufficient scopes Reconnect the YouTube OAuth2 credential in n8n, ensuring the youtube.upload scope is granted
Google Sheet not updating Column name mismatch (case-sensitive) Confirm your sheet headers match exactly: video_url, Video Caption, Status

Frequently Asked Questions

Do I need a paid Cloudinary plan for this to work?

No. Cloudinary’s free tier supports up to 25 GB of storage and 25 GB monthly bandwidth, which is plenty for most small to medium posting schedules. If you’re posting long-form videos or several per day, upgrade to a paid plan. The secure_url generated for each upload never expires, so your CDN links remain valid indefinitely.

Can I change the AI model that generates captions?

Yes. The “video Caption” node is a standard HTTP Request to OpenRouter. Change the model field in the JSON body to any OpenRouter-supported model, for example, openai/gpt-4o-mini for lower cost or meta-llama/llama-3-70b-instruct for an open-source option. The system prompt and output format stay the same regardless of model.

Why does the workflow use Cloudinary instead of uploading directly from Drive?

Facebook and Instagram’s APIs require a publicly accessible video URL that their servers can download. Google Drive download links require authentication and have rate limits. Cloudinary solves both problems: it fetches the file from Drive during upload and serves it via a clean, public CDN URL that all four platforms can reliably access. It also handles transcoding and format compatibility automatically.

Can this post to TikTok automatically too?

The workflow currently stages TikTok data in a separate sheet tab (video URL + caption) but doesn’t publish directly, because TikTok’s Content Posting API requires business account approval and has stricter OAuth flows. The staged row is ready to wire into a TikTok API node once you have access, or you can use a tool like Buffer or Later to pick up from that sheet.

What happens if the workflow runs and there’s no video in the Drive folder?

The Google Drive “Search files and folders” node will return an empty result set, and the workflow will stop at that point with no error, it simply has nothing to process. No rows will be updated, no API calls will be made. You can optionally add an IF node after the Drive search to send yourself a Telegram or email notification when no videos are found.

Can I post to multiple Facebook Pages or Instagram accounts?

Yes, but you’ll need a separate set of Facebook Graph API nodes for each page or account, each with its own credential. The cleanest approach is to duplicate the Facebook and Instagram sections of the workflow and swap the Page ID and Instagram User ID in the cloned nodes. One n8n workflow can handle multiple accounts in parallel using a Split In Batches node at the top.

🚀 Get the Social Media Automation Template

Download the ready-to-import workflow JSON, the Google Sheets setup template with both tabs pre-configured, and a step-by-step credential guide: everything you need to go from zero to cross-platform posting in under 20 minutes.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Add a Telegram alert: wire a Telegram node at the very end to send yourself a confirmation message with the video title and publish status every time a video goes live.
  • Schedule multiple posts per day: duplicate the workflow and set one trigger for 9 AM and another for 6 PM to double your daily output without any extra manual work.
  • Auto-generate video thumbnails: add a Cloudinary transformation step that extracts a frame from the uploaded video and creates a custom thumbnail you can use as the YouTube cover image.
  • Connect a content calendar: replace the manual “add a file to Drive” step with a Notion or Airtable database where you queue up video ideas, titles, and target dates, and let the workflow pick the next scheduled entry automatically.
  • Add TikTok direct posting: once your TikTok Content Posting API access is approved, add a TikTok node after the “Append row in sheet” step and close the loop on all five platforms in a single run.
n8n Social Media Automation Instagram Reels Facebook Reels YouTube Automation Cloudinary Google Drive OpenRouter AI Caption Generator No Code