This workflow follows the Error Trigger → 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 →
{
"name": "Grain Google Search Console Hub v1",
"nodes": [
{
"parameters": {
"values": {
"string": [
{
"name": "tenant_id",
"value": "={{ $vars.GRAIN_TENANT_ID || 'default' }}"
},
{
"name": "dry_run",
"value": "={{ $vars.GRAIN_DRY_RUN || false }}"
},
{
"name": "webhook_prefix",
"value": "={{ $vars.GRAIN_WEBHOOK_PREFIX || '/webhook' }}/{{ $workflow.name.toLowerCase().replace(/[^a-z0-9]/g, '-') }}"
}
]
},
"options": {}
},
"id": "grain-config-abzm7pdtr",
"name": "GRAIN_CONFIG",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
-200,
-200
],
"notesInFlow": true,
"notes": "\ud83d\udd27 Central Config"
},
{
"parameters": {
"content": "## \ud83d\udcca Google Search Console Hub\nFree SEO data directly from GSC: impressions, clicks, CTR, position tracking.",
"height": 100,
"width": 350,
"color": 4
},
"id": "note-overview",
"name": "Note: Overview",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
180,
200
]
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * 1"
}
]
}
},
"id": "weekly-trigger",
"name": "Weekly Report",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
250,
400
]
},
{
"parameters": {
"method": "POST",
"url": "=https://searchconsole.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($env.GSC_SITE_URL) }}/searchAnalytics/query",
"authentication": "oAuth2",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"startDate\": \"{{ $now.minus({days: 28}).toFormat('yyyy-MM-dd') }}\",\n \"endDate\": \"{{ $now.minus({days: 1}).toFormat('yyyy-MM-dd') }}\",\n \"dimensions\": [\"query\"],\n \"rowLimit\": 100,\n \"dataState\": \"final\"\n}"
},
"id": "gsc-queries",
"name": "Get Top Queries",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
500,
400
]
},
{
"parameters": {
"method": "POST",
"url": "=https://searchconsole.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($env.GSC_SITE_URL) }}/searchAnalytics/query",
"authentication": "oAuth2",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"startDate\": \"{{ $now.minus({days: 28}).toFormat('yyyy-MM-dd') }}\",\n \"endDate\": \"{{ $now.minus({days: 1}).toFormat('yyyy-MM-dd') }}\",\n \"dimensions\": [\"page\"],\n \"rowLimit\": 50,\n \"dataState\": \"final\"\n}"
},
"id": "gsc-pages",
"name": "Get Top Pages",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
500,
600
]
},
{
"parameters": {
"jsCode": "const queries = $('Get Top Queries').first().json.rows || [];\nconst pages = $('Get Top Pages').first().json.rows || [];\n\nconst topQueries = queries.slice(0, 20).map(q => ({\n keyword: q.keys[0],\n clicks: q.clicks,\n impressions: q.impressions,\n ctr: (q.ctr * 100).toFixed(2) + '%',\n position: q.position.toFixed(1)\n}));\n\nconst lowHangingFruit = queries.filter(q => q.position >= 5 && q.position <= 20 && q.impressions > 100)\n .slice(0, 10)\n .map(q => ({ keyword: q.keys[0], position: q.position.toFixed(1), impressions: q.impressions }));\n\nconst topPages = pages.slice(0, 10).map(p => ({\n url: p.keys[0],\n clicks: p.clicks,\n impressions: p.impressions,\n ctr: (p.ctr * 100).toFixed(2) + '%'\n}));\n\nconst totalClicks = queries.reduce((a, b) => a + b.clicks, 0);\nconst totalImpressions = queries.reduce((a, b) => a + b.impressions, 0);\n\nreturn {\n json: {\n summary: { total_clicks: totalClicks, total_impressions: totalImpressions, avg_ctr: ((totalClicks / totalImpressions) * 100).toFixed(2) + '%' },\n top_queries: topQueries,\n low_hanging_fruit: lowHangingFruit,\n top_pages: topPages\n }\n};"
},
"id": "analyze-gsc",
"name": "Analyze GSC Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
750,
500
]
},
{
"parameters": {
"model": "gpt-4o",
"messages": {
"values": [
{
"role": "system",
"content": "You are an SEO analyst. Based on GSC data, provide:\n\n1. Top 3 quick wins (keywords to optimize)\n2. Content gaps to address\n3. Pages that need CTR improvement\n4. Weekly action items\n\nBe specific and actionable."
},
{
"role": "user",
"content": "GSC Summary:\n{{ JSON.stringify($json.summary) }}\n\nLow Hanging Fruit (Position 5-20):\n{{ JSON.stringify($json.low_hanging_fruit) }}\n\nTop Pages:\n{{ JSON.stringify($json.top_pages) }}"
}
]
}
},
"id": "ai-recommendations",
"name": "AI Recommendations",
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1,
"position": [
1000,
500
]
},
{
"parameters": {
"channel": "#seo-weekly",
"text": "=\ud83d\udcca **Weekly GSC Report**\n\n\ud83d\udcc8 Summary:\n\u2022 Clicks: {{ $('Analyze GSC Data').first().json.summary.total_clicks }}\n\u2022 Impressions: {{ $('Analyze GSC Data').first().json.summary.total_impressions }}\n\u2022 Avg CTR: {{ $('Analyze GSC Data').first().json.summary.avg_ctr }}\n\n\ud83c\udfaf Low Hanging Fruit (Pos 5-20):\n{{ $('Analyze GSC Data').first().json.low_hanging_fruit.slice(0, 5).map(k => '\u2022 ' + k.keyword + ' (Pos: ' + k.position + ')').join('\\n') }}\n\n\ud83d\udca1 AI Recommendations:\n{{ $json.message.content }}",
"otherOptions": {}
},
"id": "slack-report",
"name": "Send Weekly Report",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.1,
"position": [
1250,
500
]
},
{
"parameters": {},
"id": "error-trigger",
"name": "Error Trigger",
"type": "n8n-nodes-base.errorTrigger",
"typeVersion": 1,
"position": [
500,
750
]
},
{
"parameters": {
"channel": "#seo-alerts",
"text": "=\ud83d\udea8 GSC Hub Error\n\nError: {{ $json.execution.error.message }}",
"otherOptions": {}
},
"id": "error-notify",
"name": "Error Alert",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.1,
"position": [
750,
750
]
}
],
"connections": {
"Weekly Report": {
"main": [
[
{
"node": "Get Top Queries",
"type": "main",
"index": 0
},
{
"node": "Get Top Pages",
"type": "main",
"index": 0
}
]
]
},
"Get Top Queries": {
"main": [
[
{
"node": "Analyze GSC Data",
"type": "main",
"index": 0
}
]
]
},
"Get Top Pages": {
"main": [
[
{
"node": "Analyze GSC Data",
"type": "main",
"index": 0
}
]
]
},
"Analyze GSC Data": {
"main": [
[
{
"node": "AI Recommendations",
"type": "main",
"index": 0
}
]
]
},
"AI Recommendations": {
"main": [
[
{
"node": "Send Weekly Report",
"type": "main",
"index": 0
}
]
]
},
"Error Trigger": {
"main": [
[
{
"node": "Error Alert",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Grain Google Search Console Hub v1. Uses httpRequest, openAi, slack, errorTrigger. Scheduled trigger; 10 nodes.
Source: https://github.com/No3214/saas/blob/efb737b073e5ff52f402061f133591fe5209bcd6/templates/seo-marketing/Grain_GSC_Hub_v1.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Automate your social media content pipeline from idea to scheduled post. This workflow reads content ideas from a Google Sheet, uses OpenAI to generate platform-optimized posts for LinkedIn, X (Twitte
A scheduled process aggregates content from eight distinct data sources and standardizes all inputs into a unified format. AI models perform sentiment scoring, detect conspiracy or misinformation sign
Imagine a dedicated financial expert tirelessly working behind the scenes, sifting through every transaction, every investment move, and every accounting entry. That's exactly what this automated syst
Daily trigger scans your Notion database for unpublished blog ideas AI generates complete blog posts + engaging LinkedIn content using OpenAI (Blog Posting is not implemented yet) Creates custom image
This workflow is an AI-powered lighting and look development pipeline designed for VFX production. It transforms a single lighting brief into multiple high-quality cinematic lighting references using