This workflow follows the Gmail → 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 →
{
"updatedAt": "2026-01-12T14:22:43.507Z",
"createdAt": "2026-01-12T10:39:58.594Z",
"id": "V6Qmnzbt0UkvF9fq42Bte",
"name": "2 SEO Performance Tracking & Auto-Optimization",
"active": false,
"isArchived": false,
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-304,
144
],
"id": "22d6a85c-7489-4bfd-acfa-a3c3dcf0d3dd",
"name": "When clicking \u2018Execute workflow\u2019"
},
{
"parameters": {
"operation": "getAll",
"options": {}
},
"type": "n8n-nodes-base.wordpress",
"typeVersion": 1,
"position": [
96,
144
],
"id": "21bc7945-9cc7-42ec-8fc4-2367489ff20e",
"name": "Get many posts",
"credentials": {
"wordpressApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "config-201",
"name": "wordpress_url",
"value": "https://iptvs-subscription.com",
"type": "string"
},
{
"id": "config-202",
"name": "tracking_days",
"value": 30,
"type": "number"
},
{
"id": "config-203",
"name": "min_performance_score",
"value": 40,
"type": "number"
},
{
"id": "config-204",
"name": "auto_optimize_enabled",
"value": true,
"type": "boolean"
},
{
"id": "config-205",
"name": "gsc_property_url",
"value": "https://iptvs-subscription.com",
"type": "string"
},
{
"id": "config-206",
"name": "ga4_property_id",
"value": "REPLACE_WITH_GA4_ID",
"type": "string"
},
{
"id": "config-207",
"name": "notification_email",
"value": "your-email@example.com",
"type": "string"
}
]
},
"options": {}
},
"id": "440869a1-c3fa-4dd0-bc1b-a9f50e54e5d8",
"name": "Set Configuration",
"type": "n8n-nodes-base.set",
"typeVersion": 3.3,
"position": [
-112,
144
]
},
{
"parameters": {
"jsCode": "// 1. Get the posts from the previous node\nconst posts = $input.all().map(item => item.json);\n\n// 2. Get the settings from the \"Set Configuration\" node\n// We use a try-catch in case you didn't run that node yet\nlet config = {};\ntry {\n config = $('Set Configuration').first().json;\n} catch (error) {\n config = {\n // Fallback defaults if config node fails\n gsc_property_url: \"https://iptvs-subscription.com\", \n ga4_property_id: \"REPLACE_WITH_YOUR_GA4_ID\" \n };\n}\n\n// 3. Transform the data for Google APIs\nreturn posts.map(post => {\n return {\n json: {\n post_id: post.id,\n post_title: post.title.rendered,\n post_url: post.link, // Maps 'link' to 'post_url' for GSC\n post_slug: post.slug,\n post_date: post.date,\n // Calculate how many days ago it was published\n days_since_publish: Math.floor((new Date() - new Date(post.date)) / (1000 * 60 * 60 * 24)),\n \n // Pass the config down to the next nodes\n gsc_property_url: config.gsc_property_url,\n ga4_property_id: config.ga4_property_id\n }\n };\n});"
},
"id": "db50cdc0-38bb-4f82-aa8f-dae8c80b93ed",
"name": "Process Posts Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
272,
144
]
},
{
"parameters": {
"jsCode": "const post = $('Process Posts Data').item.json;\n\n// 1. Fake the Google Data (since we skipped those nodes)\nconst totalImpressions = 0;\nconst totalClicks = 0;\nconst avgPosition = 0;\nconst ctr = 0;\nconst topQueries = []; // We have no keywords from Google\nconst sessions = 0;\nconst bounceRate = 0;\n\n// 2. Simple Scoring Logic: Based ONLY on Age\n// If the post is older than 30 days, we say it needs optimization.\nlet performanceScore = 100;\n\nif (post.days_since_publish > 30) {\n performanceScore = 30; // Force a low score so it triggers optimization\n} else {\n performanceScore = 90; // It's new, leave it alone\n}\n\nconst needsOptimization = performanceScore < 40;\n\nconst issues = [];\nif (needsOptimization) {\n issues.push('Article is old and needs a refresh');\n}\n\nreturn [{\n json: {\n post_id: post.post_id,\n post_url: post.post_url,\n post_title: post.post_title,\n post_slug: post.post_slug,\n days_since_publish: post.days_since_publish,\n // Pass empty data so the next nodes don't break\n search_console: {\n impressions: 0,\n clicks: 0,\n ctr: 0,\n average_position: 0,\n top_queries: [] \n },\n analytics: {\n sessions: 0,\n users: 0,\n bounce_rate: 0,\n avg_session_duration: 0\n },\n performance_score: performanceScore,\n needs_optimization: needsOptimization,\n issues: issues,\n status: needsOptimization ? 'poor' : 'excellent',\n tracked_at: new Date().toISOString(),\n wordpress_url: post.wordpress_url,\n auto_optimize_enabled: post.auto_optimize_enabled\n }\n}];"
},
"id": "1cf89670-d029-45bb-8781-03f318e7cf1b",
"name": "Calculate Performance Score1",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
512,
144
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "needs-opt",
"leftValue": "={{ $json.needs_optimization }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "49d58b5e-9ba1-49db-a0ee-dd77de57fa82",
"name": "Needs Optimization?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
752,
144
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/chat/completions",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "REDACTED_HEADER_VALUE"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "gpt-4o"
},
{
"name": "messages",
"value": "={{ [{\"role\": \"system\", \"content\": \"You are an SEO expert.\"}, {\"role\": \"user\", \"content\": \"Analyze this article title and suggest a better, more click-worthy version.\\n\\nCurrent Title: \" + $json.post_title + \"\\n\\nReturn ONLY valid JSON: { \\\"optimized_title\\\": \\\"...\\\", \\\"optimized_meta\\\": \\\"...\\\" }\"}] }}"
}
]
},
"options": {}
},
"id": "0be83ece-ef0e-490a-93e4-436f433d7d80",
"name": "Generate Optimization Plan",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1040,
16
]
},
{
"parameters": {
"jsCode": "const post = $input.first().json;\nconst aiResponse = $json;\n\nlet recommendations;\ntry {\n const content = aiResponse.choices[0].message.content.replace(/```json\\n?/g, '').replace(/```/g, '').trim();\n recommendations = JSON.parse(content);\n} catch (e) {\n recommendations = {\n optimized_title: post.post_title + \" (Updated)\",\n optimized_meta: post.post_title\n };\n}\n\nreturn [{\n json: {\n post_id: post.post_id,\n optimized_title: recommendations.optimized_title,\n optimized_meta: recommendations.optimized_meta\n }\n}];"
},
"id": "66e658f0-b3fc-46cd-b450-ec089e3bd9a4",
"name": "Parse Recommendations",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1248,
16
]
},
{
"parameters": {
"operation": "update",
"postId": "={{ $('Calculate Performance Score1').item.json.post_id }}",
"updateFields": {
"title": "={{ $json.optimized_title }}"
}
},
"id": "8167be04-87c3-4677-887e-2e6209f9eed9",
"name": "Update WordPress",
"type": "n8n-nodes-base.wordpress",
"typeVersion": 1,
"position": [
1472,
16
],
"credentials": {
"wordpressApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "sendEmail"
},
"id": "29cc7067-6c9d-464c-a8bd-cc3888ec63a0",
"name": "Send Performance Report1",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
1040,
384
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const allResults = $input.all();\n\n// FIX: Make sure this name matches your actual config node name exactly\nconst config = $('Set Configuration').first().json;\n\nlet totalArticles = allResults.length;\nlet totalImpressions = 0;\nlet totalClicks = 0;\nlet avgPosition = 0;\nlet totalSessions = 0;\n\nconst topPerformers = [];\nconst needsAttention = [];\nconst optimizedToday = [];\n\nallResults.forEach(item => {\n const data = item.json;\n \n // 1. Aggregate Search Console Data\n if (data.search_console) {\n totalImpressions += data.search_console.impressions || 0;\n totalClicks += data.search_console.clicks || 0;\n avgPosition += data.search_console.average_position || 0;\n }\n \n // 2. Aggregate Analytics Data\n if (data.analytics) {\n totalSessions += data.analytics.sessions || 0;\n }\n \n // 3. Categorize Articles by Score\n if (data.performance_score >= 80) {\n topPerformers.push({\n title: data.post_title,\n score: data.performance_score,\n url: data.post_url\n });\n } else if (data.performance_score < 40) {\n needsAttention.push({\n title: data.post_title,\n score: data.performance_score,\n issues: data.issues,\n url: data.post_url\n });\n }\n \n // 4. Track Optimizations (Only works if connected after optimization)\n if (data.action_taken === 'title_meta_updated') {\n optimizedToday.push({\n title: data.post_title,\n old_score: data.current_score,\n url: data.post_url\n });\n }\n});\n\n// Calculate Average Position across all articles\navgPosition = totalArticles > 0 ? avgPosition / totalArticles : 0;\n\n// 5. Construct Final Report Object\nconst report = {\n report_date: new Date().toISOString(),\n summary: {\n total_articles: totalArticles,\n total_impressions: totalImpressions,\n total_clicks: totalClicks,\n overall_ctr: totalImpressions > 0 ? ((totalClicks / totalImpressions) * 100).toFixed(2) : 0,\n avg_position: avgPosition.toFixed(1),\n total_sessions: totalSessions,\n top_performers_count: topPerformers.length,\n needs_attention_count: needsAttention.length,\n optimized_today_count: optimizedToday.length\n },\n top_performers: topPerformers.slice(0, 5),\n needs_attention: needsAttention.slice(0, 10),\n optimized_today: optimizedToday,\n notification_email: config.notification_email\n};\n\nreturn [{json: report}];"
},
"id": "4bcd02e8-eb74-4eed-806a-b5c1268ede8b",
"name": "Generate Daily Report",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
832,
384
]
}
],
"connections": {
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Set Configuration",
"type": "main",
"index": 0
}
]
]
},
"Get many posts": {
"main": [
[
{
"node": "Process Posts Data",
"type": "main",
"index": 0
}
]
]
},
"Set Configuration": {
"main": [
[
{
"node": "Get many posts",
"type": "main",
"index": 0
}
]
]
},
"Process Posts Data": {
"main": [
[
{
"node": "Calculate Performance Score1",
"type": "main",
"index": 0
}
]
]
},
"Needs Optimization?": {
"main": [
[
{
"node": "Generate Optimization Plan",
"type": "main",
"index": 0
}
],
[]
]
},
"Generate Optimization Plan": {
"main": [
[
{
"node": "Parse Recommendations",
"type": "main",
"index": 0
}
]
]
},
"Parse Recommendations": {
"main": [
[
{
"node": "Update WordPress",
"type": "main",
"index": 0
}
]
]
},
"Calculate Performance Score1": {
"main": [
[
{
"node": "Needs Optimization?",
"type": "main",
"index": 0
},
{
"node": "Generate Daily Report",
"type": "main",
"index": 0
}
]
]
},
"Generate Daily Report": {
"main": [
[
{
"node": "Send Performance Report1",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"availableInMCP": false
},
"staticData": null,
"meta": {
"templateCredsSetupCompleted": true
},
"versionId": "38970438-c14e-489e-a661-009ff4844558",
"activeVersionId": null,
"triggerCount": 0,
"shared": [
{
"updatedAt": "2026-01-12T10:39:58.612Z",
"createdAt": "2026-01-12T10:39:58.612Z",
"role": "workflow:owner",
"workflowId": "V6Qmnzbt0UkvF9fq42Bte",
"projectId": "HHopAZ4lOFgjhBzT"
}
],
"activeVersion": null,
"tags": []
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
gmailOAuth2wordpressApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
2 SEO Performance Tracking & Auto-Optimization. Uses wordpress, httpRequest, gmail. Event-driven trigger; 11 nodes.
Source: https://github.com/abde0112/n8n_bkv2/blob/main/2-seo-performance-tracking-&-auto-optimization-V6Qmnzbt0UkvF9fq42Bte.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.
100% autonomous workflow that transforms YouTube videos into unique, high-quality, SEO-optimized blog articles and automatically publishes them to WordPress. No human intervention required: it uses Yo
📄✨ Easy Wordpress Content Creation from PDF Document + Human In The Loop with Gmail Approval. Uses stickyNote, formTrigger, extractFromFile, lmChatOpenAi. Event-driven trigger; 27 nodes.
This n8n workflow automates the process of transforming PDF documents into engaging, SEO-friendly WordPress blog posts. It incorporates AI-powered text analysis, automatic image generation, and a huma
This template is perfect for SEO writers, niche bloggers, and content marketers who want to generate high-quality blog posts from a single keyword without spending hours on research and writing. If yo
AI-Generated Summary Block for WordPress Posts - with OpenAI, WordPress, Google Sheets & Slack. Uses httpRequest, googleSheets, slack, wordpress. Event-driven trigger; 32 nodes.