n8n tutorial - Lesson 15: AI-Powered YouTube Title SEO with n8n
Hi everyone, in this post we're building a fully automated YouTube title SEO pipeline using n8n — the AI reads your existing video titles, suggests optimized versions with better keywords and CTR, queues them for review in a Google Sheet, then a second workflow pushes approved titles live to YouTube. This is Session 15 of our n8n Workflow Automation Tutorial series, and it's one of the most practical examples of n8n YouTube SEO automation you can build right now.
How to do:
Step 1 — Create the Google Sheet for Title Suggestions
Before building any nodes, set up the review Sheet that acts as the state machine between the two workflows.- Create a new Google Sheet named T5-Title-Suggestions with a tab called Suggestions.
- Add exactly 9 columns in this order:
video_id,video_url,current_title,view_count,published_at,suggested_title,reason,status,created_at. - The
statuscolumn drives the state machine — it accepts four values:pending_review— AI has suggested a title, awaiting human decisionapproved— human approved, ready for the updater workflow to push liverejected— human rejected, the updater workflow skips this rowupdated— title has been successfully pushed to YouTube
- Note your Sheet ID from the URL — you'll paste it into every Google Sheets node in both workflows.
Step 2 — Build Workflow 1: T5-B3-Title-SEO (Schedule + Idempotent Guard)
Create the main workflow named T5-B3-Title-SEO and set up the first three nodes that handle scheduling and duplicate prevention.- Add a Schedule Trigger node. Set it to run Weekly on Sunday at 10:00.
- Add a Google Sheets node named Get Existing Video IDs:
- Operation: Get Many, range
A:A - Go to Settings tab → enable Always Output Data
- This setting is critical — if the Sheet is empty on first run, n8n will stop the chain by default; Always Output Data forces execution to continue with zero items instead of halting.
- Operation: Get Many, range
- Add a Code node named Aggregate IDs. Write a short script that collects all
video_idvalues from the previous node into a single array calledexisting_ids, then outputs it as one item.
Production tip — Any node that reads a "reference list" at the top of a pipeline (lookup data, not core flow data) must have Always Output Data enabled. This same fix was applied retroactively to the T5-B2-Comment-Pipeline from Session 14 after discovering the same empty-Sheet bug.
Step 3 — Fetch Videos from YouTube and Filter Early
Fetch the latest 50 videos from the channel, then filter out already-processed ones before any AI call to save token costs.- Add an HTTP Request node named Get All Videos:
- URL:
https://www.googleapis.com/youtube/v3/search - Parameters:
part=id,snippet,channelId={your_channel_id},order=date,maxResults=50,type=video - Use your YouTube OAuth2 (HTTP) credential.
- URL:
- Add a Split Out node named Split Videos. Set Field To Split Out to
items— this unpacks the wrapped Google API response (one item containing an array) into individual video items. - Add a Code node named Filter New Videos. For each item, read
$json.id.videoIdand drop any whose ID already exists in theexisting_idsarray from the Aggregate IDs node.
Note — Google APIs wrap list responses in a metadata envelope: {kind, etag, pageInfo, nextPageToken, items}. This means the HTTP node returns one n8n item containing all videos inside items[]. The Split Out node is what "opens" that envelope into N individual items. When dragging id.videoId from the Expression Editor, expand the id object first, then drag the videoId child — dragging the parent id gives you an object, not a string.
Production tip — Placing the idempotent filter before the AI node (not after) is the key cost-saving improvement in this session versus Session 14. Videos already in the Sheet never reach the AI, so you only pay for genuinely new content.
Step 4 — Fetch Video Stats and Flatten Data
For each new video, fetch full statistics and flatten everything into a clean 5-field object for the AI.- Add an HTTP Request node named Get Video Stats:
- URL:
https://www.googleapis.com/youtube/v3/videos - Parameters:
part=snippet,statistics,id={{ $json.id.videoId }} - This runs once per video (fan-out pattern), so each item from the filter step triggers its own API call.
- URL:
- Add a Split Out node named Split Stats, splitting on
itemsagain — thevideos.listendpoint also returns a wrapped response. - Add a Code node named Flatten Video. Output exactly 5 fields:
video_id— from$json.id(note:videos.listreturnsidas a top-level string, unlikesearch.listwhich nests it asid.videoId)video_url— constructed ashttps://www.youtube.com/watch?v={video_id}current_title— from$json.snippet.titleview_count— from$json.statistics.viewCount, parsed withparseInt()because the API returns it as a stringpublished_at— from$json.snippet.publishedAt
Step 5 — AI Title Suggestion with Claude
Connect a Basic LLM Chain node to generate optimized title suggestions for each video.- Add a Basic LLM Chain node named Suggest Title. Configure the model:
- Model: Claude Haiku 4.5
- Temperature:
0.7 - Max tokens:
400
- Write the system prompt using an XML 4-block structure:
- Block 1 — Role: Define the AI as a YouTube SEO title optimizer focused on keyword density and CTR.
- Block 2 — Rules: Keep the same language as the original title, add the year 2026 where relevant, use power words, stay under 70 characters.
- Block 3 — Few-shot examples: Include 3 examples — one Vietnamese cooking title (phở bò), one English template/resource title, one Vietnamese n8n automation title. This trains the model to preserve language and style.
- Block 4 — Task: Pass
{{ $json.current_title }}and{{ $json.view_count }}and ask for one optimized title with a reason.
- Add an Output Parser with a JSON schema defining two fields:
suggested_title(string) andreason(string).
Tip — The few-shot examples are what keep the AI from switching languages. Without them, Claude may translate Vietnamese titles into English. With 3 bilingual examples, the model consistently preserves the original language while adding SEO improvements like year tags and high-CTR power words.
Step 6 — Build the 9-Column Row and Append to Sheet
The AI node drops the upstream fields, so you must use cross-node references to reconstruct the full row.- Add an Edit Fields node named Build Row. Map all 9 columns:
- For fields that came from before the AI node (
video_id,video_url,current_title,view_count,published_at), use cross-node syntax:{{ $('Flatten Video').item.json.video_id }}— replacevideo_idwith the relevant field name for each. - For AI output fields (
suggested_title,reason), use{{ $json.suggested_title }}and{{ $json.reason }}— these come from the current item flowing through the chain. - Set
statusto the fixed stringpending_review. - Set
created_atto{{ $now.toISO() }}.
- For fields that came from before the AI node (
- Add a Google Sheets node named Append to Suggestions:
- Operation: Append
- Select your T5-Title-Suggestions Sheet and Suggestions tab
- Mapping: Auto-Map — n8n matches column headers to field names automatically
Note — The reason cross-node reference is required here is that the Basic LLM Chain node replaces the current item's fields with only its own output — the upstream video data is dropped from $json. The syntax $('Flatten Video').item.json.X jumps back up the chain to retrieve those fields. Use $json.X (no node name tag) only when reading from the node directly connected via wire; use $('Node Name').item.json.X when crossing over a node that drops fields.
Step 7 — Build Workflow 2: T5-B3b-Title-Updater
Create the second workflow that runs every hour, picks up approved rows, pushes the new title to YouTube, and marks the row as updated.- Create a new workflow named T5-B3b-Title-Updater. Add a Schedule Trigger set to run every 1 hour.
- Add a Google Sheets node named Get Approved Rows:
- Operation: Get Many, range
A:I - Add a filter:
statusequalsapproved - Enable Always Output Data — if no rows are approved yet, the workflow should exit cleanly instead of throwing an error.
- Operation: Get Many, range
- Add a YouTube node named Update Video Title:
- Credential: YouTube (Personal) service-specific credential
- Operation: Update a video
- Video ID:
{{ $json.video_id }} - Title:
{{ $json.suggested_title }} - Region Code: set to your target region (e.g., Vietnam) — this field is required, the API call will fail without it
- Category Name or ID: select the appropriate category (e.g., People & Blogs) — also required
- Add a Google Sheets node named Mark as Updated:
- Operation: Update Row
- Set Column to Match On to
video_id - Set
statustoupdated
Note — The YouTube videos.update API is technically PUT-style, meaning it normally requires you to send the full snippet object or it overwrites missing fields with empty values. The n8n YouTube node abstracts this entirely — it fetches the existing snippet first, merges your changes in, then sends the complete object. This means your video's description, tags, and other metadata are safe when you only change the title. This behavior was verified with a live video test in this session.
Step 8 — Activate Both Workflows and Test
With both workflows built, activate them and run an end-to-end test.- Open T5-B3-Title-SEO and click Activate in the top-right. It will run automatically next Sunday at 10:00, or you can click Execute Workflow to test immediately.
- After a test run, open your T5-Title-Suggestions Sheet and verify:
- New rows appeared with
status = pending_review - All 9 columns are populated correctly
- Videos already in the Sheet from a previous run are not duplicated
- New rows appeared with
- Manually change one row's
statusfrompending_reviewtoapprovedin the Sheet. - Open T5-B3b-Title-Updater, click Activate, then Execute Workflow. Verify:
- The approved video's title changed on YouTube
- The video's description and tags are unchanged
- The row in the Sheet now shows
status = updated
Tip — After activating, also check the Executions tab for both workflows the next morning to confirm no errors occurred during unattended runs. Catching issues early (like credential expiry or quota limits) is much easier when you review execution history within 24 hours of going live.
Key Lessons from This Session
- Always Output Data is required for reference-data nodes at the top of a pipeline. Any Sheets Get Many node that reads a lookup list (not the core flow item) must have this setting enabled, or an empty Sheet will silently halt the entire workflow.
- Place the idempotent filter before the AI node, not after. Filtering already-processed IDs early means only genuinely new videos reach the LLM, directly reducing API token costs per run.
- Google APIs return "wrapped" responses — one n8n item containing an
items[]array. Always follow an HTTP Request node that calls a Google list endpoint with a Split Out node targetingitemsto unpack it into individual records. - Use
$('Node Name').item.json.Xfor cross-node references when a middle node drops fields. LLM Chain nodes replace the current item with their own output, so upstream fields must be retrieved by name, not via$json. - The YouTube Update node requires both Region Code and Category Name/ID — they are not optional. Omitting either field causes the API call to fail even though the node UI does not mark them as required.
- The
videos.listAPI returnsidas a top-level string, unlikesearch.listwhich nests it asid.videoId. Always check which endpoint you're reading from before mapping the video ID field. - The
statistics.viewCountfield from the YouTube API is a string, not a number. Wrap it withparseInt()in your Flatten code node to avoid downstream sorting or math errors.
Conclusion:
In this session of our n8n workflow automation tutorial series, we built a complete n8n YouTube SEO automation system — a 12-node AI pipeline that suggests optimized titles weekly, plus a 4-node updater that pushes approved changes live without touching your video descriptions or tags. The key architectural wins are the early idempotent filter that cuts AI costs, the cross-node reference pattern for post-LLM field recovery, and the two-workflow state machine that keeps a human in the loop via Google Sheets. In the next session (Session 16), we'll build a weekly performance analytics pipeline that pulls video statistics, runs AI-generated insights, and delivers a report to Telegram.
If you have any questions, feel free to leave a comment below. Thank you!Tags: n8n youtube SEO automation, n8n tutorial, n8n workflow automation, YouTube title optimization, n8n AI automation, Google Sheets n8n, n8n LLM Chain, YouTube API n8n
Maybe you are interested!
- Getting Started with n8n: Interface & Your First Manual Trigger
- n8n HTTP Request Node: Connect Any API Without Code
- Branching Workflows: IF, Switch & Merge Nodes in n8n
- n8n Expressions & Built-in Variables: The Complete Guide
- Comparing AI Models in n8n: Claude vs Gemini vs ChatGPT
- Connect Gmail to n8n: OAuth Setup & Reading Emails
- Build an AI Email Classifier with n8n
- Automated Email Digest to Telegram with n8n
- Google Sheets Automation with n8n: 4 Key Operations
- Auto-Generate Google Docs from Data with n8n
No Comment to " n8n tutorial - Lesson 15: AI-Powered YouTube Title SEO with n8n "