AutomationFlowsAI & RAG › Creatorlens - Wf03 Trend Agent

Creatorlens - Wf03 Trend Agent

CreatorLens - WF03 Trend Agent. Uses httpRequest, chainLlm, lmChatGoogleGemini. Webhook trigger; 17 nodes.

Webhook trigger★★★★☆ complexityAI-powered17 nodesHTTP RequestChain LlmGoogle Gemini Chat
AI & RAG Trigger: Webhook Nodes: 17 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Chainllm → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "q1p9YZ4v7KVtbzFE",
  "name": "CreatorLens - WF03 Trend Agent",
  "description": "Feature 2 - Keyword Trend Analyzer. Searches YouTube for niche videos from the last 14 days (max 3 searches), computes view velocity, Gemini extracts trending keywords with opportunity scores into keyword_trends.",
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": true,
    "binaryMode": "separate"
  },
  "connections": {
    "Trend Webhook": {
      "main": [
        [
          {
            "node": "Normalize Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Input": {
      "main": [
        [
          {
            "node": "Mark Run Running",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark Run Running": {
      "main": [
        [
          {
            "node": "Build Search Queries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Search Queries": {
      "main": [
        [
          {
            "node": "YT Niche Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "YT Niche Search": {
      "main": [
        [
          {
            "node": "Dedupe Video IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedupe Video IDs": {
      "main": [
        [
          {
            "node": "Found Videos?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Found Videos?": {
      "main": [
        [
          {
            "node": "YT Trend Video Stats",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Save Empty Trends",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "YT Trend Video Stats": {
      "main": [
        [
          {
            "node": "Compute Velocity",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark Run Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute Velocity": {
      "main": [
        [
          {
            "node": "Gemini Trend Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Trend Analysis": {
      "main": [
        [
          {
            "node": "Parse Gemini Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark Run Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Flash Lite": {
      "ai_languageModel": [
        [
          {
            "node": "Gemini Trend Analysis",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Parse Gemini Response": {
      "main": [
        [
          {
            "node": "Save Keyword Trends",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark Run Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Keyword Trends": {
      "main": [
        [
          {
            "node": "Mark Run Succeeded",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark Run Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Empty Trends": {
      "main": [
        [
          {
            "node": "Mark Run Succeeded",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "trend-agent",
        "authentication": "headerAuth",
        "options": {}
      },
      "id": "4a13aacf-7794-414b-aaed-f54388053d99",
      "name": "Trend Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        304
      ]
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "={{ { \"runId\": $json.body?.runId ?? $json.runId, \"channelId\": $json.body?.channelId ?? $json.channelId, \"niche\": $json.body?.niche ?? $json.niche } }}",
        "options": {}
      },
      "id": "c38dd2b4-66f4-4a3f-8ede-fb561206f335",
      "name": "Normalize Input",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        224,
        304
      ]
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "=https://ukaxegkcrgsxeizskrot.supabase.co/rest/v1/agent_runs?id=eq.{{ $json.runId }}",
        "authentication": "genericCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Prefer",
              "value": "return=representation"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"status\": \"running\" } }}",
        "options": {},
        "genericAuthType": "httpCustomAuth"
      },
      "id": "8f3d6dd8-510b-46fd-b1da-5a97506cb9b0",
      "name": "Mark Run Running",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        448,
        304
      ]
    },
    {
      "parameters": {
        "jsCode": "const niche = ($('Normalize Input').first().json.niche || '').trim();\nif (!niche) throw new Error('No niche provided');\nconst year = new Date().getFullYear();\nconst queries = [niche, `${niche} ${year}`, `best ${niche}`].slice(0, 3);\nconst publishedAfter = new Date(Date.now() - 14 * 86400000).toISOString();\nreturn queries.map(q => ({ json: { query: q, publishedAfter } }));"
      },
      "id": "c2b3d18c-9860-497e-b309-9cef909c77db",
      "name": "Build Search Queries",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        304
      ]
    },
    {
      "parameters": {
        "url": "=https://www.googleapis.com/youtube/v3/search?part=snippet&q={{ encodeURIComponent($json.query) }}&type=video&order=viewCount&publishedAfter={{ $json.publishedAfter }}&maxResults=25",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "options": {}
      },
      "id": "0d47c6a0-508b-40ac-9dea-cd1bcc896097",
      "name": "YT Niche Search",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        880,
        304
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const ids = new Set();\nfor (const it of $input.all()) {\n  for (const v of (it.json.items || [])) {\n    if (v.id && v.id.videoId) ids.add(v.id.videoId);\n  }\n}\nreturn [{ json: { idCsv: Array.from(ids).slice(0, 50).join(',') } }];"
      },
      "id": "f61fa03f-2295-4ed9-8081-189b28e160bd",
      "name": "Dedupe Video IDs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1104,
        304
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "leftValue": "={{ $json.idCsv }}",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "rightValue": ""
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "672dc228-855b-41dd-8ef3-692b9a21546c",
      "name": "Found Videos?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1328,
        304
      ]
    },
    {
      "parameters": {
        "url": "=https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics&id={{ $json.idCsv }}&maxResults=50",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "options": {}
      },
      "id": "587be416-59cd-4f3c-a271-d17d6c64723c",
      "name": "YT Trend Video Stats",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1552,
        208
      ],
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.first().json.items || [];\nconst now = Date.now();\nconst videos = items.map(v => {\n  const views = Number((v.statistics && v.statistics.viewCount) || 0);\n  const days = Math.max(1, (now - new Date(v.snippet.publishedAt).getTime()) / 86400000);\n  return {\n    title: v.snippet.title,\n    channel: v.snippet.channelTitle,\n    published_at: v.snippet.publishedAt,\n    views,\n    views_per_day: Math.round(views / days),\n    likes: Number((v.statistics && v.statistics.likeCount) || 0),\n    comments: Number((v.statistics && v.statistics.commentCount) || 0),\n    tags: (v.snippet.tags || []).slice(0, 8)\n  };\n}).sort((a, b) => b.views_per_day - a.views_per_day).slice(0, 40);\nconst niche = $('Normalize Input').first().json.niche;\nconst prompt = `You are a YouTube trend analyst. Niche: \"${niche}\".\\nBelow are recent (last 14 days) high-velocity videos in this niche, sorted by views per day.\\n\\nIdentify trending keywords/topics a creator in this niche should make videos about NOW.\\n\\nReturn ONLY valid JSON (no markdown fences):\\n{\"keywords\":[{\"keyword\":\"...\",\"momentum\":\"exploding|rising|steady\",\"avg_views\":<number>,\"competition\":\"low|medium|high\",\"opportunity_score\":<0-100>,\"sample_titles\":[\"up to 3 real titles from the data\"]}],\"insights\":\"3-4 sentences on overall niche dynamics\"}\\n\\nReturn 6-10 keywords ordered by opportunity_score descending.\\n\\nVideos:\\n${JSON.stringify(videos)}`;\nreturn [{ json: { prompt, video_count: videos.length } }];"
      },
      "id": "9651df41-7057-4706-be06-9ea7f5c1d432",
      "name": "Compute Velocity",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1760,
        208
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.prompt }}",
        "messages": {
          "messageValues": [
            {
              "message": "You are a YouTube niche trend analyst. You always respond with raw JSON only - no markdown fences, no extra text."
            }
          ]
        },
        "batching": {}
      },
      "id": "d6ee4627-2939-472c-889a-59a81e226006",
      "name": "Gemini Trend Analysis",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        1904,
        80
      ],
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "modelName": "models/gemini-3.1-flash-lite",
        "options": {
          "maxOutputTokens": 4096,
          "temperature": 0.4
        }
      },
      "id": "1807161d-8431-4a3e-a70b-cf97f7005547",
      "name": "Gemini Flash Lite",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1.1,
      "position": [
        1952,
        432
      ]
    },
    {
      "parameters": {
        "jsCode": "const raw = $input.first().json;\nlet text = raw.text ?? raw.output ?? raw.response ?? '';\nif (typeof text === 'object') text = JSON.stringify(text);\ntext = String(text).trim().replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```$/, '');\nlet parsed;\ntry { parsed = JSON.parse(text); } catch (e) { throw new Error('Gemini returned unparseable JSON: ' + text.slice(0, 300)); }\nif (parsed.output && !parsed.keywords) parsed = parsed.output;\nif (!Array.isArray(parsed.keywords)) throw new Error('Model output missing keywords array');\nreturn [{ json: { keywords: parsed.keywords, insights: parsed.insights || '' } }];"
      },
      "id": "8b6be335-bfcd-4d99-b6a6-20ddc4e1c6bf",
      "name": "Parse Gemini Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2208,
        208
      ],
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://ukaxegkcrgsxeizskrot.supabase.co/rest/v1/keyword_trends",
        "authentication": "genericCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Prefer",
              "value": "return=representation"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"channel_id\": $(\"Normalize Input\").item.json.channelId, \"niche\": $(\"Normalize Input\").item.json.niche, \"result\": { \"keywords\": $json.keywords, \"insights\": $json.insights, \"video_sample_size\": $(\"Compute Velocity\").item.json.video_count } } }}",
        "options": {},
        "genericAuthType": "httpCustomAuth"
      },
      "id": "0777097e-e828-4e3b-b8cf-81e187cb2741",
      "name": "Save Keyword Trends",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        2432,
        208
      ],
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "=https://ukaxegkcrgsxeizskrot.supabase.co/rest/v1/agent_runs?id=eq.{{ $(\"Normalize Input\").item.json.runId }}",
        "authentication": "genericCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"status\": \"succeeded\", \"finished_at\": $now.toISO() } }}",
        "options": {},
        "genericAuthType": "httpCustomAuth"
      },
      "id": "eb6bad49-1049-4b99-9076-dd7ae76ee95c",
      "name": "Mark Run Succeeded",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        2640,
        304
      ]
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "=https://ukaxegkcrgsxeizskrot.supabase.co/rest/v1/agent_runs?id=eq.{{ $(\"Normalize Input\").item.json.runId }}",
        "authentication": "genericCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"status\": \"failed\", \"finished_at\": $now.toISO(), \"error\": ($json.error?.message ?? $json.error ?? \"Trend agent failed\").toString().slice(0, 500) } }}",
        "options": {},
        "genericAuthType": "httpCustomAuth"
      },
      "id": "02c948bc-1ed4-455a-9e6d-a6a4bb4b7cdb",
      "name": "Mark Run Failed",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1408,
        640
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://ukaxegkcrgsxeizskrot.supabase.co/rest/v1/keyword_trends",
        "authentication": "genericCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Prefer",
              "value": "return=representation"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"channel_id\": $(\"Normalize Input\").item.json.channelId, \"niche\": $(\"Normalize Input\").item.json.niche, \"result\": { \"keywords\": [], \"insights\": \"No recent high-velocity videos were found for this niche in the last 14 days. Try a broader niche phrase.\", \"video_sample_size\": 0 } } }}",
        "options": {},
        "genericAuthType": "httpCustomAuth"
      },
      "id": "a467874c-04c0-4a61-9186-9cba453482ca",
      "name": "Save Empty Trends",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1552,
        448
      ]
    },
    {
      "parameters": {
        "content": "## WF03 Trend Agent\nsearch.list costs 100 quota units per call - capped at 3 queries per run (~300 units). Web app enforces max 1 run per niche per 6h.\n\n### Credentials to attach\n1. Webhook: **CreatorLens Webhook Secret**\n2. Supabase HTTP nodes: **Supabase CreatorLens**\n3. YT nodes: **YouTube API Key** (Query Auth, param `key`)\n4. Gemini Flash Lite: **Gemini**",
        "height": 344,
        "width": 520,
        "color": 4
      },
      "id": "d032eaae-01ca-4d43-ab70-06f47e11f8db",
      "name": "Sticky Note 59ff20d9",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -64,
        -256
      ]
    }
  ],
  "meta": {
    "aiBuilderAssisted": true,
    "builderVariant": "mcp"
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

CreatorLens - WF03 Trend Agent. Uses httpRequest, chainLlm, lmChatGoogleGemini. Webhook trigger; 17 nodes.

Source: https://github.com/Ahmi-786/AI-Driven-Content-Analytics/blob/97d966d365a4e5628206f47133567d08341291e3/n8n-workflows/wf03-trend-agent.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

ANIS_HUB 1. Uses gmail, googleDrive, googleSheets, httpRequest. Webhook trigger; 89 nodes.

Gmail, Google Drive, Google Sheets +3
AI & RAG

CLINICAINTEGRAL_secretary. Uses postgres, mcpClientTool, googleDriveTool, toolWorkflow. Webhook trigger; 89 nodes.

Postgres, Mcp Client Tool, Google Drive Tool +14
AI & RAG

my-secretary. Uses postgres, mcpClientTool, googleDriveTool, toolWorkflow. Webhook trigger; 86 nodes.

Postgres, Mcp Client Tool, Google Drive Tool +13
AI & RAG

secretaria. Uses postgres, n8n-nodes-evolution-api, openAi, httpRequest. Webhook trigger; 71 nodes.

Postgres, N8N Nodes Evolution Api, OpenAI +12
AI & RAG

Resume Screening & Behavioral Interviews with Gemini, Elevenlabs, & Notion ATS copy. Uses outputParserStructured, chainLlm, googleDrive, stickyNote. Webhook trigger; 67 nodes.

Output Parser Structured, Chain Llm, Google Drive +9