DEV Community

Grewup
Grewup

Posted on

N8N Workflow That Auto-Creates and Uploads YouTube Videos While You Sleep

What this builds

A fully automated faceless YouTube content pipeline. You add a topic to a Google Sheet.

The workflow:

Reads the topic
Uses GPT-4 to write an intro, outro, and full Top 10 script with image prompts
Sends the full structured output to JSON2Video API to render the video
Polls the render status until complete
Downloads the finished MP4
Uploads it to YouTube
Updates your Google Sheet with the video URL and status
Enter fullscreen mode Exit fullscreen mode

Zero manual steps after initial setup. Run it for one video, ten videos, or schedule it daily.

Architecture overview


`Google Sheets (topic: "to-do")
        ↓
Intro & Outro Agent (GPT-4 via OpenRouter)
        ↓
Rankings Agent (GPT-4 — 10 items with voiceover + image prompts)
        ↓
Generate Video (JSON2Video API — POST)
        ↓
Wait 150s → Get Video status → Switch (done / running / error)
        ↓ [loop until done]
Download MP4 (HTTP Request → File)
        ↓
Upload to YouTube
        ↓
Update Google Sheet (status + video URL)`
Enter fullscreen mode Exit fullscreen mode

Step 1 — Google Sheet setup

Create a new Google Sheet with these column headers:

Subject | Creation Status | Posting Status | Video URL
Add your topics in the Subject column. Set Creation Status to to-do for each.
Example rows:
Top 10 Cities in Europe | to-do | to-do |
Top 10 Classic Movie Characters| to-do | to-do |
Top 10 Breakfast Meals | to-do | to-do |
Why this structure matters: The first n8n node filters for rows where Creation Status equals to-do and returns only the first match. This prevents re-processing completed videos and controls workflow execution to one video at a time.

Step 2 — Google Sheets node (trigger)

Add a Google Sheets node to your n8n canvas.
Configuration:

Operation: Get Rows
Filter: Creation Status = "to-do"
Limit: 1 (return only first matching row)

Enable "Return only the first matching row" — this is the throttle that keeps the workflow from trying to process your entire backlog in one run.
Auth: Connect your Google account via OAuth inside n8n credentials. You may need to enable the Google Sheets API in your Google Cloud Console — check the Google Cloud documentation if the connection fails.

*Step 3 — Intro & Outro Agent
*

Add an AI Agent node (or OpenAI node via OpenRouter). This node generates four outputs in one call:

intro_text — the spoken intro script
intro_image — image prompt for the intro visual
outro_text — the spoken outro script
outro_image — image prompt for the outro visual

The structured output is critical. Enable "Require specific output format" and define the JSON schema. Without this, GPT returns one text blob that is impossible to parse cleanly downstream.

System prompt:

You are a professional YouTube scriptwriter creating faceless Top 10 videos.
Write engaging, energetic scripts in a countdown style.
Always return valid JSON with exactly these four keys:
intro_text, intro_image, outro_text, outro_image

User message:

Create intro, outro, and image prompts for a YouTube video titled:
"{{ $json.Subject }}"

Enter fullscreen mode Exit fullscreen mode

Return only clean JSON, no backticks, no explanation.
Model: gpt-4o-mini via OpenRouter for cost efficiency (~$0.001 per call).

Step 4 — Rankings Agent

Add a second AI Agent node. This generates the full Top 10 list.
For each of the 10 items it must produce:

rank — number (10 down to 1)
title — the item name
voiceover — 2–3 sentence spoken description
image_prompt — visual description for image generation
lower_third — on-screen text (e.g. "Number 8: James Bond")

System prompt:

You are a YouTube Top 10 video scriptwriter.
Create ranked lists that are engaging and surprising.
Return a JSON array of exactly 10 objects, each with these keys:
rank, title, voiceover, image_prompt, lower_third
Count DOWN from 10 to 1 (most anticipated = #1).
Return only clean JSON, no backticks.

User message:

Create a complete Top 10 ranking for: "{{ $json.Subject }}"
Execute the node and verify the output is a clean JSON array of 10 objects. If GPT returns a wrapped object, adjust your prompt to specify "return a raw JSON array, no wrapper object."

Step 5 — Generate Video (JSON2Video API)

Add an HTTP Request node to send your structured content to JSON2Video for rendering.
Method: POST
URL: https://api.json2video.com/v1/movies
Headers: x-api-key: [your JSON2Video key]
Body: JSON (build from previous nodes' output)
JSON2Video takes your intro, outro, and 10 ranked items and maps them to their video template engine. The API returns a project_id immediately — the video is not yet rendered, just queued.
Save this project_id — you need it in Step 6.

Step 6 — Polling loop (wait → check → decide)

Video rendering takes 2–10 minutes depending on length. You cannot just wait a fixed time — you need to check status and loop.
Node sequence:
Wait node (150 seconds)

HTTP Request → GET https://api.json2video.com/v1/movies/{{ $json.project_id }}

Switch node
→ status = "done" → continue to download
→ status = "running" → Wait node (10 seconds) → loop back to GET
→ status = "preparing"→ Wait node (10 seconds) → loop back to GET
→ status = "error" → Update Sheet with "error" → stop
Switch node configuration:
Condition 1: {{ $json.status }} equals "done" → output 1
Condition 2: {{ $json.status }} equals "running" → output 2
Condition 3: {{ $json.status }} equals "preparing" → output 2
Default: → output 3 (error path)
The 150-second initial wait prevents unnecessary API calls during the first render phase. The 10-second loop interval after that keeps you from hammering the API.

Step 7 — Download the MP4

Once status is done, the API response contains a video_url.
Add an HTTP Request node:
Method: GET
URL: {{ $json.video_url }}
Response format: File
The "Response format: File" setting is the same critical step from the Instagram automation workflow — without it you get JSON metadata instead of binary video data. n8n will hold the MP4 in memory as binary data, ready to pass directly to the YouTube upload node.

Step 8 — YouTube upload

Add a YouTube node:
Operation: Upload Video
Title: {{ $('Google Sheets').first().json.Subject }}
Description: [your template description]
Privacy: unlisted (change to public after review)
Binary data: toggle ON
Connect your YouTube account via OAuth in n8n credentials. You will need to enable the YouTube Data API v3 in Google Cloud Console and add your n8n redirect URI to the OAuth app's authorized URIs.

Step 9 — Update Google Sheet

After the YouTube upload succeeds, the response contains the video ID.
Add a final Google Sheets node:
Operation: Update Row
Match on: Subject = {{ $('Google Sheets').first().json.Subject }}
Update:
Creation Status → "created"
Posting Status → "unlisted"
Video URL → https://youtube.com/watch?v={{ $json.id }}
Now your Sheet tracks every video — topic, status, and direct YouTube link. When you next trigger the workflow, it skips all rows where Creation Status is not to-do.

Running it

Toggle the workflow active. Open your Google Sheet. Add a topic with Creation Status to-do. Manually trigger the workflow once to test.
Full execution takes approximately 5–12 minutes depending on rendering time. Check the Executions tab in n8n to watch each node complete.
For scheduled daily runs: replace the manual trigger with a Schedule Trigger node set to whatever time you want.

**
What tends to break**

JSON2Video returns an error status: Usually a malformed template body. Log the exact POST body you are sending and validate it against JSON2Video's API documentation.
YouTube upload fails with 403: Your OAuth app does not have the youtube.upload scope. Re-authorize in n8n credentials and ensure the scope is included.
Rankings Agent returns 9 or 11 items instead of 10: Add to the prompt: "You MUST return exactly 10 items, no more, no less. Count carefully before returning."
Google Sheets filter stops returning rows: Check that the Creation Status column value is exactly to-do with no trailing space. Case sensitive.

Workflow JSON

The full importable workflow JSON is available on the blog: download here
Import via n8n → Workflows → Import from file.
Building a variation of this? Drop your node count and use case in the comments.

Top comments (1)

Collapse
 
bta_ghost_3973f23df97163c profile image
BTA GHOST

Thanks