How to Analyze Trending YouTube Videos with n8n, Apify, OpenAI, and Google Sheets

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

Burning hours on YouTube topic research is a creator tax nobody should pay. This n8n template turns a keyword into a stream of high-performing video ideas, thumbnail notes, and AI-written outlines inside Google Sheets – without opening a single YouTube tab.

In this tutorial you’ll import a ready-made workflow that combines n8n Apify OpenAI GPT-4.1 GPT-4o-mini Vision Google Sheets to automate the exact research loop most YouTubers run by hand every week.

What the workflow does

The template exposes a simple n8n form that accepts a keyword or topic. From there, everything is automatic:

  1. Scrape trending videos with Apify’s YouTube search actor – last 60 days, mid-length, newest first, capped at 100 results per run.
  2. Filter outliers: keep only videos with more than 1,000 views and more views than the channel has subscribers. This surfaces videos the algorithm is actively amplifying.
  3. Deduplicate against your sheet so you never re-process the same video twice.
  4. Analyze the thumbnail with GPT-4o-mini Vision – layout, subjects, color palette, mood – in one natural-language paragraph.
  5. Fetch the transcript via a second Apify actor.
  6. Generate an SEO-tuned title plus a 3-5 word thumbnail overlay with GPT-4.1-mini (JSON mode).
  7. Write an original outline with GPT-4.1 that borrows the structure of the winning video but takes a fresh angle – no copy-paste.
  8. Update the Google Sheet with the enriched row: new title, thumbnail text, thumbnail description, transcript, and outline.
Tip: Every node is renamed for clarity and has a sticky note next to it. The workflow is a single-screen flow once you import it – no hunting for connections.

Why this beats manual YouTube research

A manual research session typically takes 60-90 minutes for ten candidate topics: searching, skimming, noting view counts, checking sub counts, screenshotting thumbnails, drafting titles. This workflow runs the same loop in roughly 3-5 minutes per keyword and stores everything in a sortable sheet you can revisit for months.

The outlier filter is the secret sauce. A video with 200k views on a channel that has 30k subscribers is statistically winning right now. Those are the ideas you want to study – not the evergreen giants sitting on top of the search results.

Prerequisites

  • An n8n instance – self-hosted (Docker, VPS, Render) or n8n Cloud. Version 1.70+.
  • Apify account with API token. Free tier is enough for testing.
  • OpenAI API key with access to gpt-4o-mini, gpt-4.1-mini, and gpt-4.1.
  • Google Workspace account and a spreadsheet you control.

You’ll find the exact step-by-step to obtain every credential in the Credentials Guide PDF included with the download, and a full import walkthrough in the Setup Guide PDF. Grab the template here.

Step 1 – Prepare the Google Sheet

Create a new Google Sheet, rename the first tab to Step 1, and paste this header row across the top:

id | title | url | fromYTUrl | thumbnailUrl | channelName | channelUrl | duration | likes | numberOfSubscribers | viewCount | videoTranscript | thumbnailDescription | thumbnailText | newTitle | newOutline

Order doesn’t matter – n8n maps columns by name – but every field above must exist or the workflow will error out on write.

Step 2 – Import the n8n workflow

  1. Open n8n and hit + New > Import from File.
  2. Select analyze-trending-youtube-videos-apify-openai-sheets-n8n.json from the ZIP.
  3. n8n will open the canvas. You’ll see 12 nodes: a form trigger, two Apify HTTP calls, an IF filter, a Google Sheets dedupe + append, three OpenAI nodes, a merge, an update row, and a sticky note with setup instructions.

Step 3 – Paste your Apify token

Click Scrape Trending Videos (Apify), then Fetch Transcript (Apify). Each has an Authorization header set to Bearer YOUR_APIFY_TOKEN. Replace the placeholder with your real token from console.apify.com/account/integrations.

Which Apify actors? The template uses streamers/youtube-scraper for search and pintostudio/youtube-transcript-scraper for captions. Both have public IDs baked in – you do not need to fork them.

Step 4 – Attach OpenAI credentials

Open Analyze Thumbnail, YouTube Title Generator, and Generate Original Outline. For each, pick or create an OpenAI credential with your API key. The models are pre-selected:

  • Analyze Thumbnail: gpt-4o-mini in image mode (~$0.00015 per thumbnail).
  • YouTube Title Generator: gpt-4.1-mini in JSON mode.
  • Generate Original Outline: gpt-4.1 for deeper structure – swap to gpt-4.1-mini if you want to save 60% on that node.

Budget roughly $0.01-$0.02 per video analyzed at default settings.

Step 5 – Connect Google Sheets

Click each of the three Google Sheets nodes (Find Duplicate Entries, Step 1 Results, Update Rows) and:

  1. Attach your Google Sheets OAuth2 credential.
  2. Open the Document dropdown and pick the sheet you prepared in Step 1.
  3. Open the Sheet dropdown and choose the Step 1 tab.

Step 6 – Activate and run

Toggle the workflow active in the top-right corner. n8n exposes a public form at the URL shown on the On form submission node. Open it, type a keyword like “ai automation” or “home workout for seniors”, and submit.

Within a couple of minutes your sheet fills up with filtered videos, AI-optimized titles, and fresh outlines ready for scripting. Sort by viewCount / numberOfSubscribers in a helper column to see the strongest outliers first.

How the filter works under the hood

The Filter High-Potential Videos node enforces two conditions with an AND combinator:

viewCount > 1000
viewCount > numberOfSubscribers

The first condition removes noise – brand-new uploads with a handful of views. The second is the outlier signal: a video whose views already beat the uploader’s subscriber count is almost certainly being pushed by YouTube’s recommendation engine. That’s the exact moment to study the thumbnail, hook, and structure.

Customization ideas

  • Daily cron: replace the form trigger with a Schedule node that runs a rotating list of keywords every morning.
  • Slack digest: add a Slack node after Update Rows to post the top five new outliers to #content channel.
  • Notion sync: mirror the sheet into a Notion database so writers can claim outlines from their side.
  • Shorts mode: swap lengthFilter to "under4" in the scrape node to hunt trending Shorts instead of long-form.
  • Voice cloning prep: pipe the generated outline into ElevenLabs for a rough voiceover of the hook.

Troubleshooting

“Unauthorized” from Apify: the Bearer token is missing or has a trailing space. Paste it again without the word Bearer duplicated.

Google Sheets node errors on missing column: double-check the header row spelling. Column names are case-sensitive.

OpenAI returns a non-JSON outline: the Generate Original Outline node is in text mode on purpose (outlines are prose). If you want structured JSON, toggle JSON Output on and update the system prompt.

Transcript is empty: the Apify transcript actor occasionally fails on age-restricted or auto-generated-caption-disabled videos. The workflow continues gracefully – the row simply has a blank videoTranscript and a weaker outline.

Skip the rebuild – get the finished template

Download the ready-to-import JSON plus the Setup and Credentials guides. Save an afternoon of wiring.

Get the YouTube Trends n8n Template

Final thoughts

YouTube research used to be a research skill. Now it’s a workflow you run on a coffee break. Hook this into your weekly content planning, let it pre-fill your sheet with validated topics, and spend your actual creative energy on the hook and delivery – the parts AI still can’t do for you.

How to Auto-Categorize Blog Posts with GPT-4, GitHub, and Google Sheets in n8n

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

If you run a blog with dozens, or hundreds, of markdown posts sitting in a GitHub repo, keeping categories and tags consistent is a pain. You either do it by hand (tedious) or you skip it (bad for SEO). This n8n workflow reads every post in your repo, ignores the ones you already analyzed, feeds the rest to GPT-4, and drops the suggested categories and tags into a Google Sheet for your review. It works for Astro, Next.js, Hugo, Jekyll: any static site that stores posts as markdown with frontmatter.

By the end of this guide you will have a form-triggered workflow that processes only new posts on each run, uses a two-sheet diff to avoid re-work, and returns a structured JSON response (old categories, new categories, old tags, new tags) for clean before/after comparison.

What this workflow does

The workflow has one job: look at the posts in your GitHub repo, figure out which ones haven’t been categorized yet, and ask GPT-4 to suggest categories and tags based on the content of each post. The proposals land in a Google Sheet so you can review and apply them later. The workflow never pushes commits to your repo, which keeps it safe to run.

  • Trigger: a simple n8n form so you can fire it on demand
  • Source: the content/ folder of your GitHub repo (configurable)
  • Deduplication: posts already logged in Google Sheets are skipped
  • AI engine: GPT-4.1-mini with a structured JSON output parser
  • Sink: Google Sheets with columns FileName, Categories, Proposed Categories, Tags, Proposed Tags

Architecture at a glance

The workflow runs in three phases. First, it pulls two lists in parallel: the full file list from GitHub and the list of previously analyzed filenames from Google Sheets. Second, a Code node computes the difference: posts in GitHub that are not yet in the sheet. Third, it loops through the difference, fetches each file’s content, passes it to the AI agent, and appends the AI’s structured response back to Google Sheets.

Nodes in the workflow

Node Purpose
On form submission Manual trigger with a “Start process” form
Get row(s) in sheet Reads the list of already-categorized filenames
List posts/articles/pages Lists every file in the GitHub content folder
Aggregate (Γ—2) Collapses both path lists into arrays
Merge Brings the two arrays into a single item
Check new repo files for AI processing Code node computing the diff
Switch Routes to “Finish” if empty, otherwise to the loop
Get post/article file Fetches the markdown source for each new file
Loop Over Posts/Pages Batches one post at a time to the AI
AI Agent + OpenAI Chat Model Analyzes content, proposes categories and tags
Structured Output Parser Forces clean JSON output
Append rows with posts / article analysis Saves proposals to Google Sheets

Step 1: Prepare your Google Sheet

Create a new Google Sheet with exactly five columns in row 1, in this order: FileName, Categories, Proposed Categories, Tags, Proposed Tags. The workflow reads FileName to decide what to skip, and writes to all five on each run. Grab the Sheet ID from the URL. It’s the long token between /d/ and /edit.

Tip: If you want to skip the setup and import a working template immediately, the pre-built n8n JSON + step-by-step setup PDF is available for $14.99 on EasyWorkflows.

Step 2: Import the workflow into n8n

Open your n8n instance, click the “+” menu, and choose Import from File (or paste JSON). After import you’ll see the full workflow with sticky notes already in place. Nothing is active yet. The trigger is a form, so there are no webhooks firing in the background.

Step 3: Connect credentials

You need three credentials. Click each highlighted node and create a fresh credential. Do not share credentials across nodes that don’t need to share them.

  1. GitHub: a personal access token with repo read scope. Used by List posts/articles/pages and Get post/article file.
  2. OpenAI: a standard API key. Used by the OpenAI Chat Model node.
  3. Google Sheets OAuth2: run through n8n’s OAuth flow. Used by both Get row(s) in sheet and Append rows with posts / article analysis.

Full step-by-step instructions for getting each token, including screenshots of the GitHub scope page and the Google Cloud OAuth consent screen, are in the Credentials Guide PDF included with the product.

Step 4: Configure the three placeholders

After import, open these three nodes and replace the placeholder values with your own:

List posts/articles/pages
  owner      = YOUR_GITHUB_USERNAME
  repository = YOUR_REPO_NAME
  filePath   = src/content/blog/  (adjust to your folder)

Get post/article file
  owner      = YOUR_GITHUB_USERNAME
  repository = YOUR_REPO_NAME

Get row(s) in sheet  +  Append rows with posts / article analysis
  documentId = YOUR_GOOGLE_SHEET_ID

That’s all the config. The workflow does not hardcode category names or tag taxonomies. The AI agent reads whatever categories already exist in your posts and proposes additions that match your style.

Step 5: Tune the AI system prompt (optional)

The AI Agent node ships with a default system message that tells GPT-4 to read frontmatter, infer categories and tags from content, and return a structured JSON object. If you already have a strict controlled vocabulary (e.g. “categories must be drawn from this list of 12”), add that list to the system message. GPT-4.1-mini handles vocabulary constraints well. The output parser will still enforce valid JSON either way.

The structured output schema is:

{
  "old_categories": ["business"],
  "categories":     ["business", "history-of-computing"],
  "old_tags":       ["DigitalGirls", "WomenInIT"],
  "tags":           ["DigitalGirls", "WomenInIT", "GraceHopper", "Debugging"]
}

Step 6: Run it

Click Execute Workflow, open the form URL, submit it, and watch the executions panel. On the first run the sheet is empty so the diff returns every file in your repo. On every run after that, only new posts since the last successful run are processed. The sheet acts as the state store.

If you want this to run nightly, swap the On form submission trigger for a Schedule trigger. Everything downstream stays the same.

Why this approach beats the alternatives

  • Vs. cron job + script: no server to maintain, no cron log to babysit, and the AI step is visual.
  • Vs. a GitHub Action: Actions bill per-minute and don’t give you a diffable proposal review step. This workflow writes to a sheet so you can vet the AI’s suggestions before applying them.
  • Vs. doing it by hand: obvious.

Common issues

The loop finishes immediately with zero items

The diff node returned an empty list, which means every file in your repo is already logged in the sheet. Delete the test rows from the sheet and re-run.

GPT-4 returns categories outside my taxonomy

Add your controlled vocabulary to the system message on the AI Agent node. Example: “Categories must be one of: business, tutorial, news, opinion. Do not invent new categories.”

Rate limit errors on the GitHub node

A personal access token gets 5,000 req/hour, plenty for most repos. If you’re hitting the limit, enable Continue on Fail on Get post/article file and add a small Wait node inside the loop.

Skip the setup, get the ready-to-import template

n8n JSON + Setup Guide PDF + Credentials Guide PDF. Import, connect, done.

Get the Template – $14.99

What to do next

Once the proposals are in Google Sheets, you have two good options. Option one: review manually and copy the winning categories and tags back into your markdown frontmatter. Option two: extend this workflow with a second step that opens a pull request on your repo with the frontmatter updates applied automatically. The second is a ~6-node addition using the GitHub node’s create file and create PR operations: a clean follow-up if you want zero-touch automation.

Either way, you now have a GPT-4-powered categorization pipeline that costs pennies per post and keeps your content taxonomy clean as your blog grows.

How to Auto-Repost TikTok Videos to YouTube Shorts with n8n

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

You’ve spent hours filming, editing, and posting a killer TikTok video. Your creator account is gaining traction. But here’s the thing: you’re leaving money on the table if that video only lives on TikTok. YouTube Shorts has a completely different audience, different monetization rules, and different growth potential. The problem? Manually downloading from TikTok, re-uploading to YouTube, writing metadata, and repeating this process for every video is a soul-crushing grind that pulls you away from creating.

What if you could automate the entire repurposing pipeline? Post to TikTok once, and within minutes, your video automatically appears on YouTube Shorts with optimized metadata, proper formatting, and zero manual intervention. That’s exactly what we’re building today with n8n.

Save hours each week by automating your cross-platform distribution. Grab the ready-made template and skip the setup headaches.

What You’ll Build

By the end of this guide, you’ll have a fully automated workflow that handles every step of repurposing TikTok content to YouTube Shorts. Here’s what your system will do:

  1. Monitor your TikTok profile. Every 6 hours, the workflow automatically checks your TikTok account for new videos using the RapidAPI TikTok Scraper.
  2. Detect fresh uploads. A filtering system identifies videos you haven’t already repurposed, preventing duplicates and wasted resources.
  3. Download video files. Your videos are fetched directly from TikTok in YouTube-compatible formats.
  4. Generate YouTube metadata. The system automatically creates optimized titles, descriptions, and tags based on your TikTok content.
  5. Upload to YouTube Shorts. Videos are submitted via YouTube’s Data API v3 with proper resumable session handling for reliability.
  6. Log everything to Google Sheets. You get a growing inventory of what’s been repurposed, timestamps, and upload status.
  7. Trigger alerts via Telegram. You’re notified the moment a video hits YouTube, plus you can manually trigger reposts via a Telegram command.

How It Works: The Big Picture

This workflow operates on a dual-trigger model. It can run on a schedule (every 6 hours) or be triggered manually through Telegram. Let’s map out the flow:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       TWO ENTRY POINTS                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                               β”‚
β”‚  Trigger A: Schedule (Every 6 Hours)    Trigger B: Telegram β”‚
β”‚  β”‚                                       β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”˜                   β”‚
β”‚                     β”‚                  β”‚                     β”‚
β”‚              Set TikTok Username    Parse Telegram           β”‚
β”‚                     β”‚                  β”‚ /repost @username   β”‚
β”‚                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚
β”‚                            β”‚                                  β”‚
β”‚                   Fetch TikTok Videos                        β”‚
β”‚                 (HTTP: RapidAPI Scraper)                    β”‚
β”‚                            β”‚                                  β”‚
β”‚                  Filter New Videos Only                      β”‚
β”‚                  (Code: Parse Response)                      β”‚
β”‚                            β”‚                                  β”‚
β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚              β”‚                           β”‚                   β”‚
β”‚        Log to Google Sheets      Prepare YouTube Metadata    β”‚
β”‚              β”‚                           β”‚                   β”‚
β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                            β”‚                                  β”‚
β”‚            Create YouTube Upload Session                     β”‚
β”‚           (YouTube API v3: Resumable)                       β”‚
β”‚                            β”‚                                  β”‚
β”‚          Download TikTok Video (Temp)                        β”‚
β”‚                            β”‚                                  β”‚
β”‚      Upload Video to YouTube Shorts                          β”‚
β”‚            (YouTube API v3)                                  β”‚
β”‚                            β”‚                                  β”‚
β”‚       Send Telegram Notification                             β”‚
β”‚              (Success Alert)                                  β”‚
β”‚                                                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

What You’ll Need

Before we build, make sure you have these pieces in place. The entire setup takes about 30 to 45 minutes.

  • n8n account (free tier works, but Pro is recommended for multiple simultaneous workflows)
  • RapidAPI account with an active TikTok Scraper subscription ($10 to 30/month depending on request volume)
  • YouTube Data API v3 enabled in Google Cloud Console with OAuth 2.0 credentials
  • Google Sheets API enabled in the same GCP project
  • Telegram Bot Token from BotFather (free, takes 2 minutes)
  • Google Account with OAuth access for YouTube and Sheets
  • Creator account on both TikTok and YouTube (with YouTube Studio access)
  • Minimum 2GB free storage on n8n for temporary video files

Step 1: Create the Schedule Trigger

1 Schedule Trigger Node

Start with n8n’s built-in Schedule node. This will kick off your workflow every 6 hours, allowing you to check for new TikTok videos automatically.

Node Configuration:

  • Set trigger to Every 6 hours
  • Leave timezone as default (your account timezone)
  • Check the box for “Activate node immediately on deployment”

The Schedule trigger feeds directly into a Set TikTok Username node, which we’ll cover next.

Step 2: Add Manual Trigger with Telegram

2 Telegram Bot Trigger

To give yourself on-demand control, add a Telegram trigger. This lets you manually request a repost without waiting for the 6-hour schedule.

Setup steps:

  1. Message @BotFather on Telegram and create a new bot (copy the token)
  2. In n8n, create a Telegram trigger node
  3. Add your Bot Token (paste from BotFather)
  4. Set trigger to New Message
  5. Configure message filter: Message starts with /repost

This trigger runs independently of the schedule, so you can trigger reposts manually anytime via Telegram.

Step 3: Set TikTok Username

3 Set Username Node (Code)

Both triggers converge here. This code node normalizes the TikTok username input, whether it came from the schedule default or a Telegram command.

Node configuration:

  • Node type: Code
  • Language: JavaScript

Code snippet:

// If triggered via Telegram, extract username from message
// Otherwise, use default username

if (items[0].json.message !== undefined) {
  // Telegram trigger: extract username from /repost @username
  const messageText = items[0].json.message;
  const parts = messageText.split(' ');
  const username = parts[1] ? parts[1].replace('@', '') : 'erinchen_creates';

  return [{
    json: {
      tikTokUsername: username,
      source: 'telegram'
    }
  }];
} else {
  // Schedule trigger: use default
  return [{
    json: {
      tikTokUsername: 'erinchen_creates',
      source: 'schedule'
    }
  }];
}

This node creates a standardized output that the next nodes can reliably consume, regardless of trigger source.

Step 4: Fetch TikTok Videos via HTTP Request

4 HTTP Request Node (RapidAPI)

Now we actually fetch the TikTok video data. We’re using RapidAPI’s TikTok Scraper, a reliable, legal way to pull public TikTok metadata.

Node configuration:

  • Method: GET
  • URL: https://tiktok-api.p.rapidapi.com/user/posts

Headers (add all three):

x-rapidapi-key: YOUR_RAPIDAPI_KEY
x-rapidapi-host: tiktok-api.p.rapidapi.com
Accept: application/json

Query parameters:

Parameter Value
username {{ $node["Set TikTok Username"].json.tikTokUsername }}
limit 10
sort newest

The response includes video ID, caption, duration, engagement metrics, and download URL. You’ll parse this in the next step.

πŸ’‘

Pro tip: RapidAPI requests are rate-limited based on your subscription. Start with a 6-hour schedule and adjust frequency based on your usage tier to avoid overages.

Step 5: Filter New Videos with Code

5 Filter Code Node

The RapidAPI response might include videos you’ve already repurposed. This code node filters the response, comparing against your Google Sheets log of uploaded videos.

Node configuration:

  • Node type: Code
  • Language: JavaScript

Code snippet:

// Parse TikTok API response
const videos = items[0].json.data || [];

// Get list of already-uploaded video IDs from Sheets context
const uploadedIds = items[1]?.json?.uploadedVideoIds || [];

// Filter to only new videos
const newVideos = videos.filter(video => {
  return !uploadedIds.includes(video.id);
});

// Return filtered array (will create one item per video)
return newVideos.map(video => {
  return {
    json: {
      videoId: video.id,
      caption: video.desc,
      downloadUrl: video.downloadUrl,
      duration: video.duration,
      likes: video.stats?.likes || 0,
      shares: video.stats?.shares || 0,
      comments: video.stats?.comments || 0,
      createTime: video.createTime
    }
  };
});

The output is now one item per new video, ready to fan out across parallel processing.

Step 6: Log to Google Sheets

6 Google Sheets Append Node

Keep a permanent record of every repurposed video. This becomes your audit log and prevents duplicate uploads.

Setup:

  1. Create a new Google Sheet named “TikTok Reposts” (or similar)
  2. Create columns: TikTok Video ID, Caption, Duration, Likes, Date Posted, YouTube Link, Upload Status, Timestamp
  3. In n8n, add a Google Sheets node
  4. Authenticate with your Google account (OAuth 2.0)

Node configuration:

  • Operation: Append
  • Spreadsheet: Select “TikTok Reposts”
  • Sheet: Select your data sheet
  • Map columns to video data from the filter node

Column mappings:

Sheet Column Source Data
TikTok Video ID {{ $node["Filter Code"].json.videoId }}
Caption {{ $node["Filter Code"].json.caption }}
Duration {{ $node["Filter Code"].json.duration }}
Upload Status Pending
Timestamp {{ new Date().toISOString() }}

This node executes in parallel with the YouTube upload process: we’re logging first, then processing.

Step 7: Prepare YouTube Metadata

7 Prepare Metadata (Code Node)

YouTube has strict requirements for title, description, and tags. This node transforms your TikTok caption into YouTube-optimized metadata.

Code snippet:

// Transform TikTok caption to YouTube metadata
const tiktokCaption = items[0].json.caption || '';
const videoId = items[0].json.videoId;

// Generate YouTube title (max 100 chars)
let youtubeTitle = tiktokCaption.substring(0, 100);
if (tiktokCaption.length > 100) {
  youtubeTitle += '...';
}

// Generate YouTube description
const description = `Originally posted on TikTok. Subscribe for more shorts!\n\n${tiktokCaption}\n\n#Shorts #TikTok #YouTubeShorts`;

// Extract hashtags from caption for YouTube tags
const hashtags = tiktokCaption.match(/#\w+/g) || [];
const tags = [...hashtags, 'shorts', 'tiktok', 'video'].slice(0, 30); // YouTube max 30 tags

return [{
  json: {
    title: youtubeTitle,
    description: description,
    tags: tags,
    privacyStatus: 'public',
    categoryId: '24', // Entertainment category
    duration: items[0].json.duration,
    downloadUrl: items[0].json.downloadUrl,
    videoId: videoId
  }
}];

This ensures your YouTube content is discoverable and follows platform best practices.

Step 8: Create YouTube Upload Session

8 YouTube API: Create Resumable Session

YouTube requires a two-step upload process: first create a resumable session, then upload the file. This prevents re-uploads if the connection drops.

Node type: HTTP Request

Method: POST

URL:

https://www.googleapis.com/youtube/v3/videos?uploadType=resumable&part=snippet,status

Headers:

Header Value
Authorization Bearer {{ $env.YOUTUBE_TOKEN }}
Content-Type application/json
X-Goog-Upload-Protocol resumable

Body (JSON):

{
  "snippet": {
    "title": "{{ $node['Prepare Metadata'].json.title }}",
    "description": "{{ $node['Prepare Metadata'].json.description }}",
    "tags": {{ JSON.stringify($node['Prepare Metadata'].json.tags) }},
    "categoryId": "24"
  },
  "status": {
    "privacyStatus": "public",
    "selfDeclaredMadeForKids": false
  }
}

YouTube responds with a resumable session URL in the Location header. Store this for the next step.

Step 9: Download TikTok Video

9 HTTP Request: Download Video

Fetch the actual video file from TikTok. n8n will store this temporarily in memory.

Node type: HTTP Request

Method: GET

URL:

{{ $node["Filter Code"].json.downloadUrl }}

Options:

  • Set “Response Format” to File
  • Add header User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
  • Set timeout to 60 seconds

The downloaded file is automatically staged for upload in the next step.

⚠️

Important: n8n’s free tier has memory limits. Videos larger than 256MB may fail. For larger files, consider using n8n’s S3 integration to stage files in cloud storage first.

Step 10: Upload Video to YouTube

10 YouTube API: Upload Video Chunk

Now upload the downloaded video file to YouTube using the resumable session created in Step 8.

Node type: HTTP Request

Method: PUT

URL:

{{ $node["Create Upload Session"].headers.location }}

Headers:

Header Value
Content-Type video/mp4
Content-Length {{ $node["Download Video"].data.fileSize }}

Body: Set to Binary and map the file from the Download Video node.

Upon success, YouTube returns a 200 response with video metadata including the id field (your new YouTube video ID).

Step 11: Send Telegram Notification

11 Telegram Bot Send Message

Celebrate the successful upload. Send yourself a Telegram notification with the YouTube link.

Node type: Telegram

Configuration:

  • Operation: Send Message
  • Bot Token: (same as Step 2)
  • Chat ID: Your personal Telegram user ID

Message text:

βœ… Video Uploaded to YouTube!

Title: {{ $node['Prepare Metadata'].json.title }}
YouTube URL: https://youtube.com/shorts/{{ $node['Upload Video'].json.id }}
Duration: {{ $node['Filter Code'].json.duration }}s
Likes: {{ $node['Filter Code'].json.likes }}

Check it out and update the sheet when ready.

This becomes your real-time alert system. You’ll know instantly when each video goes live.

The Data Structure

Your Google Sheets log is the backbone of the system. Here’s what your tracking sheet should look like:

TikTok Video ID Caption Duration Likes Date Posted YouTube Video ID Upload Status Timestamp
7261849372941234567 Morning coffee thoughts #coffee #viral 59 12,340 2026-04-13 dQw4w9WgXcQ Success 2026-04-13 09:42:17
7261847295837265100 New fitness routine! #gym #fitnessmotivation 45 8,920 2026-04-12 jNQXAC9IVRw Success 2026-04-12 18:15:03

Each row represents one repurposed video. Over time, this sheet becomes your content inventory: searchable, sortable, and exportable for analytics.

Full System Flow

Here’s the complete, end-to-end picture of your automated workflow:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    START: DUAL TRIGGERS                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                β”‚
β”‚   Schedule: Every 6 hours          Telegram: /repost cmd      β”‚
β”‚   (Morning, Noon, Evening, Night)  (Manual on-demand)         β”‚
β”‚                                                                β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚
β”‚                          β”‚                                    β”‚
β”‚                   [Set TikTok Username]                       β”‚
β”‚              Extract or use default username                 β”‚
β”‚                          β”‚                                    β”‚
β”‚          [HTTP: Fetch TikTok Videos via RapidAPI]            β”‚
β”‚               Get latest 10 videos, newest first              β”‚
β”‚                          β”‚                                    β”‚
β”‚              [Code: Filter New Videos Only]                  β”‚
β”‚        Compare against Sheets log, skip already-uploaded    β”‚
β”‚                          β”‚                                    β”‚
β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”‚
β”‚              β”‚ (Loop: Per New Video) β”‚                        β”‚
β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β”‚
β”‚                          β”‚                                    β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚         β”‚                                 β”‚                   β”‚
β”‚    [Google Sheets]              [Code: Prepare YouTube     β”‚
β”‚    Append Row                    Metadata & Tags]            β”‚
β”‚    - Video ID                    - Title (max 100 chars)     β”‚
β”‚    - Caption                     - Description + Hashtags    β”‚
β”‚    - Duration                    - Category: Entertainment   β”‚
β”‚    - Likes/Shares                - Privacy: Public           β”‚
β”‚    - Status: Pending             - Tags: 30 max             β”‚
β”‚         β”‚                                 β”‚                  β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                          β”‚                                    β”‚
β”‚      [YouTube API: Create Resumable Session]                 β”‚
β”‚           POST to /videos?uploadType=resumable               β”‚
β”‚           Receive: X-Goog-Upload-Protocol Location           β”‚
β”‚                          β”‚                                    β”‚
β”‚         [HTTP: Download TikTok Video File]                    β”‚
β”‚           Temporary storage in n8n memory/disk                β”‚
β”‚                          β”‚                                    β”‚
β”‚      [YouTube API: Upload Video File (PUT)]                  β”‚
β”‚      Send binary file to resumable session URL               β”‚
β”‚                          β”‚                                    β”‚
β”‚         [Code: Extract YouTube Video ID]                     β”‚
β”‚           Parse response, get youtu.be ID                    β”‚
β”‚                          β”‚                                    β”‚
β”‚    [Google Sheets: Update Row]                               β”‚
β”‚    - YouTube Video ID column                                 β”‚
β”‚    - Status: Success                                         β”‚
β”‚    - Timestamp: Upload completion time                       β”‚
β”‚                          β”‚                                    β”‚
β”‚    [Telegram: Send Notification]                             β”‚
β”‚    "βœ… Video uploaded: [title] [youtube.com/shorts/ID]"      β”‚
β”‚                          β”‚                                    β”‚
β”‚             [Workflow End for This Video]                    β”‚
β”‚      (Loop continues for next new video if exists)           β”‚
β”‚                          β”‚                                    β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚    [All videos processed?]                                   β”‚
β”‚         Yes ↓              No ↓                               β”‚
β”‚    [Workflow Done]    [Continue loop]                        β”‚
β”‚                                                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Testing Your Workflow

Before activating on the real 6-hour schedule, test everything in isolation. Here’s a systematic approach:

Test Plan

  1. Test TikTok Fetch (Step 4)
    • Run just the HTTP RapidAPI node with a username you know has videos
    • Check that the response contains video objects with id, desc, downloadUrl
    • Verify API rate limit isn’t exceeded (check RapidAPI dashboard)
  2. Test Filter Logic (Step 5)
    • Mock the RapidAPI response with sample video data
    • Verify the filter code correctly identifies new vs. already-uploaded videos
    • Use a small test Sheets with 1-2 pre-existing IDs to confirm filtering works
  3. Test YouTube OAuth (Step 8)
    • Create a test n8n YouTube node and authenticate
    • Verify your token is valid and has youtube.upload scope
    • Try creating a resumable session (you can discard it, don’t upload yet)
  4. Test End-to-End with a Real Video
    • Use the workflow’s “Test” button to run from the Schedule trigger
    • Upload one real video to YouTube to verify the full pipeline
    • Check the YouTube Studio to confirm the video appears with correct metadata
    • Verify Telegram notification was sent
    • Verify Google Sheets row was created and updated

Troubleshooting Guide

Error Likely Cause Solution
“Invalid API key” from RapidAPI Wrong or expired RapidAPI key Check x-rapidapi-key header, regenerate key if needed
“403 Forbidden” from YouTube API YouTube OAuth token expired or missing scopes Disconnect and re-authenticate YouTube in n8n; verify youtube.upload scope
Video file too large, upload fails TikTok video exceeds n8n memory limit Use n8n S3 integration to stage files; OR upgrade n8n plan
“Telegram message not sent” Bot token invalid or wrong chat ID Verify Bot Token from BotFather; get chat ID via telegram.me/IDBot
Duplicate videos uploaded (filter not working) Sheets lookup returning empty list Check Google Sheets read node before filter; ensure column names match
YouTube metadata missing/incomplete Prepare Metadata code node failed silently Check TikTok caption contains valid data; add error handling to code node

Frequently Asked Questions

Can I upload to multiple YouTube channels?

Yes, but you’ll need separate YouTube OAuth credentials for each channel. Add a “Select Channel” node before the upload, and loop through each channel’s upload session. Alternatively, use YouTube’s channel switching feature if all channels are on one Google account.

What happens if a video upload fails halfway through?

YouTube’s resumable upload protocol is designed for this. If the connection drops, the next upload attempt resumes from where it stopped (within 24 hours). n8n will retry failed HTTP requests if you enable “Retry on Fail” in the upload node. Always check your YouTube Studio for duplicate uploads after failures.

Can I customize the YouTube description and tags automatically?

Absolutely. The “Prepare Metadata” code node (Step 7) extracts hashtags from your TikTok caption. You can extend it to pull from a lookup table, add timestamps, or include affiliate links. Just be careful to follow YouTube’s community guidelines on self-promotion.

How do I handle videos that are too short or too long for YouTube Shorts?

YouTube Shorts accepts videos 15 seconds to 10 minutes. Add a duration check in the Filter node (Step 5): skip videos under 15 seconds, and split videos over 60 seconds into multiple Shorts. For splitting, you’d need to integrate a video processing tool like FFmpeg via n8n’s SSH or webhook triggers.

Can I monetize the YouTube videos once they’re uploaded?

YouTube’s Partner Program requires 1,000 subscribers and 4,000 watch hours. The workflow doesn’t affect eligibility, but note that repurposed content may have different monetization rules. Check YouTube’s policies on content reuse. Once you’re monetized, ensure the original TikTok and YouTube videos aren’t flagged as duplicates, different timestamps and metadata help.

Ready to automate your content pipeline?

Stop spending hours on manual uploads. The ready-made n8n template handles every step, from TikTok fetching to YouTube scheduling to Telegram alerts. All the nodes are pre-configured; you just add your API keys.

Get the Template Now

Includes full documentation, test data, and 30-day email support

What’s Next?

Once your core workflow is humming, consider these extensions:

  • Instagram Reels Integration. Add Instagram Data API to cross-post to Reels with the same metadata pipeline. Instagram and YouTube have slightly different format requirements, so you’d branch the metadata preparation.
  • Analytics Tracking. Pull daily YouTube Analytics API data for each uploaded video and log performance metrics (views, watch time, CTR) back to your Sheets. Identify which TikToks perform best on YouTube.
  • Smart Scheduling. Use n8n’s AI nodes to analyze your audience timezone and schedule uploads when your audience is most active. YouTube Shorts have different peak times than TikTok.
  • Content Moderation. Add a screening step using OpenAI’s moderation API to flag videos with potentially problematic content before they go live on YouTube.
  • Thumbnail Generation. Automatically extract a frame from each TikTok and use an image-to-text API to generate optimized YouTube thumbnail text overlays.
  • Multi-Platform Dashboard. Build a custom UI with Webflow or Bubble that displays your reposts, engagement metrics, and upload history, no more spreadsheet hunting.

The modular nature of n8n means each extension is just a few new nodes. Start simple, and scale as your workflow matures.

n8n
TikTok
YouTube Shorts
Automation
Content Marketing
RapidAPI
YouTube API
Google Sheets
Telegram Bot
Workflow

How to Build an AI Blog SEO Analyzer with n8n and GPT-4 (Full Audit in Seconds)

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

Every content team wants better SEO, but running a proper audit on a blog post takes 30 to 60 minutes of manual work: checking keyword density, analyzing meta elements, assessing readability, spotting technical issues, and identifying backlink opportunities. Multiply that by 10 posts a week and it becomes a full-time job. This n8n workflow fixes that. Send a URL, get a complete GPT-4 SEO analysis in seconds, all with built-in ethical scraping compliance.

In this guide you’ll build the workflow from scratch, understand each node, and learn how to hook the output into Slack, Google Sheets, or any dashboard you already use.

πŸ’‘ Prefer to skip the build? Grab the ready-made template β†’ and be running in under 10 minutes.

What You’ll Build

  1. POST a blog URL to an n8n webhook from any app or script
  2. n8n validates the URL and checks the site’s robots.txt for scraping permission
  3. The blog’s HTML is fetched, converted to clean markdown, and fed to GPT-4o
  4. GPT-4 returns a structured JSON report with scores across four SEO dimensions
  5. The report comes back in the HTTP response, ready for dashboards, Sheets, or Slack

How It Works: The Big Picture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ AI BLOG SEO ANALYZER β”‚
β”‚ β”‚
β”‚ [POST /webhook] β†’ [Extract URL] β†’ [Validate URL] β”‚
β”‚ ↓ β”‚
β”‚ [Check robots.txt] β”‚
β”‚ ↓ β”‚
β”‚ [Parse robots.txt Rules] β”‚
β”‚ ↓ β”‚
β”‚ [Scraping Allowed?] β”‚
β”‚ ↓ YES ↓ NO β”‚
β”‚ [Scrape Blog] [Return 403 Error] β”‚
β”‚ ↓ β”‚
β”‚ [Convert HTML β†’ Markdown] β”‚
β”‚ ↓ β”‚
β”‚ [SEO Analysis (GPT-4o)] β”‚
β”‚ ↓ β”‚
β”‚ [Format Report] β†’ [Return JSON Response] β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

What You’ll Need

  • n8n: self-hosted (free) or n8n Cloud
  • OpenAI API key: GPT-4o access required (~$0.01 to $0.05 per audit depending on post length)
  • A webhook client: Postman, curl, or any HTTP tool
  • Build time: ~45 minutes from scratch
  • With the template: under 10 minutes (add API key + activate)

Step 1: Webhook Trigger

Node: Webhook Trigger n8n-nodes-base.webhook

This is the entry point. It listens for POST requests and passes the payload to the rest of the workflow.

Configure it:

  1. Set HTTP Method to POST
  2. Set Response Mode to Using Respond to Webhook Node
  3. Copy the generated webhook URL, you’ll POST to this from your client
  4. Enable Allow all origins under Options if testing from a browser tool

Once activated, clients call it like this:

curl -X POST https://your-n8n.com/webhook/YOUR_WEBHOOK_ID \
  -H "Content-Type: application/json" \
  -d '{ "blogUrl": "https://techcrunch.com/2026/03/15/ai-startup-funding" }'
πŸ’‘ Tip: The workflow accepts blogUrl, message, or url as the key, whichever you send, it’ll find the URL. Easy to connect from Telegram bots, Slack slash commands, or form submissions.

Step 2: Extract Blog URL

Node: Extract Blog URL n8n-nodes-base.set

Normalizes the incoming payload so downstream nodes always find body.url regardless of which key the caller used.

Configure it (Manual mode):

  1. Add one assignment: Name = body, Type = Object
  2. Value = ={{ { url: $json.body.blogUrl || $json.body.message || $json.body.url } }}

Step 3: Validate URL Input

Node: Validate URL Input n8n-nodes-base.code

Validates the URL format, ensures a value was provided, and sets default CSS selectors for content extraction. If the URL is invalid, the workflow throws an error here before wasting an API call.

// Output after validation:
{
  "url": "https://techcrunch.com/2026/03/15/ai-startup-funding",
  "userPrompt": "Provide a comprehensive SEO analysis with actionable recommendations.",
  "selectors": {
    "title": "title, h1",
    "content": "p, .content, article",
    "links": "a[href]",
    "images": "img[src]"
  },
  "timestamp": "2026-04-10T09:15:00.000Z"
}
πŸ’‘ Tip: Extend the Code node to strip UTM parameters, normalize trailing slashes, or add a domain allowlist so only approved sites can be analyzed.

Step 4: Check robots.txt (Ethical Scraping)

Node: Check robots.txt n8n-nodes-base.httpRequest

Fetches https://domain.com/robots.txt before touching any content. This is the ethical compliance gate.

Configure it:

  1. Method: GET
  2. URL: ={{ $json.url.split('/').slice(0, 3).join('/') }}/robots.txt
  3. Set timeout to 10,000 ms and max redirects to 3

Step 5: Parse Robots.txt Rules

Node: Parse Robots.txt Rules n8n-nodes-base.code

Reads the robots.txt response and checks whether the target URL path is disallowed. If scraping is blocked, it sets scrapingAllowed: false.

// If scraping is permitted, output looks like:
{
  "url": "https://techcrunch.com/2026/03/15/ai-startup-funding",
  "robotsInfo": "robots.txt found and analyzed",
  "scrapingAllowed": true,
  "timestamp": "2026-04-10T09:15:00.123Z"
}
πŸ’‘ Tip: Many sites block /wp-admin/ and /search/ but allow /blog/ and /articles/. Regular blog posts are almost always permitted.

Step 6: Scraping Allowed? (IF Branch)

Node: Scraping Allowed? n8n-nodes-base.if

Routes the workflow: scrapingAllowed = true proceeds to scrape; false returns a 403 error immediately.

Configure it:

  1. Add condition: Left Value = ={{ $json.scrapingAllowed }}
  2. Operator: Boolean β†’ Is True
  3. Connect Output 0 (TRUE) β†’ Scrape Blog Content
  4. Connect Output 1 (FALSE) β†’ Return Scraping Blocked Error

Step 7: Scrape Blog Content

Node: Scrape Blog Content n8n-nodes-base.httpRequest

Fetches the full HTML of the blog post. n8n’s HTTP Request node handles redirects, compressed responses, and most edge cases automatically.

Configure it:

  1. Method: GET
  2. URL: ={{ $json.url }}
  3. Set timeout to 30,000 ms and max redirects to 5
{
  "data": "<!DOCTYPE html><html>...</html>",
  "headers": { "content-type": "text/html; charset=utf-8" },
  "statusCode": 200
}

Step 8: Convert HTML to Markdown

Node: Convert HTML to Markdown n8n-nodes-base.markdown

Strips HTML tags and converts content to clean markdown, 40 to 60% fewer tokens than raw HTML, saving significant GPT-4 costs.

Configure it:

  1. HTML: ={{ $json.data }}
  2. Enable Code Block Style: Fence
  3. Enable Use Link Reference Definitions
πŸ’‘ Tip: For very long articles (>8,000 words), add a Code node to truncate: return [{ json: { data: $json.data.substring(0, 24000) } }]

Step 9: SEO Analysis with GPT-4o

Node: SEO Analysis (GPT-4) @n8n/n8n-nodes-langchain.openAi

The core of the workflow. Sends the markdown to GPT-4o with a structured prompt covering four SEO dimensions, returns a JSON report.

Configure it:

  1. Model: GPT-4o
  2. Temperature: 0.1 (precise, repeatable analysis)
  3. JSON Output: Enable
  4. Add your OpenAI credential
  5. User Message: ={{ $json.data }}
{
  "overallScore": 73,
  "executiveSummary": {
    "strengths": [
      "Strong primary keyword placement in H1 and first paragraph",
      "Good internal linking structure with 8 contextual links"
    ],
    "opportunities": [
      "Meta description missing, critical for CTR",
      "No FAQ schema markup for People Also Ask eligibility"
    ],
    "priorityActions": [
      "Write a 155-character meta description with primary keyword",
      "Add FAQ schema for top 5 questions in the article"
    ]
  },
  "keywordStrategy": {
    "primaryKeywords": ["AI startup funding", "venture capital 2026"],
    "longTailOpportunities": ["how much AI startup funding in 2026"]
  },
  "implementationRoadmap": {
    "quickWins": ["Add meta description", "Fix broken image alt tags"],
    "shortTerm": ["Create FAQ section", "Build 3 internal cluster posts"],
    "longTerm": ["Guest post campaign targeting DA 50+ sites"]
  }
}
πŸ’‘ Tip: Customize the system prompt for your niche, e.g., “This is a SaaS marketing blog targeting CTOs”, for more targeted keyword and tone recommendations.

Step 10: Format Analysis Report

Node: Format Analysis Report n8n-nodes-base.code

Parses the OpenAI response, extracts the JSON, and wraps it with metadata (URL, timestamp) before returning to the caller.

{
  "success": true,
  "url": "https://techcrunch.com/2026/03/15/ai-startup-funding",
  "analyzedAt": "2026-04-10T09:15:44.321Z",
  "overallScore": 73,
  "report": { ... }
}

Connecting the Output to Other Tools

Once running, chain the output of Format Analysis Report into:

  • Google Sheets: Append a row per URL with score, quick wins, and keyword gaps, build a running SEO audit log
  • Slack: Post scores and priority actions to #seo-reports channel every time a post is analyzed
  • Notion: Create a database record per analysis with scores as structured properties
  • Airtable: Track keyword opportunities across your entire content library in one view

The SEO Report Structure

Field Type Example Description
overallScore Integer 73 Aggregate SEO score 0 to 100
contentOptimization.score Integer 78 Content quality and keyword integration
keywordStrategy.primaryKeywords Array ["AI funding"] Top keywords GPT-4 detected in content
keywordStrategy.longTailOpportunities Array ["best AI startups 2026"] Missing keyword angles to target
technicalSEO.score Integer 65 Technical health score
technicalSEO.issues Array ["No canonical tag"] Technical problems found
backlinkPotential.score Integer 81 How link-worthy the content is
implementationRoadmap.quickWins Array ["Add meta description"] High-impact, low-effort fixes

Scaling This Workflow

The webhook trigger is perfect for on-demand audits. For batch use, replace it with a Schedule Trigger + Google Sheets source to run overnight audits across your entire blog library. Or wire it to an RSS feed node to auto-audit every new post you publish.

For high-volume use (100+ URLs/day), add a Wait node between the HTTP scrape and GPT-4 call. OpenAI’s Tier 1 rate limit on GPT-4o handles roughly 20 to 30 blog audits per minute, more than enough for most teams.

Skip the Build: Get the Ready-Made Template

Includes the complete workflow JSON, a step-by-step Setup Guide, and a Credentials Guide showing exactly where to find your OpenAI API key. Import, configure, and start auditing in under 10 minutes.

Download the Template, $14.99 β†’

How to Repurpose Instagram Reels into AI-Rewritten Scripts with n8n

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

You probably already follow a dozen Instagram accounts in your niche. Every day they post Reels about tools, tips, and trends your audience would love. But watching each video, taking notes, and rewriting the content in your own voice takes hours you don’t have. What if you could feed n8n a list of Instagram handles and wake up to a spreadsheet full of AI-rewritten scripts ready to record?

That’s exactly what this workflow does. It pulls the latest Reel from each account on your list, transcribes the audio, uses GPT-4O to analyze and filter the content, researches the tools mentioned via Perplexity AI, and writes a brand-new script tailored to your audience. The whole thing runs in minutes, not hours.

Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.

What You’ll Build

  1. Add Instagram handles to a Google Sheet (e.g., mkbhd, garyvee, levelsio).
  2. Click “Execute” in n8n. The workflow fetches each account’s latest Reel, downloads the video, and transcribes the audio with OpenAI Whisper.
  3. GPT-4O analyzes each transcript to decide if it covers a tool or technology worth repurposing, then generates step-by-step usage instructions and content improvement suggestions.
  4. Perplexity AI researches each tool for unique facts your competitors probably missed.
  5. GPT-4O writes a polished, original script (~100 words) in your chosen tone, complete with a call-to-action, and saves everything to your output sheet.

How It Works — The Big Picture

The workflow is a single linear pipeline that loops through each Instagram profile on your list. Here’s the full flow:

┌──────────────────────────────────────────────────────────────────────────────────┐
│  INSTAGRAM CONTENT REPURPOSING PIPELINE                                    │
│                                                                            │
│  [Manual Trigger] → [Google Sheets] → [Loop] → [Scrape Instagram]       │
│       → [Extract URLs] → [Download Video] → [Whisper Transcribe]         │
│       → [GPT-4O Analyze] → [Perplexity Research] → [GPT-4O Rewrite]     │
│       → [Save to Sheets] → [Loop Back]                                   │
└──────────────────────────────────────────────────────────────────────────────────┘
  

What You’ll Need

  • n8n (Cloud or self-hosted, version 1.0+)
  • OpenAI API key — for GPT-4O text generation and Whisper audio transcription
  • Perplexity AI API key — for web research on identified tools
  • Scrape Creators API key — for fetching Instagram post data (paid service)
  • Google account — for Google Sheets OAuth2 access
  • A Google Sheet with a “profiles” tab containing an “Instagram Handles” column

Estimated build time: 35–50 minutes from scratch, or under 10 minutes with the template.

Part 1 — Loading Instagram Profiles

1 Manual Trigger

The workflow starts with a manual trigger, meaning you click “Execute Workflow” in n8n to kick it off. This gives you full control over when the content research runs. If you’d prefer an automated schedule, you can swap this for a Schedule Trigger node set to run daily or weekly.

2 Read Instagram Profiles (Google Sheets)

This node connects to your Google Sheet and reads every row from the “profiles” tab. Each row should have one Instagram handle (without the @ symbol) in a column called Instagram Handles.

[
  { "Instagram Handles": "mkbhd" },
  { "Instagram Handles": "garyvee" },
  { "Instagram Handles": "levelsio" }
]
💡

Tip: Add as many handles as you want. The workflow processes them one at a time via the loop, so you won’t hit rate limits as easily.

3 Loop Over Items

The Split In Batches node processes one Instagram handle at a time. After each profile is fully processed (transcribed, analyzed, rewritten, and saved), the loop returns here to grab the next one. This prevents API overload and makes debugging easier.

Part 2 — Fetching and Processing Video

4 Fetch Latest Instagram Post (HTTP Request)

This node calls the Scrape Creators API to retrieve the most recent post from the current Instagram handle. It sends a GET request with the handle and a limit of 1, returning the post’s metadata including video URLs, caption, engagement counts, and timestamps.

{
  "items": [{
    "id": "3456789012345678901",
    "caption": { "text": "This AI tool is completely free..." },
    "video_versions": [
      { "url": "https://scontent.cdninstagram.com/v/..." }
    ],
    "like_count": 12453,
    "comment_count": 892,
    "play_count": 245000
  }]
}

5 Extract Video URLs (Set Node)

Pulls out the key fields we need from the Instagram API response: the high-resolution video URL, a low-res fallback, the caption text, and the post ID. This keeps the data clean for downstream nodes.

6 Download Video (HTTP Request)

Takes the video URL from the previous step and downloads the actual MP4 file from Instagram’s CDN. The binary data is passed directly to the next node for transcription. Error handling is set to continue even if the download fails (some posts may be images, not videos).

7 Transcribe Video (OpenAI Whisper)

Sends the downloaded video file to OpenAI’s Whisper model for speech-to-text transcription. Whisper handles multiple languages and accents well, so it works even with international content creators. The output is a plain text transcript of everything said in the Reel.

{
  "text": "Hey there. So I just wanted to let you know about an AI text-to-speech generator that has recently become available. It doesn't cost anything at all. It has a lot of voices and the best part is you don't need to sign up..."
}
💡

Tip: Whisper costs $0.006 per minute of audio. A typical 60-second Reel costs less than a penny to transcribe.

Part 3 — AI Content Analysis and Script Rewriting

8 Analyze Content with GPT-4O

This is where the intelligence happens. GPT-4O receives the transcript and performs three tasks in one call: it decides whether the content is about a tool, technology, or AI topic worth repurposing (verdict: true/false). If relevant, it identifies the specific tools mentioned, writes step-by-step usage instructions, and suggests how to make the content more appealing to your audience.

{
  "verdict": "true",
  "tools": ["Speechma"],
  "stepByStep": "1. Visit speechma.com\n2. Enter your text...",
  "suggestion": "Create a comparison test against ElevenLabs...",
  "searchPrompt": "Speechma, the AI text-to-speech tool"
}
📌

If the verdict is “false” (the Reel wasn’t about a relevant tool), the workflow still continues through the remaining nodes but produces empty results. You can add an IF node after this step to skip irrelevant content entirely.

9 Research Tools via Perplexity (HTTP Request)

Uses the searchPrompt from the previous step to query Perplexity AI’s Sonar Pro model. The prompt asks for three interesting, peculiar facts about the tool. This gives your rewritten script unique angles that the original creator didn’t cover, making your content genuinely original rather than a simple rehash.

10 Write Repurposed Script (GPT-4O)

The final AI step. GPT-4O receives everything gathered so far: the tool names, the original rough transcript, the Perplexity research, the step-by-step guide, and the content improvement suggestions. It produces a polished ~100-word script in a casual, direct tone with a call-to-action ending.

The prompt includes a one-shot example so GPT-4O understands the exact format and tone you want. You can customize this example in the node parameters to match your brand voice.

{
  "script": "Hey\u2014there's a completely new AI text to speech generator that's just as good as ElevenLabs, but FREE.\nIt allows you to choose from over 400 voices, it supports 60 languages, and it's all completely unlimited with no signup required.\nJust head over to this website\nPaste your text\nChoose one of the voices\nAnd click generate\nThat's it! Your AI speech is now ready to use.\nYou can use this voice for YouTube videos, TikToks, or whatever you like.\nSo go try it yourself.\nJust Comment \"speech\" and I'll share the link with you."
}

Part 4 — Saving Results

11 Save Results to Google Sheets

Writes all the collected data to your output sheet in one row per profile. The row includes the post ID, timestamp, caption, video URL, engagement metrics (likes, comments, views), video duration, the original transcript, and the rewritten script. The node uses “append or update” mode, matching on post ID so re-running the workflow won’t create duplicates.

After saving, execution loops back to Step 3 to process the next Instagram profile on your list.

The Data Structure

The workflow uses two tabs in the same Google Sheet. Here’s the schema for the output tab where results are saved:

Column Type Example Description
id String 3456789012345678901 Unique Instagram post ID (used as match key to prevent duplicates)
Username String mkbhd The Instagram handle this Reel came from
timeStamp ISO Date 2026-04-08T14:23:00.000Z When the original Reel was posted
caption String This AI tool is completely free… The original caption text from the Instagram post
videoUrl URL https://scontent.cdninstagram.com/… Direct link to the video file
likesCount Number 12453 Number of likes on the original post
commentcount Number 892 Number of comments on the original post
videoViewsCount Number 245000 Number of video views/plays
Duration Number 58.2 Video length in seconds
original Script String Hey there. So I just wanted to… Raw Whisper transcription of the audio
rewritten Script String Hey—there’s a completely new… AI-rewritten script ready to record
📌

Column names must match exactly as shown above. The workflow references these names in expressions. If you rename columns in Google Sheets, update the corresponding field mappings in the Save Results node.

Full System Flow

  Google Sheet ("profiles" tab)
       │
       ▼
  ┌──── LOOP (per profile) ───────────────────────────────────────────┐
  │  Scrape Creators API → Download MP4 → Whisper (transcript)    │
  │       │                                                         │
  │       ▼                                                         │
  │  GPT-4O Analyze → Perplexity Research → GPT-4O Rewrite         │
  │       │                                                         │
  │       ▼                                                         │
  │  Save to Google Sheet ("output" tab)                             │
  └─────────────────────────────────────────────────────────────┘
  

Testing Your Workflow

  1. Add 2–3 Instagram handles to your “profiles” sheet. Pick accounts that post Reels about tools or tech (e.g., mkbhd, mattvidpro).
  2. Click Execute Workflow in n8n and watch the execution log.
  3. After it finishes, open your output Google Sheet. You should see one row per profile with all fields populated.
  4. Read the “rewritten Script” column. It should contain an original, ~100-word script in a casual tone with a CTA at the end.
Problem Likely Cause Fix
Fetch node returns empty Account is private or handle is misspelled Double-check the handle exists and the account is public
Transcription is blank The latest post is an image, not a video The account’s most recent post must be a Reel with audio
GPT-4O says verdict: false The Reel wasn’t about a tool or technology This is expected. The workflow filters for relevant content only
Perplexity returns an error API key missing or wrong model name Verify the Authorization header includes “Bearer ” prefix. Check the model is “sonar-pro”
Sheets not updating Wrong spreadsheet ID or sheet name Open the Google Sheets nodes and reselect your spreadsheet from the dropdown

Frequently Asked Questions

Can I use this workflow for niches other than AI and automation?

Yes. The GPT-4O prompts in the Analyze and Write Script nodes are configured for an AI/automation audience by default, but you can edit them to target any niche. Change the system prompt and the filtering criteria to match fitness, finance, cooking, or whatever your channel covers.

How much does it cost to run per profile?

Roughly $0.02–0.05 per profile. Whisper transcription costs about $0.006/minute, GPT-4O runs two calls at ~$0.01 each, and Perplexity is $0.005 per query on the Sonar Pro plan. The Scrape Creators API cost depends on your plan.

What happens if an Instagram account posts an image instead of a Reel?

The Download Video and Transcribe Video nodes have error handling set to “continue on error.” If there’s no video to download, those steps produce empty output. GPT-4O will return a verdict of “false” and the row will still be saved with whatever metadata was available.

Can I run this automatically on a schedule instead of manually?

Absolutely. Replace the Manual Trigger node with a Schedule Trigger. Set it to run daily at a specific time (e.g., 8:00 AM) and you’ll have fresh scripts waiting for you every morning.

Is there a risk of Instagram blocking my requests?

The Scrape Creators API handles Instagram access through their infrastructure, so your IP isn’t exposed. However, hitting the API aggressively (hundreds of requests per minute) could trigger their rate limits. The loop-based design of this workflow naturally throttles requests to a safe pace.

🚀 Get the Instagram Content Repurposing Template

Skip the 45-minute build. Get the complete workflow JSON, a setup guide, and a credentials guide with step-by-step API key instructions for every service.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Add a Telegram or Slack notification after the script is written so you get an instant alert with the rewritten content.
  • Build a TikTok version using a TikTok scraping API instead of Scrape Creators to repurpose short-form video from multiple platforms.
  • Add an AI image generation step (DALL-E or Midjourney via API) to auto-create thumbnail images alongside each script.
  • Connect to a content calendar (Notion, Airtable, or Trello) to automatically schedule the rewritten scripts for production.
n8n
Instagram
OpenAI
GPT-4O
Perplexity
Google Sheets
Whisper
content repurposing
automation
social media

How to Auto-Generate LinkedIn Posts from Your Blog with n8n and AI

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

You publish a blog post every week. It’s great content: researched, written, polished. But then you face a familiar problem: how do you turn that article into a compelling LinkedIn post? Do you manually rewrite it? Copy-paste? Start from scratch? You end up spending 20 minutes crafting something that captures the essence of your article, and you repeat this every single week.

What if that rewriting happened automatically?

This guide walks you through building a workflow that pulls your latest blog posts from Ghost CMS, feeds them to an AI agent powered by GPT-4o-mini, and saves LinkedIn-ready promotional posts to a Google Sheet, all on a schedule, no manual work required. Get the complete template below.

What You’ll Build

By the end of this tutorial, you’ll have a fully automated workflow that:

  1. Fetches your latest blog posts from Ghost CMS every Monday morning at 9am
  2. Cleans up the HTML content to extract just the text, removing all markup and formatting noise
  3. Sends each post to an AI agent (GPT-4o-mini) with a custom prompt to generate a professional LinkedIn promotional post
  4. Appends everything to a Google Sheet where you can review, refine, or directly copy the AI-generated post to LinkedIn
  5. Repeats automatically every week, giving you a constantly growing library of pre-written LinkedIn content

How It Works: The Big Picture

Here’s the workflow architecture at a glance:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Schedule Trigger    β”‚ (Every Monday 9am)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Extract Blog Posts  β”‚ (Ghost CMS - getAll, limit 3)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Map Post Fields     β”‚ (Set node - extract id, title, etc.)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Process Each Post   β”‚ (SplitInBatches - batch size 1)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Strip HTML Tags     β”‚ (Code node - JS to remove markup)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Combine Post Data   β”‚ (Merge node - SQL combine)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Generate LinkedIn   β”‚ (AI Agent - GPT-4o-mini)
β”‚ Post                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Merge AI Output     β”‚ (Merge node - combine with original)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Save to Sheets      β”‚ (Google Sheets - append rows)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Each blog post flows through this pipeline independently. The workflow extracts relevant data, cleans it, feeds it to AI, and stores the result in a structured spreadsheet for your review.

What You’ll Need

Before you start building, make sure you have:

  • n8n account (free tier works fine, or self-hosted)
  • Ghost CMS with at least 3 published blog posts
  • Ghost Admin API key (generate in Settings β†’ Integrations)
  • OpenAI API key with access to GPT-4o or GPT-4o-mini
  • Google Sheets API credentials (or just use n8n’s built-in Google Sheets connector)
  • A Google Sheet ready to receive the data
  • Time commitment About 45 minutes to build and test the entire workflow

Building the Workflow

Let’s build this step by step. I’ll walk you through each node, what it does, and how to configure it.

1 Schedule Trigger: Weekly Automation

Start with a Schedule node to run your workflow every Monday morning at 9am Eastern Time.

Configuration:

  • Choose Recurring as the trigger type
  • Set Trigger Type to Weekly
  • Select Monday (or your preferred day)
  • Set the time to 09:00:00 (9am)
  • Set timezone to America/New_York

This node outputs a single object with a timestamp. It doesn’t pass data forward. It just kicks off the workflow on schedule.

πŸ“Œ

Pro tip: If you want to test the workflow immediately without waiting for Monday, you can manually trigger it by clicking the “Execute Workflow” button in n8n’s editor. No need to change the schedule.

2 Extract Blog Posts: Pull from Ghost CMS

Next, add a Ghost node configured to fetch your latest blog posts.

Configuration:

  • Create a new Ghost connection using your Admin API key
  • Set the resource to Posts
  • Set the operation to Get All
  • Under “Options,” set Limit to 3 (fetch the 3 most recent posts)
  • Enable Include HTML so we capture the full content

Expected output (sample):

[
  {
    "id": "post_5a2k8x9m",
    "title": "5 Ways to Automate Your Marketing Funnel in 2026",
    "featured_image": "https://ghost.easyworkflows.net/content/images/2026/04/marketing-funnel.jpg",
    "excerpt": "Automation is no longer a luxury...",
    "html": "<h2>Automation is...</h2><p>...",
    "slug": "5-ways-automate-marketing-funnel-2026",
    "published_at": "2026-04-08T08:00:00Z"
  },
  ...
]

3 Map Post Fields: Extract What We Need

Now use a Set node to pluck out just the fields we care about. This keeps our data clean and reduces noise downstream.

Configuration:

  • Add a Set node after the Ghost node
  • In the “Set” section, map these fields from the Ghost posts:
    • id ← id
    • title ← title
    • featured_image ← featured_image
    • excerpt ← excerpt
    • content ← html
    • link ← construct using slug: https://yourblog.ghost.io/{{$node["Ghost"].data.slug}}/

At this point, each post has a clean data structure with just what we need.

4 Process Each Post: Use SplitInBatches

Since we fetched multiple posts, we need to process them one at a time. A SplitInBatches node lets us handle each post independently before merging results back together.

Configuration:

  • Add a SplitInBatches node
  • Set Batch Size to 1
  • Set Options β†’ Timeout to 120 seconds (gives AI time to respond)

This node splits the array of posts into single-item batches. Each batch loops through the remaining nodes.

5 Strip HTML Tags: Clean the Content

The Ghost CMS gives us HTML-rich content, but we want plain text for the AI. A Code node will strip all HTML tags and clean up whitespace.

Configuration:

  • Add a Code node (JavaScript)
  • Paste this function:
const htmlContent = $node["Map Post Fields"].data.content;

// Strip HTML tags
let cleanText = htmlContent.replace(/<[^>]+>/g, '');

// Decode HTML entities
cleanText = cleanText
  .replace(/&/g, '&')
  .replace(/</g, '<')
  .replace(/>/g, '>')
  .replace(/"/g, '"')
  .replace(/'/g, "'");

// Remove extra whitespace
cleanText = cleanText
  .replace(/\s+/g, ' ')
  .trim();

return { clean_content: cleanText };

Output example:

{
  "clean_content": "5 Ways to Automate Your Marketing Funnel in 2026 Automation is no longer a luxury. It's a necessity..."
}

6 Combine Post Data: Merge Original + Cleaned

We now have two pieces of data floating around: the original post fields and the cleaned content. A Merge node combines them back into a single, complete object.

Configuration:

  • Add a Merge node
  • Merge mode: Combine
  • Input 1: Output from “Map Post Fields” (original fields)
  • Input 2: Output from “Strip HTML Tags” (cleaned content)

Result:

{
  "id": "post_5a2k8x9m",
  "title": "5 Ways to Automate Your Marketing Funnel in 2026",
  "featured_image": "https://...",
  "excerpt": "Automation is no longer a luxury...",
  "content": "<h2>...</h2>...",
  "link": "https://yourblog.ghost.io/...",
  "clean_content": "5 Ways to Automate... [full plain text]"
}

7 Generate LinkedIn Post: AI Agent with GPT-4o-mini

Now the magic happens. We send the cleaned blog content to an AI Agent node powered by OpenAI, which generates a professional LinkedIn promotional post.

Configuration:

  • Add an AI Agent node
  • Model: gpt-4o-mini
  • Credentials: Connect your OpenAI API key
  • System Prompt: Copy and customize this:
    You are a LinkedIn content specialist. Your job is to transform blog articles into engaging, professional LinkedIn posts.
    
    Guidelines:
    - Keep it between 3-5 sentences
    - Use 1-2 relevant emojis (but not too many)
    - Include a call-to-action at the end (e.g., "Read the full article below" or "What's your experience?")
    - Maintain a professional but friendly tone
    - Focus on the key insight or takeaway from the blog post
    - Do NOT include hashtags
    
    Format your response as plain text only.
  • User Message: Set this to:
    Blog Title: {{$node["Combine Post Data"].data.title}}
    
    Blog Content:
    {{$node["Combine Post Data"].data.clean_content}}
    
    Generate a LinkedIn promotional post for this blog article.

Expected output:

"In 2026, your marketing stack is only as strong as your automation. We just published a deep dive into 5 game-changing automation strategies that cut manual work, reduce errors, and scale your growth.

Whether you're managing leads, nurturing prospects, or coordinating campaigns, automation does the heavy lifting. Curious how? Check out the full breakdown below. πŸ“ˆ

What automation tool has made the biggest impact for you?"
πŸ’‘

Not getting the tone you want? Tweak the system prompt. Ask the AI to be more casual, more technical, more sales-focused, whatever fits your brand. The beauty of AI agents is they adapt to your instructions.

8 Merge AI Output: Combine Generated Post with Metadata

The AI agent returned a LinkedIn post, but we also want to keep the original blog metadata (title, link, featured image) so we can reference them in the Google Sheet.

Configuration:

  • Add another Merge node
  • Merge mode: Combine
  • Input 1: Output from “Combine Post Data” (all original + cleaned fields)
  • Input 2: Output from “Generate LinkedIn Post” (the AI-generated text)
  • In Input 2, set the field name to linkedin_post so the AI output is clearly labeled

Final merged object:

{
  "id": "post_5a2k8x9m",
  "title": "5 Ways to Automate Your Marketing Funnel in 2026",
  "featured_image": "https://...",
  "excerpt": "Automation is no longer a luxury...",
  "content": "<...>",
  "clean_content": "5 Ways to Automate... [plain text]",
  "link": "https://yourblog.ghost.io/...",
  "linkedin_post": "In 2026, your marketing stack is only as strong..."
}

9 Save to Google Sheets: Append the Results

Finally, append each row to your Google Sheet so you have a growing library of LinkedIn posts ready to go.

Configuration:

  • Add a Google Sheets node
  • Credentials: Authenticate with your Google account
  • Spreadsheet: Select or paste the ID of your sheet
  • Sheet: Choose the sheet tab (e.g., “Posts”)
  • Resource: Append
  • Columns to append: Map these fields:
    • id
    • title
    • featured_image
    • excerpt
    • link
    • clean_content
    • linkedin_post

Each week, new rows are added to the bottom of your sheet with the original blog data and the AI-generated post.

The Data Structure

Here’s exactly how your Google Sheet should be organized. Create these column headers in row 1:

id title featured_image excerpt link clean_content linkedin_post
post_5a2k8x9m 5 Ways to Automate Your Marketing Funnel in 2026 https://ghost.easyworkflows.net/content/images/2026/04/marketing-funnel.jpg Automation is no longer a luxury. It’s a necessity… https://blog.easyworkflows.net/marketing-automation-2026/ 5 Ways to Automate Your Marketing Funnel in 2026 Automation is no longer a luxury… In 2026, your marketing stack is only as strong as your automation. We just published a deep dive into 5 game-changing automation strategies…
post_3j9m2x5k Why n8n is Better Than Zapier for Complex Workflows https://ghost.easyworkflows.net/content/images/2026/04/n8n-vs-zapier.jpg When it comes to no-code automation, flexibility matters… https://blog.easyworkflows.net/n8n-vs-zapier-comparison/ Why n8n is Better Than Zapier for Complex Workflows When it comes to no-code automation, flexibility matters… Ever hit a wall with Zapier because it can’t do exactly what you need? n8n is different. We compared side-by-side, and the results might surprise you. Read our full breakdown…

The featured_image column is great for visual reference. The link column lets you click straight to the blog post. And linkedin_post is what you’ll actually copy into LinkedIn when you’re ready to post.

Full System Flow

Here’s a more detailed view of the entire workflow end-to-end:

WORKFLOW: Auto-Generate LinkedIn Posts from Blog Content

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 1. SCHEDULE TRIGGER                                  β”‚
β”‚    Runs: Every Monday at 9:00 AM (America/New_York) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 2. GHOST CMS NODE                                    β”‚
β”‚    Fetches: Latest 3 published blog posts            β”‚
β”‚    Fields: id, title, excerpt, html, featured_image β”‚
β”‚    Output: Array of 3 post objects                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 3. SET NODE (Map Fields)                             β”‚
β”‚    Extracts: id, title, featured_image, excerpt,    β”‚
β”‚             content (html), link (slug-based)        β”‚
β”‚    Output: Cleaned post object                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 4. SPLIT IN BATCHES NODE                             β”‚
β”‚    Batch Size: 1                                     β”‚
β”‚    Processes: Each post individually in loop         β”‚
β”‚    Output: Single post object (batch)                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 5. CODE NODE (Strip HTML)                            β”‚
β”‚    Removes: All HTML tags                            β”‚
β”‚    Cleans: Whitespace, HTML entities                β”‚
β”‚    Output: { clean_content: "plain text..." }        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 6. MERGE NODE (Combine)                              β”‚
β”‚    Input 1: Original post fields                     β”‚
β”‚    Input 2: clean_content from Code node             β”‚
β”‚    Output: Single object with all fields             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 7. AI AGENT NODE (OpenAI GPT-4o-mini)                β”‚
β”‚    System Prompt: LinkedIn content specialist        β”‚
β”‚    Input: Blog title + clean_content                 β”‚
β”‚    Generates: Professional LinkedIn post (3-5 sent.) β”‚
β”‚    Output: { text: "In 2026, your marketing..." }    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 8. MERGE NODE (Combine with Metadata)                β”‚
β”‚    Input 1: All original fields + clean_content      β”‚
β”‚    Input 2: AI-generated linkedin_post               β”‚
β”‚    Output: Complete object ready for Sheets          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 v
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 9. GOOGLE SHEETS NODE (Append)                       β”‚
β”‚    Spreadsheet: "LinkedIn Auto-Posts"                β”‚
β”‚    Sheet Tab: "Posts"                                β”‚
β”‚    Appends: 1 row per blog post                      β”‚
β”‚    Columns: id, title, featured_image, excerpt,      β”‚
β”‚            link, clean_content, linkedin_post        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

RESULT: Every blog post β†’ 1 professional LinkedIn post
        Stored in Google Sheet for review & scheduling

Testing Your Workflow

Before you set it to run on schedule, test it end-to-end. Here’s the checklist:

  1. Execute the workflow manually from the n8n editor (click the play button)
  2. Check the Ghost node output: do you see your 3 recent posts?
  3. Check the Set node output: are all the fields (title, link, content) correct?
  4. Check the Code node output: is the HTML stripped? Is clean_content plain text?
  5. Check the AI Agent output: is the generated LinkedIn post sensible? Does it match your tone?
  6. Check Google Sheets: did the row appear? Are all columns populated?
  7. Copy the LinkedIn post to LinkedIn: does it read well? Would you actually post it?

Troubleshooting table:

Problem Likely Cause Solution
Ghost node returns no posts Admin API key invalid or no published posts Verify API key in Ghost integration; ensure you have 3+ published posts
AI Agent times out Content too long or OpenAI API slow Reduce blog excerpt length; increase timeout in SplitInBatches to 180s
Google Sheets append fails Missing column headers or wrong Sheet ID Manually create header row in Sheet; verify Spreadsheet ID matches
AI-generated post is bland System prompt too generic Customize system prompt with your brand voice, target audience, examples
Duplicate rows in Sheet Workflow executed multiple times Check n8n execution logs; disable “activate” if not ready to run on schedule

Frequently Asked Questions

Can I use WordPress or RSS instead of Ghost CMS?

Absolutely. Replace the Ghost node with a WordPress node (if available in n8n) or use an RSS node to pull your latest posts. The rest of the workflow stays the same. You’ll just need to adjust the field mappings to match WordPress’s output format (e.g., post_content instead of html).

Can I use a different AI model instead of GPT-4o-mini?

Yes. You can swap in Claude 3.5 Sonnet, Gemini, or any other LLM supported by n8n. The workflow structure stays identical. Just update the AI Agent node credentials and model selection. Different models may produce slightly different tones, so test and see which you prefer.

How do I customize the LinkedIn post style to match my brand voice?

Edit the System Prompt in the AI Agent node (Step 7). Add specific instructions about your brand voice, target audience, desired length, tone, and format. For example: “Use more technical language,” “Add industry jargon,” “Make it humorous,” “Include a specific CTA.” The AI will adapt accordingly.

Can I auto-post directly to LinkedIn instead of saving to a sheet first?

Not yet. LinkedIn’s API is limited, and n8n doesn’t have a direct “post to LinkedIn feed” node. However, you can use LinkedIn’s RSS feed directly or integrate with a tool like Buffer or Later that connects to n8n. For now, using Google Sheets as a review layer is the safest approach; it gives you a chance to tweak the AI output before publishing.

How many blog posts can I process at once?

The workflow processes posts one at a time (batch size 1) to avoid rate-limiting and stay within API cost bounds. If you want to fetch more than 3 posts, increase the Limit in the Ghost node. Keep in mind: each post calls the OpenAI API, so processing 10 posts will cost more than processing 3. Start with 3 and scale as needed.

πŸ’‘

Want to take this further? Try setting up a second workflow that watches your Google Sheet and automatically schedules posts to LinkedIn via Buffer on specific days. Or build a variation that emails the LinkedIn post to your team for approval before it hits the sheet. Check out our templates library for more advanced workflows.

Get the Complete Workflow Template

Don’t want to build it from scratch? We’ve packaged the entire workflow, all 9 nodes pre-configured and ready to import, so you can get up and running in minutes, not hours.

Download the Template

Includes step-by-step setup guide and troubleshooting tips.

What’s Next?

You now have a powerful foundation. Here are four natural extensions to consider:

  1. Multi-platform distribution: Fork the workflow to generate Twitter posts, email newsletters, or Slack announcements from the same blog content.
  2. A/B testing variants: Call the AI agent twice with different system prompts and store both versions in the sheet. See which one gets more engagement on LinkedIn, then refine your prompt based on the winner.
  3. Sentiment and keyword extraction: Add a node to analyze the blog post sentiment and extract key topics, storing them in the sheet for SEO and content tracking.
  4. Scheduled LinkedIn publishing: Integrate with Buffer or another scheduling tool to automatically queue the posts for posting at optimal times, bypassing the manual copy-paste step entirely.

The beauty of n8n is that each workflow is a building block. Start here, learn what works for your brand, and expand from there.


Questions? Run into issues? The n8n community forum is incredibly active, and our templates team is always available. Happy automating!

n8n
Ghost CMS
LinkedIn
OpenAI
Google Sheets
automation
content marketing

How to Build an AI Content Factory with n8n (Google Trends to Blog, Instagram & TikTok)

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

Chasing trends is exhausting. You’re scrolling Google Trends hoping something resonates, sketching outlines, writing blog posts, designing graphics, scripting videos, all for three different platforms. By the time you publish, the trend’s already fading. What if your entire content machine ran itself? Every 8 hours, a workflow discovers the hottest trending topic, generates a polished 3000-word blog outline, a 5-slide Instagram carousel concept, and a 60-second TikTok script, then sends them to you for a single-click approval. That’s the power of an AI Content Factory built with n8n, OpenAI, and Slack.

Ready to automate your content creation pipeline? Get the complete workflow template to start building.

What You’ll Build

This workflow transforms you from a content creator into a content director. Here’s the five-step experience:

  1. Trend discovery runs automatically. Every 8 hours, the system fetches the US Google Trends RSS feed and identifies the single most viral topic using AI analysis.
  2. Three AI agents work in parallel. A blog writer generates a 3000-word SEO outline with proper heading hierarchy, an Instagram designer dreams up a 5-slide carousel concept, and a TikTok writer scripts a punchy 60-second video hook.
  3. Content is reviewed via Slack. A beautifully formatted message arrives in your Slack channel with preview text and one-click Approve/Reject buttons.
  4. You approve or reject instantly. No need to touch n8n. Just click in Slack, and the workflow reacts in real time.
  5. Approved content lands in Google Sheets. All three content pieces (blog, Instagram, TikTok) save to a spreadsheet you can export, edit, or feed into your publishing tools.

How It Works: The Big Picture

Here’s the complete flow, from trend detection to Sheets:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Schedule Trigger (Every 8 hours)                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────────────────────────────────────┐
β”‚  Config Node (Google Trends URL, Slack channel)             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────────────────────────────────────┐
β”‚  HTTP Request β†’ Google Trends Daily RSS Feed (US)           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────────────────────────────────────┐
β”‚  AI Agent: Trend Filter (GPT-4o-mini)                       β”‚
β”‚  β†’ Selects 1 viral topic + reason + target audience         β”‚
β”‚  β†’ Returns JSON with structured output                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚                                      β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€v────────┐         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────┐
    β”‚ AI Agent:       β”‚         β”‚ AI Agent:                 β”‚
    β”‚ Blog Writer     β”‚         β”‚ Instagram Designer        β”‚
    β”‚ (GPT-4o-mini)   β”‚         β”‚ (GPT-4o-mini)             β”‚
    β”‚ β†’ 3K word SEO   β”‚         β”‚ β†’ 5-slide carousel        β”‚
    β”‚   outline       β”‚         β”‚   concept                 β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚                               β”‚
             β”‚      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
             β”‚      β”‚ AI Agent:        β”‚    β”‚
             β”‚      β”‚ TikTok Script    β”‚    β”‚
             β”‚      β”‚ (GPT-4o-mini)    β”‚    β”‚
             β”‚      β”‚ β†’ 60-sec script  β”‚    β”‚
             β”‚      β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────────────┐
        β”‚  Merge (3 inputs)              β”‚
        β”‚  Combine all 3 AI outputs      β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────────────┐
        β”‚  Code Node (JavaScript)        β”‚
        β”‚  Sanitize text + structure     β”‚
        β”‚  β†’ blog_content                β”‚
        β”‚  β†’ instagram_content           β”‚
        β”‚  β†’ tiktok_script               β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────────────┐
        β”‚  Slack Message (Block Kit)     β”‚
        β”‚  Send Approve/Reject buttons   β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────────────┐
        β”‚  Wait for Webhook Callback     β”‚
        β”‚  Pause until user clicks       β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€v────────────────┐
        β”‚  IF: query.action === approve? β”‚
        β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”˜
    YES β”‚                              β”‚ NO
        β”‚                         (End)β”‚
    β”Œβ”€β”€β”€vβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚
    β”‚  Google Sheets Append
    β”‚  β†’ blog_content, instagram_content,
    β”‚    tiktok_script (3 columns)
    β”‚  βœ“ Content saved
  

What You’ll Need

Before you start, gather these pieces:

  • n8n instance. Free cloud at n8n.cloud or self-hosted
  • OpenAI API key. GPT-4o-mini model access (budget ~$0.20 to $0.50 per workflow run)
  • Slack workspace. With bot token (scopes: chat:write, incoming-webhook)
  • Google Sheets. One sheet with 3 columns: blog_content, instagram_content, tiktok_script
  • Google Trends RSS URL. US daily trends: https://trends.google.com/trends/trendingsearches/daily/rss?geo=US
  • Time. ~45 minutes to set up (most time spent configuring credentials)

Part 1: Trend Discovery

1 Schedule Trigger

Start with a Schedule Trigger node set to repeat every 8 hours. This ensures your content factory runs throughout the day: morning, afternoon, evening. Choose a time that fits your workflow; 9 AM, 5 PM, and 1 AM is a solid split.

πŸ’‘

Pro tip: If you want manual control too, add a Manual trigger in parallel so you can test anytime.

2 Config Node

Next, create a Set node called “Config” to store your constants. This keeps your workflow clean and reusable:

  • google_trends_url = https://trends.google.com/trends/trendingsearches/daily/rss?geo=US
  • slack_channel = #content-approvals
  • slack_bot_token = (your Slack bot token)

Store these as {{ $node["Config"].json.google_trends_url }} when you reference them later. This pattern keeps credentials safe if you share the workflow.

3 HTTP Request: Fetch Google Trends

Add an HTTP Request node with these settings:

  • Method: GET
  • URL: {{ $node["Config"].json.google_trends_url }}
  • Response Format: JSON

Google Trends serves an RSS feed. n8n automatically parses it into JSON. You’ll get back a list of trending topics with titles, descriptions, and images. The structure looks like this:

{
  "rss": {
    "channel": {
      "item": [
        {
          "title": "Climate Crisis 2026",
          "description": "Up 2,100% in the last 24 hours",
          "link": "https://..."
        },
        {
          "title": "AI Robotics Breakthrough",
          "description": "Up 1,850% in the last 24 hours"
        }
        // ... more items
      ]
    }
  }
}

The HTTP Request node extracts these automatically. You’ll reference {{ $node["HTTP Request"].json.rss.channel.item }} in the next step.

4 AI Agent: Trend Filter

Now the AI enters. Create an AI Agent node (OpenAI) that analyzes all trends and picks the single most viral topic. Configure it like this:

  • Model: GPT-4o-mini
  • Prompt:
You are a trend analyst. I've given you a list of today's Google Trends.
Analyze each trend's growth metrics and cultural relevance.
Pick THE SINGLE most viral topic that will appeal to a broad creator audience.
Return a JSON object with exactly these fields:
{
  "selected_topic": "exact trend title",
  "reason": "2-sentence explanation of why this trend is viral",
  "target_audience": "who should care about this (e.g., Gen Z, fitness enthusiasts)"
}

Set Structured Output Parser to JSON mode. Pass in {{ $node["HTTP Request"].json.rss.channel.item }} as the input data. The AI Agent will return structured JSON like:

{
  "selected_topic": "AI-Powered Fitness Wearables",
  "reason": "Up 2,340% in last 24h. Combines AI hype + health trend + consumer gadgets.",
  "target_audience": "Tech enthusiasts, fitness influencers, early adopters"
}

Part 2: Parallel Content Generation

Now that you have the viral topic, three AI agents run simultaneously to create content for different platforms. This is where n8n’s parallel execution shines.

5 AI Agent: Blog Writer

Add an AI Agent node called “Blog Writer” with this prompt:

You are an expert SEO content strategist. I need a 3000-word blog post outline
on this topic: "{{ $node["Trend Filter"].json.selected_topic }}"

Create a comprehensive outline with:
- H1: Catchy main title
- H2 sections (5 to 7 main sections)
- H3 subsections under each H2
- 2 to 3 bullet points under each H3
- Include an intro section, an FAQ section, and a CTA section

Target audience: {{ $node["Trend Filter"].json.target_audience }}

Output as plain text outline (not HTML).

Model: GPT-4o-mini. This generates a roadmap for writers or AI-to-content pipelines. The output is ready to feed to a blog post writer or expanded into full prose.

6 AI Agent: Instagram Designer

Add another AI Agent called “Instagram Designer” with this prompt:

You are a social media content strategist specializing in Instagram.
Create a 5-slide carousel concept for this trending topic: "{{ $node["Trend Filter"].json.selected_topic }}"

Slide breakdown:
- Slide 1 (Cover): Eye-catching headline + hook
- Slides 2-4 (Info): Key insights, stats, or benefits (one concept per slide)
- Slide 5 (CTA): Call-to-action (link bio, DM for more, etc.)

For each slide, write:
1. Text copy (max 150 chars per slide)
2. Visual concept (describe the design, colors, layout)
3. Recommended hashtags

Target audience: {{ $node["Trend Filter"].json.target_audience }}

Output as plain text instructions for a designer or content creator.

The AI creates a blueprint that you or a designer can use to create the actual carousel graphics.

7 AI Agent: TikTok Script Writer

Create a third AI Agent called “TikTok Script Writer”:

You are a TikTok content strategist. Write a 60-second TikTok script on this topic:
"{{ $node["Trend Filter"].json.selected_topic }}"

Script structure:
- Hook (0 to 3 seconds): Stop-the-scroll opener
- Body (3 to 50 seconds): Main content, insights, or entertainment
- CTA (50 to 60 seconds): What viewers should do next

Include:
- [Action] or [Visual] cues in brackets
- Pacing notes (fast cuts, slow reveals, etc.)
- Suggested audio vibe (upbeat, educational, etc.)

Target audience: {{ $node["Trend Filter"].json.target_audience }}

Output as plain text script with brackets for visual cues.

Position all three AI agents in parallel, they don’t depend on each other, so n8n runs them simultaneously. This saves ~45 seconds per workflow run.

Part 3: Review & Approval

8 Merge Node

After the three AI agents finish, add a Merge node to combine their outputs. Set it to merge “3 into 1 row” to bundle the data:

  • Input 1: Blog Writer AI output
  • Input 2: Instagram Designer AI output
  • Input 3: TikTok Script Writer AI output

The Merge node creates a single data object with all three content pieces.

9 Code Node: Sanitize & Structure

Add a Code node (JavaScript) to clean up the text and create a final data structure:

// Sanitize helper: remove extra whitespace, HTML entities
const sanitize = (str) => {
  return str.trim().replace(/"/g, '"').replace(/&/g, '&').slice(0, 5000);
};

// Extract content from merged inputs
const blogContent = sanitize($('Blog Writer').json.text || '');
const instagramContent = sanitize($('Instagram Designer').json.text || '');
const tiktokScript = sanitize($('TikTok Script Writer').json.text || '');

return {
  blog_content: blogContent,
  instagram_content: instagramContent,
  tiktok_script: tiktokScript,
  topic: $('Trend Filter').json.selected_topic,
  timestamp: new Date().toISOString()
};

This node outputs a clean, validated JSON object ready for Slack and Google Sheets.

10 Slack: Send Approval Message

Add a Slack node to post a beautifully formatted message with Approve/Reject buttons. Use Block Kit format:

{
  "channel": "#content-approvals",
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "New Trending Content Ready for Review"
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Topic:* {{ $node["Code"].json.topic }}\n*Generated:* {{ $node["Code"].json.timestamp }}"
      }
    },
    {
      "type": "divider"
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Blog Outline Preview:*\n```{{ $node["Code"].json.blog_content.slice(0, 300) }}...```"
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Instagram Carousel:*\n```{{ $node["Code"].json.instagram_content.slice(0, 300) }}...```"
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*TikTok Script:*\n```{{ $node["Code"].json.tiktok_script.slice(0, 300) }}...```"
      }
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": {
            "type": "plain_text",
            "text": "Approve"
          },
          "style": "primary",
          "action_id": "approve_btn"
        },
        {
          "type": "button",
          "text": {
            "type": "plain_text",
            "text": "Reject"
          },
          "style": "danger",
          "action_id": "reject_btn"
        }
      ]
    }
  ]
}

11 Wait for Webhook

Add a Wait node and set it to “Receive Webhook Data.” This pauses the workflow until someone clicks a button in Slack. Configure the webhook to listen for responses with a unique identifier so n8n knows which workflow instance to resume.

12 IF Node: Check Approval

Add an IF node to evaluate the button click:

  • Condition: {{ $json.action_id }} === "approve_btn"
  • True path: Proceed to Google Sheets (next step)
  • False path: End workflow (Reject path)

13 Google Sheets: Append Content

On the True path, add a Google Sheets node to append a row with your three content pieces:

  • Operation: Append Row
  • Range: Sheet1!A:C (columns A, B, C)
  • Values:
[
  "{{ $node['Code'].json.blog_content }}",
  "{{ $node['Code'].json.instagram_content }}",
  "{{ $node['Code'].json.tiktok_script }}"
]

Each approval adds a new row to your sheet. Your Sheets file becomes a living archive of all generated content.

The Data Structure

Here’s what your Google Sheets looks like after a few approvals:

blog_content instagram_content tiktok_script
H1: AI Fitness Wearables Explained
H2: Why Wearables Matter
H3: Real-time Health Monitoring…
Slide 1: “AI wearables are here πŸ€–”
Slide 2: “Track your heart rate live”
Slide 3: “Predict health trends”…
[Fast cut] “Your fitness watch just got smarter!”
[Slow reveal] Shows wearable features
[Hook] “Link in bio for the best AI tracker”
H1: The Viral Pet Trend of 2026
H2: Why Pets Rule Social Media
H3: TikTok’s Pet Algorithm…
Slide 1: “Your pet could be famous 🐾”
Slide 2: “Pet content gets 10x engagement”
Slide 3: “Three tips for viral pet videos”…
[Upbeat music] Show cute pet moment
[Voiceover] “This got 5M views…”
[CTA] “Film your pet today!”
H1: Micro-Learning Apps Changing Education
H2: The Attention Economy
H3: 5-minute Learning Sessions…
Slide 1: “Learn anything in 5 minutes”
Slide 2: “Education goes mobile”
Slide 3: “Apps making it happen”…
[Montage] Quick lesson clips
[Stats] “1M students downloaded this week”
[CTA] “Join the learning revolution”

Full System Flow Diagram

Here’s the complete end-to-end picture:

START
  β”‚
  β”œβ”€β†’ [Schedule: Every 8h]
  β”‚
  β”œβ”€β†’ [Config: Trends URL, Slack channel]
  β”‚
  β”œβ”€β†’ [HTTP: Fetch Google Trends RSS]
  β”‚
  β”œβ”€β†’ [AI: Trend Filter (Pick 1 topic)]
  β”‚    └─→ { selected_topic, reason, audience }
  β”‚
  β”œβ”€β†’ [PARALLEL BRANCH]
  β”‚   β”œβ”€β†’ [AI: Blog Writer]
  β”‚   β”‚   └─→ 3000-word SEO outline
  β”‚   β”‚
  β”‚   β”œβ”€β†’ [AI: Instagram Designer]
  β”‚   β”‚   └─→ 5-slide carousel concept
  β”‚   β”‚
  β”‚   β”œβ”€β†’ [AI: TikTok Script Writer]
  β”‚   β”‚   └─→ 60-second script
  β”‚
  β”œβ”€β†’ [Merge: Combine 3 outputs]
  β”‚
  β”œβ”€β†’ [Code: Sanitize & structure]
  β”‚
  β”œβ”€β†’ [Slack: Send Approve/Reject message]
  β”‚
  β”œβ”€β†’ [Wait: Listen for webhook callback]
  β”‚
  β”œβ”€β†’ [IF: query.action === "approve"?]
  β”‚   β”œβ”€ YES: Google Sheets Append β†’ DONE
  β”‚   └─ NO: END (Rejected)
  β”‚
END
  

Testing Your Workflow

Test Plan

  1. Test each node individually. Use the “Test” button in n8n to run HTTP Request in isolation. Verify you get valid RSS data.
  2. Test the Trend Filter AI Agent. Mock some sample trend data and confirm it picks one topic and returns valid JSON.
  3. Test the parallel AI agents. Run Blog Writer, Instagram Designer, and TikTok Script Writer on a sample topic. Check the quality of outputs.
  4. Test the Code node. Verify it sanitizes text and structures the final JSON correctly.
  5. Test Slack integration. Send a test Block Kit message to your #content-approvals channel. Verify the layout and buttons.
  6. Test the full workflow. Trigger manually, approve in Slack, and confirm the row appears in Google Sheets within 30 seconds.

Troubleshooting

Problem Cause Solution
HTTP Request returns empty data RSS feed URL changed or is blocked Test URL directly in browser. Check if Google Trends blocks n8n IPs. Use a proxy if needed.
AI Agent returns invalid JSON Prompt unclear or model tier too low Clarify prompt with JSON example. Use GPT-4o instead of -mini for complex tasks.
Slack message doesn’t appear Bot token missing scopes or channel misspelled Verify bot has chat:write and incoming-webhook scopes. Double-check channel name (#content-approvals).
Wait node times out after 24h Slack button click not sent to webhook Verify webhook URL is correct in Slack integration. Test with manual webhook trigger first.
Google Sheets append fails Columns A, B, C don’t exist or are protected Create three blank columns in Sheet1. Ensure service account has editor access. Check range is Sheet1!A:C.

Frequently Asked Questions

How much does this cost to run?

Each workflow run consumes ~$0.20 to $0.50 in OpenAI API credits (for GPT-4o-mini’s 3 concurrent calls). Running every 8 hours = ~$1.50 to $3.75 per day, or ~$45 to $115 per month. n8n cloud is free up to 1,000 executions/month. Slack and Google Sheets are free for basic use.

Can I change the frequency? What if I want content every 4 hours?

Yes. Edit the Schedule Trigger to repeat every 4 hours. You’ll generate more content but consume 2x the OpenAI credits. For budget-conscious users, 8 to 12 hours is ideal. For agencies, 4 hours keeps content ultra-fresh.

What if I want to filter trends by category (e.g., only tech or health)?

Modify the Trend Filter AI Agent prompt to include a category constraint. Example: “Pick the most viral tech topic” or “Pick the most viral health/fitness trend.” You can also use multiple RSS feeds (Google Trends has different regional and category feeds) and use n8n’s Switch node to route to different workflows.

Can I store approvals and rejections in Google Sheets?

Absolutely. Add a second Google Sheets node in the Reject path (the False branch of the IF node) that appends to a different sheet (e.g., “Rejected Content”) with a reason field or rejection timestamp. This gives you a complete audit trail.

How do I handle multiple trends? Can the workflow pick the top 3?

Yes. Modify the Trend Filter AI Agent to return an array of 3 topics instead of 1. Then use a Loop node to iterate over the 3 topics and spawn 3 parallel content-generation sub-workflows. You’ll end up with more content but 3x the API cost. Great for high-volume content factories.

Ready to Launch Your AI Content Factory?

Stop manually hunting trends and scripting videos. Get the complete, production-ready n8n workflow template and start automating your content pipeline in minutes. Includes pre-configured nodes, all prompts, and Slack integration.

Get the Workflow Template

Includes setup guide, testing checklist, and 30-day support.

What’s Next?

You’ve built a lean, mean content machine. Here are four directions to expand:

  1. Add LinkedIn content generation. Duplicate the Blog Writer AI Agent, adjust the prompt to LinkedIn voice (professional, conversational), and add LinkedIn as a fourth platform in the Merge node. Your workflow now outputs blog, Instagram, TikTok, and LinkedIn posts simultaneously.
  2. Store full blog posts, not outlines. Replace the Blog Writer AI Agent with a more powerful model (GPT-4o) and ask it to generate the full 3000-word blog post with HTML tags. Append the HTML directly to a “Published Content” sheet or send to your WordPress site via REST API.
  3. Add image generation. After the Instagram Designer AI Agent, pipe the carousel concept to DALL-E 3 to auto-generate actual carousel images. The Slack message can preview real images instead of text concepts.
  4. Build a content calendar dashboard. Connect your Google Sheets to a tool like Airtable or a custom web dashboard. View all pending, approved, and published content in one place. Add filters by topic, platform, and date.

n8n
Google Trends
OpenAI
Slack
Google Sheets
automation
content marketing

How to Build an AI SEO Content Engine with n8n and Google Gemini

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

How to Build an AI SEO Content Engine with n8n and Google Gemini

Learn how to build an automated content engine that generates SEO and GEO-optimized articles using n8n and Google Gemini, with smart distribution to WordPress, LinkedIn, and email.

What You’ll Build

This guide shows you how to create a fully automated AI content pipeline that:

  • Watches a Google Sheet for content ideas
  • Generates SEO/GEO-optimized articles with Google Gemini
  • Intelligently routes content to the right distribution channels
  • Publishes to WordPress, LinkedIn, email, and more

Getting Started

Access the complete workflow template and setup guides at: AI SEO Content Engine Template

How to Build an AI Social Media Manager Bot with n8n

How to Build an AI Social Media Manager Bot with n8n

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

Keeping your social media channels active is a full-time job, and most founders, creators, and small teams simply don’t have the bandwidth to do it consistently. What if you could send a voice note, a document, or a quick text to a Telegram bot and have it draft, get your approval, and publish a polished post to X and LinkedIn automatically?

That’s exactly what this n8n workflow builds: your own AI-powered social media manager, running 24/7 inside Telegram, connected to Google Gemini for smart content generation, and wired directly to your X (Twitter) and LinkedIn profiles.

Prefer to skip the setup? Grab the ready-made template β†’ and be up and running in under 10 minutes.

What You’ll Build

  1. Send a text, voice note, or document to your Telegram bot, the AI understands all three formats
  2. The agent drafts a platform-appropriate post, respecting X’s 280-character limit and LinkedIn’s professional tone
  3. You review the draft in Telegram and approve it. Nothing gets published without your explicit say-so
  4. With your approval, the bot publishes instantly to X (Twitter) and/or LinkedIn
  5. The bot remembers your brand voice, company details, and preferences over time, so you stop repeating yourself in every session

How It Works: The Big Picture

The workflow uses a Telegram bot as the command center. Every incoming message is classified by type: text, document, voice, or button tap, and routed through the right processing pipeline before reaching a Google Gemini-powered AI Agent. The agent drafts posts, manages both short- and long-term memory, and uses LinkedIn and X tools to publish only after you approve.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AI SMM MANAGER                                                      β”‚
β”‚                                                                      β”‚
β”‚  [Telegram Trigger] β†’ [Set Context] β†’ [Input Type Switch]            β”‚
β”‚                                              β”‚                       β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚         ↓                ↓                   ↓                ↓      β”‚
β”‚     [Text]          [Document]            [Voice]        [Callback]  β”‚
β”‚         β”‚           ↓ Download           ↓ Download           β”‚      β”‚
β”‚         β”‚           ↓ Encode             ↓ Encode        [If cm?]   β”‚
β”‚         β”‚           ↓ Gemini OCR         ↓ Transcribe   ↓       ↓   β”‚
β”‚         β”‚           ↓ Doc Prompt         ↓ Audio Prompt [Clear] [βœ—]  β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β”‚
β”‚                               ↓                                      β”‚
β”‚                         [AI Agent] ←── [Gemini LLM]                 β”‚
β”‚                    ↑   ↑   ↑   ↑      [Short-term Memory]           β”‚
β”‚                    β”‚   β”‚   β”‚   └── [Retrieve Knowledge (vector)]     β”‚
β”‚                    β”‚   β”‚   └──── [Save Knowledge (sub-workflow)]     β”‚
β”‚                    β”‚   └──────── [Create X (Twitter) Post]           β”‚
β”‚                    └──────────── [Create LinkedIn Post]              β”‚
β”‚                               ↓                                      β”‚
β”‚                    [Split Response] β†’ [Split Out]                    β”‚
β”‚                               ↓                                      β”‚
β”‚                    [Send Response via Telegram]                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  

What You’ll Need

  • n8n (cloud or self-hosted, version 1.0+)
  • Telegram Bot API key: create one for free via @BotFather in Telegram
  • Google AI Studio API key: free at aistudio.google.com (powers Gemini 2.0 Flash)
  • X (Twitter) Developer API: free developer account at developer.x.com
  • LinkedIn OAuth credentials: create an app at the LinkedIn Developer Portal
  • A companion n8n sub-workflow: “Save Vector Store Record” (included in the template pack) for long-term memory storage

Estimated build time: 45 to 60 minutes from scratch, or under 10 minutes with the template.


Part 1: Bot Entrance and Input Preprocessing

1 Telegram Trigger (telegramTrigger)

This is the entry point. The Telegram Trigger node opens a webhook that listens for all incoming events from your bot: text messages, documents, voice notes, and inline button taps.

Configuration:

  1. Open the node and click “Credential for Telegram API”
  2. Paste your BotFather token and save
  3. Under “Updates”, enable: message, callback_query, inline_query, and *
{
  "message": {
    "chat": { "id": 198472651, "type": "private", "first_name": "Sarah" },
    "text": "Write a LinkedIn post about our product launch next Tuesday",
    "message_id": 142
  },
  "update_id": 553043001
}
πŸ’‘

Tip: Add a “Restrict to chat IDs” filter so only your account can trigger the bot. Find your personal chat ID by messaging @userinfobot on Telegram.

2 Set Input Context (set)

This node normalizes the raw Telegram payload into clean, consistently named fields, regardless of whether the input is a text message, a file, or a voice note.

FieldWhat it capturesExample
chat_idSender’s chat ID (from message or callback)198472651
message_textText content of the message"Write a post about our launch"
update_typeDetected type: text, document, voice, callback"text"
file_idDocument file ID (if document sent)"BQACAgIA..."
file_mime_typeMIME type of document"application/pdf"
file_captionCaption text sent with the document"Our company overview"
voice_file_idVoice note file ID"AwACAgIA..."

3 Input Type Switch (switch)

Routes execution to the correct processing branch based on update_type:

  • text β†’ straight to AI Agent via Text Prompt
  • document β†’ Download β†’ Encode β†’ Gemini OCR β†’ Document Prompt β†’ AI Agent
  • voice β†’ Download β†’ Encode β†’ Gemini Transcribe β†’ Audio Prompt β†’ AI Agent
  • callback β†’ If node (checks whether it’s a memory-clear command)

Part 2: Document and Audio Processing

4 Document Branch (Download β†’ Encode β†’ Describe)

When a user sends a PDF, presentation, or any file, three nodes work in sequence. Download document fetches the raw binary from Telegram using the file_id. Encode document converts the binary to base64. Describe document sends it to Gemini 2.0 Flash via HTTP POST:

{
  "contents": [{
    "parts": [
      { "text": "Extract all the information about the company or product from this document" },
      {
        "inline_data": {
          "mime_type": "application/pdf",
          "data": "JVBERi0xLjQgJeL..."
        }
      }
    ]
  }]
}

Gemini returns a structured extraction of all company and product details, which gets injected into the AI Agent prompt alongside the document’s caption.

πŸ’‘

Tip: This works with PDFs, HTML files, Word docs, and most common formats. The mime_type is pulled automatically from Telegram’s download metadata.

5 Voice Branch (Download β†’ Encode β†’ Transcribe)

Voice messages follow the same pattern: Download Audio β†’ Encode Audio β†’ call Gemini with “Generate a transcript of the speech.” The transcript becomes the chatInput field, so the agent processes it exactly like a typed message.

πŸ“Œ

Note: Telegram encodes all voice messages as OGG files. The workflow hardcodes "mime_type": "audio/ogg" in the Gemini request, so do not change this value.


Part 3: The AI Agent

6 AI Agent (agent)

The brain of the entire operation. The agent receives the normalized user input and decides autonomously what to do next: ask clarifying questions if context is missing, draft a post tailored to the target platform, send it to you for review, publish after your explicit approval, or save brand information to long-term memory.

The system message enforces a strict rule: nothing gets published without the user’s explicit “approved” signal. This is the safety net that keeps the bot from acting without consent.

ToolPurpose
Retrieve knowledgeFetches relevant brand context from the vector store
Save knowledgeCalls the sub-workflow to save new information to vector memory
Create X (Twitter) postPosts to X; enforces the 280-character limit
Create post in LinkedIn as a personPosts from your personal LinkedIn profile
Create post in LinkedIn as a companyPosts from a company/organization page

7 Google Gemini Chat Model + Memory

The agent is powered by Google Gemini via the lmChatGoogleGemini node. Two memory systems work in parallel:

Short-term memory (Buffer Window Memory): Keyed to the chat_id, this stores recent conversation history so the agent maintains context within a single Telegram session.

Long-term memory (In-Memory Vector Store + Gemini Embeddings): Powered by the embedding-001 model, this lets the agent remember brand voice, company info, and preferences across completely separate sessions.

πŸ’‘

Tip: The in-memory vector store resets when n8n restarts. For production use, replace it with a persistent store like Pinecone, Supabase pgvector, or Qdrant.

8 Memory Clear Callback

When a user taps an inline Telegram button with callback data "cm", the workflow routes to the If node, clears all buffer window memory via Chat Memory Manager, and sends a “Memory is cleaned” confirmation back to Telegram. Handy for switching between clients or starting fresh on a new campaign.


Part 4: Response Delivery

9 Split + Send (code + telegram)

Telegram enforces a 4,096-character limit per message. The Split agent’s response node uses JavaScript to break the AI output into 3,000-character chunks, and Split Out iterates over each chunk. Every chunk is sent as a separate Telegram message so nothing is ever truncated.

function splitString(str, size) {
  let result = [];
  for (let i = 0; i < str.length; i += size) {
    result.push(str.slice(i, i + size));
  }
  return result;
}

for (const item of $input.all()) {
  const parts = splitString(item.json.output, 3000);
  item.json.parts = parts;
}

return $input.all();

Full System Flow

User sends message (text / voice / document) to Telegram bot
                          β”‚
                          β–Ό
              [Telegram Trigger] β†’ [Set Input Context]
                                            β”‚
               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β–Ό                            β–Ό                   β–Ό
         [text route]               [document route]     [voice route]
               β”‚                    ↓ Download            ↓ Download
               β”‚                    ↓ Encode (base64)     ↓ Encode (base64)
               β”‚                    ↓ Gemini OCR           ↓ Gemini Transcribe
               β”‚                    ↓ Document Prompt      ↓ Audio Prompt
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                            β”‚
                                            β–Ό
                                      [AI Agent]
                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                            ↓                         ↓
                    [X & LinkedIn Tools]    [Memory: Save + Retrieve]
                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                            β”‚
                                            β–Ό
                              [Split Response (3,000 chars)]
                                            β”‚
                                            β–Ό
                              [Send each chunk via Telegram]
                                            β”‚
                                            β–Ό
                                      User gets reply βœ“
  

Testing Your Workflow

  1. Import the JSON and attach all credentials (see the Credentials Guide PDF)
  2. Activate the workflow, n8n registers the Telegram webhook automatically
  3. Open Telegram, find your bot, and send: "Write a LinkedIn post about how AI is changing small business marketing"
  4. Confirm the bot replies with a draft and asks for your approval
  5. Reply "approved": check your LinkedIn profile for the published post
  6. Test a voice note: record 10 seconds describing a product feature and send it to the bot
  7. Test document processing: send a PDF of your company overview and ask the bot to write a post about it
ProblemLikely CauseFix
Bot doesn't respond at allWorkflow not activeToggle the Active switch in n8n
"Forbidden" error posting to XTweet exceeds 280 charactersThe agent handles this, rephrase your brief to be shorter
Document not processedUnsupported mime typeStick to PDF, HTML, or DOCX formats
Memory lost after restartIn-memory vector store clearedSwitch to Pinecone or Qdrant for persistent memory
LinkedIn post failsOAuth token expiredRe-authorize LinkedIn credentials in n8n settings
No response from botGemini API quota exceededCheck your AI Studio usage dashboard and upgrade if needed

Frequently Asked Questions

Can I add more platforms like Instagram or Facebook?

Yes, n8n has native nodes for Facebook Pages and Instagram Business. Add them as additional tool nodes attached to the AI Agent, following the same pattern as the X and LinkedIn tools already in this workflow.

Does the bot ever post without my approval?

Never. The system message explicitly instructs the agent to always get explicit user approval before posting anything. The draft is always sent to Telegram first, and you can review, edit, or reject it before anything goes live.

Can I use this for multiple clients or brands?

The workflow is keyed by chat_id, so each Telegram conversation has separate memory. For multiple brands, create separate Telegram bots and run separate workflow instances, or extend the memory key to include a brand identifier prefix.

What happens if Gemini is unavailable?

The AI Agent node will throw an error and n8n will log it. For production use, add a retry setting (3 attempts, 5-second delay) on the AI Agent node and an error handler that sends you a fallback Telegram message so you know something went wrong.

Is my document content safe when processed through Gemini?

Your document data is sent to Google's API for processing. Review Google AI Studio's data usage policy at aistudio.google.com if confidentiality is a concern. For sensitive documents, consider replacing the Gemini HTTP calls with a self-hosted model via Ollama.

How do I teach the bot my brand voice?

Just tell it in plain language: "My company is Acme Corp, we sell B2B SaaS to HR teams, and our tone is professional but approachable." The Save Knowledge tool stores this automatically and the bot recalls it in all future sessions without you needing to repeat it.

πŸš€ Get the AI SMM Manager Template

Everything you need to deploy your own Telegram-powered social media bot: ready-to-import workflow JSON, Setup Guide PDF, and full Credentials Guide PDF, so you go from download to live bot in under 10 minutes.

Get the Template β†’

Instant download Β· Works on n8n Cloud and self-hosted

What's Next?

  • Add Instagram support via the Instagram Graph API as a new tool on the AI Agent node
  • Schedule posts by connecting a Google Sheets queue and a Schedule Trigger to batch-publish at optimal times
  • Add AI image generation using DALL-E or Flux to auto-create visuals for each post before publishing
  • Multi-language support: instruct the agent to detect the user's language and draft posts in the same language automatically
  • Analytics tracking: connect a Google Sheets node to log every post with timestamps so you can track what's performing
n8n Telegram Google Gemini LinkedIn Twitter / X AI Agent Social Media automation

Facebook Ads Spy Agent

Facebook Ads Spy Agent

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

Running Facebook ads without knowing what your competitors are doing is like showing up to a poker game blindfolded. The Facebook Ads Library is completely public, but manually trawling through it for every competitor, every week, is nobody’s idea of a good time. This n8n workflow fixes that: you enter a competitor’s Facebook Page URL, and an AI agent automatically scrapes their active ads, analyzes each one with Google Gemini, rewrites the copy for your own inspiration, and logs everything to a Google Sheet, hands-free.

Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.

What You’ll Build

  1. You open a simple web form and paste a competitor’s Facebook Page URL, choose how many ads to pull, pick a country, and select a date range.
  2. The workflow scrapes that page’s active ads from the Facebook Ads Library using Apify’s actor, with no API key from Meta required.
  3. Each ad is classified automatically as a Video, Image, or Text ad, and routed into its own analysis branch.
  4. Google Gemini AI analyzes video and image ads visually, extracting hooks, CTAs, emotional triggers, and conversion insights.
  5. An OpenRouter AI agent writes a full marketing summary and a rewritten version of the ad copy for each ad, then saves everything to your Google Sheet.

How It Works — The Big Picture

This workflow is a single pipeline that fans out into three parallel branches based on ad type, then converges in a shared Google Sheet.

┌────────────────────────────────────────────────────────────────────────────┐
│  FACEBOOK ADS SPY — n8n AI AGENT                                           │
│                                                                             │
│  [Form Trigger]                                                             │
│       │  URL, count, country, period                                        │
│       ▼                                                                     │
│  [Apify Scraper]  ← Facebook Ads Library Scraper actor                     │
│       │  Array of active ads                                                │
│       ▼                                                                     │
│  [Filter: 1,000+ page likes]                                                │
│       │  Quality-filtered ads                                               │
│       ▼                                                                     │
│  [Switch: Ad Type]                                                          │
│       │                                                                     │
│       ├──── Video Ad ────► [Loop] → [Download Video] → [Gemini Analyze]    │
│       │                        → [AI Agent: Summarize + Rewrite]           │
│       │                        → [Google Sheets Append] → [Loop back]      │
│       │                                                                     │
│       ├──── Image Ad ────► [Loop] → [Download Image] → [Gemini Analyze]    │
│       │                        → [AI Agent: Summarize + Rewrite]           │
│       │                        → [Google Sheets Append] → [Loop back]      │
│       │                                                                     │
│       └──── Text Ad ─────► [Loop] → [AI Agent: Summarize + Rewrite]        │
│                                → [Google Sheets Append] → [Loop back]      │
│                                                                             │
│                   ↓↓↓ ALL BRANCHES WRITE TO ↓↓↓                             │
│               [Google Sheet: Facebook Ad Spy]                               │
└────────────────────────────────────────────────────────────────────────────┘
  

What You’ll Need

  • n8n: cloud or self-hosted (v1.0+)
  • Apify account: free tier works; you’ll use the Facebook Ads Library Scraper actor
  • Google account: for Google Sheets (OAuth2)
  • Google Gemini API key: free tier available at Google AI Studio
  • OpenRouter account: pay-per-use; costs pennies per workflow run
  • A Google Sheet set up with the right column headers (schema below)

Estimated build time: 45–60 minutes from scratch, or under 10 minutes with the template.


Part 1 — Scraping Phase

1 On form submission (Form Trigger)

This is the entry point — a hosted web form n8n generates for you automatically. No coding required. Users fill it in and hit Submit.

How to configure it:

  1. Add an n8n Form Trigger node.
  2. Set the Form Title to Facebook Ad Spy.
  3. Add four fields: Ad URL (Text), Ad Count (Number), country (Text), and Ad Period (Dropdown with options: last24h, last7d, last14d, last30d).

What the data looks like after this node:

{
  "Ad URL": "https://www.facebook.com/nike/",
  "Ad Count": 20,
  "country": "US",
  "Ad Period": "last30d"
}
💡

Tip: The Form Trigger gives you a public URL you can bookmark and share with your team. No one else needs to touch n8n — they just open the form in a browser.

2 Run an Actor and get dataset (Apify)

This node calls Apify’s Facebook Ads Library Scraper and returns all active ads for the target page.

How to configure it:

  1. Add an Apify node. Set operation to Run actor and get dataset.
  2. Select actor ID XtaWFhbtfxyzqrFmd (Facebook Ads Library Scraper).
  3. In Custom Body, reference the form fields using n8n expressions to pass count, countryCode, period, and the target url dynamically.
  4. Set Memory to 512 MB and attach your Apify credential.

What each ad object looks like:

{
  "ad_archive_id": "1027384950617283",
  "start_date_formatted": "2026-02-14",
  "advertiser": {
    "ad_library_page_info": {
      "page_info": { "page_name": "Nike", "likes": 34200000 }
    }
  },
  "snapshot": {
    "page_profile_uri": "https://www.facebook.com/nike/",
    "videos": [{ "video_hd_url": "https://cdn.fbcdn.net/.../video.mp4" }],
    "cards": [],
    "body": { "message": "Just Do It. New Spring Collection." }
  }
}
💡

Tip: Apify’s free tier includes $5 in monthly credits — enough to run this workflow dozens of times. A typical 20-ad run costs roughly $0.05–$0.15.

3 Filter (1,000+ Page Likes)

This Filter node drops ads from tiny pages. If a page has fewer than 1,000 likes, the advertiser likely isn’t spending enough to produce tested creative worth analyzing. Configure the condition to check advertiser.ad_library_page_info.page_info.likes is greater than 1000. You can raise this to 10,000 or 100,000 to focus on larger brands.


Part 2 — Routing by Ad Type

4 Switch (Ad Type Router)

This Switch node forks the workflow into three branches based on the type of media in each ad.

  1. Output 0, Video Ad: checks if snapshot.videos[0].video_hd_url exists.
  2. Output 1, Image Ad: checks if snapshot.cards[0].original_image_url exists.
  3. Fallback, Text Ad: catches everything else (copy-only ads).
💡

Tip: Order matters — video is checked first. If an ad contains both video and images (carousel), it routes to the Video branch.


Part 3 — Video Ad Branch

5 Loop Over Items (Video)

A SplitInBatches node processes one video ad at a time, preventing Gemini from being overwhelmed with concurrent requests. Connect its output back from the Sheets append node to complete the loop.

6 Download Video (HTTP Request)

Downloads the raw .mp4 file from snapshot.videos[0].video_hd_url as binary data so Gemini can analyze it visually. Set response format to File / Binary.

7 Analyze Video (Google Gemini)

Gemini watches the ad video and extracts 10 marketing data points. Set the resource to Video, model to models/gemini-2.5-flash, and input type to Binary. Use this analysis prompt:

You are a direct response marketing expert.

Analyze this Facebook ad video and extract:
1. Hook (first 3 seconds message)
2. Main problem presented
3. Main solution offered
4. Target audience (who is this ad for)
5. Emotional triggers used
6. Offer details (discount, bonus, urgency)
7. Call to action
8. Video structure breakdown (scene by scene summary)
9. Why this ad might be converting
10. Suggestions to improve this ad

Be concise and structured. Focus only on marketing insights.

8 AI Agent + OpenRouter (Video)

The AI Agent takes the Gemini analysis and the raw ad JSON to produce a structured marketing summary and a rewritten version of the ad copy. Configure the agent prompt to include both the JSON scrape and the Gemini video description. Connect a Structured Output Parser with this schema:

{ "summary": "", "rewrittenAdCopy": "" }

Connect an OpenRouter Chat Model as the language model sub-node. Sample output:

{
  "summary": "Nike targets value-conscious runners with a durability-first message. Hook leads with pain point (shoes falling apart), transitions to stress-test product demo, closes with 30-day return guarantee. High conversion probability due to specific claim + risk reversal combo.",
  "rewrittenAdCopy": "Your running shoes should outlast your excuses. Our React Infinity handles 500 miles of punishment, guaranteed. Try them free for 30 days or your money back."
}

9 Append Row in Sheet (Video)

Saves the full result to your Google Sheet. Map these columns:

Sheet ColumnValue / Expression
ad_archive_idLoop Over Items β†’ ad_archive_id
page_nameadvertiser.ad_library_page_info.page_info.page_name
page_profile_urisnapshot.page_profile_uri
ad typeHardcoded: Video
ad Datestart_date_formatted formatted as yyyy-MM-dd
Summary$json.output.summary
RewrittenAdCopy$json.output.rewrittenAdCopy

Connect the output back to Loop Over Items (port 0) to continue processing the next ad.


Part 4 — Image Ad Branch

The image branch is nearly identical to the video branch, with two key differences:

  1. Download Image — the HTTP Request uses snapshot.cards[0].original_image_url.
  2. Analyze Image (Gemini) — set resource to Image and tailor the prompt to ask for visual structure analysis (headline placement, design elements, layout) instead of scene-by-scene breakdown.

The loop, AI Agent, Structured Output Parser, OpenRouter model, and Sheets append are all configured identically to the video branch.


Part 5 — Text Ad Branch

Text ads contain no video or image, so Gemini isn’t needed here. The loop goes straight to the AI Agent, which analyzes the raw ad JSON (body copy, headline, CTA text) and produces the same summary + rewrittenAdCopy output. The agent works purely from the structured ad data — no media description step required.


The Data Structure

Every branch writes to the same Google Sheet. Create a sheet named Sheet1 with these columns in row 1:

ColumnTypeExampleDescription
ad_archive_idText1027384950617283Unique Facebook ad ID, useful for deduplication
page_nameTextNikeThe advertiser’s Facebook Page name
page_profile_uriTexthttps://facebook.com/nike/Link back to the advertiser’s page
ad typeTextVideoOne of: Video, Image, Text
ad DateDate2026-02-14When the ad started running
SummaryText (long)AI marketing analysis…Gemini + OpenRouter combined analysis
RewrittenAdCopyText (long)Rewritten copy…Swiped and repurposed ad copy

Sample rows:

ad_archive_idpage_namead typead DateSummary (excerpt)
1027384950617283NikeVideo2026-02-14Hook leads with pain point, strong risk reversal…
9182736450192837AdidasImage2026-02-10Bold visual with product closeup, seasonal urgency…
4561728394017263Under ArmourText2026-01-30Copy-heavy, targets gym-goers, performance focus…
📌

Important: Column names must match exactly — including capitalization and spaces. The workflow maps to these exact headers. If you rename any column, update the corresponding expression in the Google Sheets node for that branch.


Full System Flow

┌───────────────────────────────────────────────────────────────────────────┐
│  USER INPUT                                                                  │
│  Form → { url, count: 20, country: "US", period: "last30d" }               │
└─────────────────────────────────┴──────────────────────────────────────────┘
                                 │
                                 ▼
┌───────────────────────────────────────────────────────────────────────────┐
│  SCRAPING                                                                    │
│  Apify Actor → 20 active ad objects from Facebook Ads Library               │
│  Filter → drops pages with < 1,000 likes                                   │
└─────────────────────────────────┴──────────────────────────────────────────┘
                                 │
                                 ▼
┌───────────────────────────────────────────────────────────────────────────┐
│  ROUTING (Switch by ad type)                                                 │
│    ├── Video → Loop → Download MP4 → Gemini vision → AI Agent             │
│    ├── Image → Loop → Download JPG → Gemini vision → AI Agent             │
│    └── Text  → Loop ───────────────────→ AI Agent             │
└─────────────────────────────────┴──────────────────────────────────────────┘
                                 │
                                 ▼
┌───────────────────────────────────────────────────────────────────────────┐
│  OUTPUT                                                                      │
│  Google Sheets row per ad: metadata + AI summary + rewritten copy            │
└───────────────────────────────────────────────────────────────────────────┘
  

Testing Your Workflow

  1. Open the Form Trigger URL in a browser (find it under the node settings → Test URL).
  2. Enter a well-known brand URL such as https://www.facebook.com/nike/, set Ad Count to 3, country to US, and Ad Period to last30d.
  3. Submit the form and watch the n8n execution log in real time.
  4. After it completes, open your Google Sheet and check for new rows.
  5. Confirm all 7 columns have data, especially Summary and RewrittenAdCopy.
ProblemLikely CauseFix
Apify returns 0 adsPage URL is wrong or page has no active adsVerify the URL; try a large brand page
All ads filtered outPage has < 1,000 likesLower the Filter threshold to 100
Gemini error on videoVideo URL expired or isn’t a direct MP4Re-run the workflow; Apify CDN URLs can expire
output.summary is emptyStructured Output Parser failedCheck that OpenRouter model is connected to AI Agent
Google Sheets auth errorOAuth token expiredRe-connect your Google credential in n8n
ad Date shows wrong formatn8n date expression issueConfirm start_date_formatted is a date string, not a timestamp

Frequently Asked Questions

Do I need a Facebook API key or special access from Meta?

No. The workflow uses Apify’s Facebook Ads Library Scraper, which pulls from Meta’s publicly available Ads Library. You don’t need any developer credentials from Facebook or Meta — just an Apify account.

How much does it cost to run?

The main cost is Apify (roughly $0.05–$0.15 per run for 20 ads) and OpenRouter (fractions of a cent per AI call). Google Gemini has a generous free tier. A typical 20-ad run costs less than $0.25 total.

Can I spy on multiple competitors in one run?

The current workflow processes one Facebook Page URL per form submission. For multiple competitors, run the form once per URL — results accumulate in the same Sheet. You could also modify the Apify node to accept an array of URLs.

What if an ad has both a video and images (carousel)?

The Switch node checks for a video URL first. If snapshot.videos[0].video_hd_url exists, the ad routes to the Video branch regardless of whether images are also present. Carousel-only ads (no video) hit the Image branch via cards[0].original_image_url.

Can I change the AI model?

Yes — the OpenRouter node accepts any model on openrouter.ai. Swap in anthropic/claude-3-haiku or openai/gpt-4o-mini for different speed/quality trade-offs. The Gemini vision nodes are fixed to Gemini for multimodal analysis, but the text generation step is fully swappable.

Can I run this automatically on a schedule?

Absolutely. Replace the Form Trigger with a Schedule Trigger, hardcode the competitor URL and settings in the Apify node, and set it to run weekly. You’ll have a fully automated competitor monitoring system that updates your Sheet every seven days without any manual input.


🚀 Get the Facebook Ads Spy Agent Template

Import the workflow, add your credentials, and start pulling AI-analyzed competitor ads into your Google Sheet in under 10 minutes — no build time required.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Add a Slack or email notification when the workflow finishes — get a digest of the top 3 most interesting ads delivered straight to your inbox.
  • Add a deduplication step before the Sheets append to check ad_archive_id against existing rows, preventing duplicates when re-running the same competitor.
  • Schedule it weekly per competitor and track which ads are still running after 30+ days — longevity is the strongest signal of a high-converting creative.
  • Add a sentiment scoring node to automatically flag ads using high-urgency language or deep discounts — a sign your competitor may be pushing hard on a particular angle.
  • Store in Airtable instead of Google Sheets for richer filtering, gallery views, and team collaboration on creative insights.
n8n Facebook Ads Apify Google Gemini OpenRouter Google Sheets AI Agent marketing automation competitor research ad spy