{ "id": "OO4izN00xPfIPGaB", "meta": { "instanceId": "workflow-b0b07836", "versionId": "1.0.0", "createdAt": "2025-09-29T07:07:42.053747", "updatedAt": "2025-09-29T07:07:42.053764", "owner": "n8n-user", "license": "MIT", "category": "automation", "status": "active", "priority": "high", "environment": "production" }, "name": "Ahrefs Keyword Research Workflow", "tags": [ "automation", "n8n", "production-ready", "excellent", "optimized" ], "nodes": [ { "id": "trigger-4189a467", "name": "Manual Trigger", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ 100, 100 ], "parameters": {} }, { "id": "4e420798-7523-4d47-af27-10f85d09f01d", "name": "When chat message received", "type": "n8n-nodes-base.noOp", "position": [ -300, -60 ], "webhookId": "f40acbbc-ac03-43d1-9341-6c9e8c674293", "parameters": { "options": {} }, "typeVersion": 1.1, "notes": "This chatTrigger node performs automated tasks as part of the workflow." }, { "id": "0f71c28e-a11b-4aed-a342-e15d2714ab47", "name": "Google Gemini Chat Model", "type": "n8n-nodes-base.noOp", "position": [ -160, 140 ], "parameters": { "options": {}, "modelName": "models/gemini-1.5-flash" }, "credentials": { "googlePalmApi": { "id": "zT4YaNflEp2E6S3m", "name": "Google Gemini(PaLM) Api account" } }, "typeVersion": 1, "notes": "This lmChatGoogleGemini node performs automated tasks as part of the workflow." }, { "id": "9b24fc9d-ac8d-4a9b-a7a5-00d1665f47af", "name": "Google Gemini Chat Model1", "type": "n8n-nodes-base.noOp", "position": [ 980, 160 ], "parameters": { "options": {}, "modelName": "models/gemini-1.5-flash" }, "credentials": { "googlePalmApi": { "id": "zT4YaNflEp2E6S3m", "name": "Google Gemini(PaLM) Api account" } }, "typeVersion": 1, "notes": "This lmChatGoogleGemini node performs automated tasks as part of the workflow." }, { "id": "d0cbe978-040d-4663-895e-85844e203773", "name": "Keyword Data Response Formatter", "type": "n8n-nodes-base.noOp", "position": [ 980, -60 ], "parameters": { "text": "Provide reponse according to the system message. ", "options": { "systemMessage": "=system_message:\n description: |\n Your role is to format and output the keyword data into a clean, readable text format. The input data consists of two main parts: **Main Keyword Data** and **Related Keywords Data**. Your task is to process and output this data in a way that is easy to read for the user. Each keyword and its associated details should be displayed clearly.\n\n Data:\n - **Main Keyword Data✨**:\n - **Keyword**: \"{{ $json.data[0].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[0].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[0].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[0].competition_value }}\"\n - **High CPC**: \"{{ $json.data[0].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[0].low_cpc }}\"\n\n - **Related Keywords🧰**:\n \n \n - **1. Keyword**: \"{{ $json.data[1].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[1].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[1].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[1].competition_value }}\"\n - **High CPC**: \"{{ $json.data[1].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[1].low_cpc }}\"\n \n - **2. Keyword**: \"{{ $json.data[2].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[2].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[2].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[2].competition_value }}\"\n - **High CPC**: \"{{ $json.data[2].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[2].low_cpc }}\"\n \n - **3. Keyword**: \"{{ $json.data[3].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[3].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[3].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[3].competition_value }}\"\n - **High CPC**: \"{{ $json.data[3].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[3].low_cpc }}\"\n \n - **4. Keyword**: \"{{ $json.data[4].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[4].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[4].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[4].competition_value }}\"\n - **High CPC**: \"{{ $json.data[4].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[4].low_cpc }}\"\n \n - **5. Keyword**: \"{{ $json.data[5].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[5].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[5].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[5].competition_value }}\"\n - **High CPC**: \"{{ $json.data[5].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[5].low_cpc }}\"\n \n - **6. Keyword**: \"{{ $json.data[6].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[6].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[6].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[6].competition_value }}\"\n - **High CPC**: \"{{ $json.data[6].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[6].low_cpc }}\"\n \n - **7. Keyword**: \"{{ $json.data[7].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[7].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[7].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[7].competition_value }}\"\n - **High CPC**: \"{{ $json.data[7].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[7].low_cpc }}\"\n \n - **8. Keyword**: \"{{ $json.data[8].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[8].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[8].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[8].competition_value }}\"\n - **High CPC**: \"{{ $json.data[8].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[8].low_cpc }}\"\n \n - **9. Keyword**: \"{{ $json.data[9].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[9].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[9].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[9].competition_value }}\"\n - **High CPC**: \"{{ $json.data[9].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[9].low_cpc }}\"\n\n - **10. Keyword**: \"{{ $json.data[10].keyword }}\"\n - **Average Monthly Searches**: \"{{ $json.data[10].avg_monthly_searches }}\"\n - **Competition Index**: \"{{ $json.data[10].competition_index }}\"\n - **Competition Value**: \"{{ $json.data[10].competition_value }}\"\n - **High CPC**: \"{{ $json.data[10].high_cpc }}\"\n - **Low CPC**: \"{{ $json.data[10].low_cpc }}\"\n" }, "promptType": "define" }, "typeVersion": 1.8, "notes": "This agent node performs automated tasks as part of the workflow." }, { "id": "9cb26cde-dbff-4118-a141-ebd1fd7df1b1", "name": "Keyword Query Extraction & Cleaning Agent", "type": "n8n-nodes-base.noOp", "position": [ -80, -60 ], "parameters": { "options": { "systemMessage": "You are a helpful assistant. You job is to check the user message and pick out the SEO keyword they have provided and output it. Make sure you output just one SEO keyword. No commentary. Do not rephrase, just correct grammar if it has been misspelt." } }, "typeVersion": 1.8, "notes": "This agent node performs automated tasks as part of the workflow." }, { "id": "6a59bf1f-68a3-433c-9cf7-47cadc1a77eb", "name": "Extract Main Keyword & 10 related Keyword data", "type": "n8n-nodes-base.code", "position": [ 540, -60 ], "parameters": { "jsCode": "// Get the main keyword data (Global Keyword Data)\nconst mainKeywordData = $input.first().json['Global Keyword Data']?.[0] || {};\n\n// Get the related keywords array\nconst relatedKeywords = $input.first().json['Related Keyword Data (Global)'] || [];\n\n// Create an output array that includes the main keyword data first\nconst output = [\n {\n keyword: mainKeywordData.keyword || 'N/A',\n avg_monthly_searches: mainKeywordData.avg_monthly_searches || 'N/A',\n competition_index: mainKeywordData.competition_index || 'N/A',\n competition_value: mainKeywordData.competition_value || 'N/A',\n high_cpc: mainKeywordData['High CPC'] || 'N/A',\n low_cpc: mainKeywordData['Low CPC'] || 'N/A'\n },\n // Map up to 10 related keywords with selected fields\n ...relatedKeywords.slice(0, 10).map(item => ({\n keyword: item.keyword,\n avg_monthly_searches: item.avg_monthly_searches,\n competition_index: item.competition_index,\n competition_value: item.competition_value,\n high_cpc: item['High CPC'],\n low_cpc: item['Low CPC']\n }))\n];\n\nreturn output;\n" }, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow." }, { "id": "a2b1b9ff-a425-4c99-bd36-a4bb0e0cd84e", "name": "Aggregate Keyword Data", "type": "n8n-nodes-base.aggregate", "position": [ 800, -60 ], "parameters": { "options": {}, "aggregate": "aggregateAllItemData" }, "typeVersion": 1, "notes": "This aggregate node performs automated tasks as part of the workflow." }, { "id": "36d4c962-71f2-473a-841c-053c6c36bcda", "name": "Ahrefs Keyword API Request", "type": "n8n-nodes-base.httpRequest", "maxTries": 2, "position": [ 280, -60 ], "parameters": { "url": "{{ $env.API_BASE_URL }}", "options": {}, "sendQuery": true, "sendHeaders": true, "queryParameters": { "parameters": [ { "name": "keyword", "value": "={{ $json.output }}" } ] }, "headerParameters": { "parameters": [ { "name": "x-rapidapi-host", "value": "ahrefs-keyword-tool.p.rapidapi.com" }, { "name": "x-rapidapi-key", "value": "\"your_rapid_api_key_here\"" } ] } }, "retryOnFail": true, "typeVersion": 4.2, "notes": "This httpRequest node performs automated tasks as part of the workflow." }, { "id": "47898c8e-37e7-4abc-beb2-64fc546a7c03", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [ -80, -260 ], "parameters": { "color": 6, "width": 260, "content": "## Keyword Query Extraction\nThis ai agent is important so that you always make sure for all queries you send, only the keyword phrase will be passed over to the API request node, and if you misspell any word, it will be corrected." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "c83f2813-d57c-48d6-8c66-6a057ca9cfc9", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [ 280, -260 ], "parameters": { "color": 4, "content": "## The API Request\nYou can tweak this to either get \"answer the public kwywords\" or \"keyword overviews\", just visit the api [docs page]({{ $env.API_BASE_URL }}" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "98ad64ea-d023-49c0-ab05-21bd87c322b9", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [ 600, -260 ], "parameters": { "content": "## Extract Keyword Data\nThe data from the API query will be so so big and I have written this javascript function to extract the most important bits. You can modify it if you want to also get monthly data, or just download the response as pdf and probably pass it for analysis." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "1f1d15f3-36f7-4bad-be63-ce74c70580f1", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [ -420, -260 ], "parameters": { "width": 260, "content": "## Trigger Node\nThis is just a sample trigger node to get started. You can use a telegram, whatsapp, webhook node etc, to get the keyword queried. " }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "a5e0b305-ebc7-44e2-ada2-8d5cf60a1fe2", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [ 980, -260 ], "parameters": { "content": "## Respose Formatter\nThe ai agent node to format responses will give you more room to decide how you want your summaries to be sent back to you. You can modify the system message to get your desired outcome. Otherwise, good luck building on top of this. I will give a detailed docs guide on the main n8n workflow page" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "00ce5fc5-aff8-4cde-871e-ffea5aa5ffb3", "name": "Simple Memory", "type": "n8n-nodes-base.noOp", "position": [ 40, 140 ], "parameters": {}, "typeVersion": 1.3, "notes": "This memoryBufferWindow node performs automated tasks as part of the workflow." } ], "active": false, "pinData": {}, "settings": { "executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner", "errorWorkflow": null, "timezone": "UTC", "executionTimeout": 3600, "maxExecutions": 1000, "retryOnFail": true, "retryCount": 3, "retryDelay": 1000 }, "versionId": "e2857a0c-4473-4d3d-9c63-6b02337bccf0", "connections": { "36d4c962-71f2-473a-841c-053c6c36bcda": { "main": [ [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda", "type": "main", "index": 0 } ], [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda-abbfc80b", "type": "main", "index": 0 } ], [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda-1e3bd5a3", "type": "main", "index": 0 } ], [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda-592cc93f", "type": "main", "index": 0 } ], [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda-3cb0e758", "type": "main", "index": 0 } ], [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda-7f2ef722", "type": "main", "index": 0 } ], [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda-c6146364", "type": "main", "index": 0 } ], [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda-edf29fb2", "type": "main", "index": 0 } ], [ { "node": "error-handler-36d4c962-71f2-473a-841c-053c6c36bcda-56bf6178", "type": "main", "index": 0 } ] ] }, "0f71c28e-a11b-4aed-a342-e15d2714ab47": { "main": [ [ { "node": "error-handler-0f71c28e-a11b-4aed-a342-e15d2714ab47-293942bc", "type": "main", "index": 0 } ] ] }, "9b24fc9d-ac8d-4a9b-a7a5-00d1665f47af": { "main": [ [ { "node": "error-handler-9b24fc9d-ac8d-4a9b-a7a5-00d1665f47af-abad501c", "type": "main", "index": 0 } ] ] } }, "description": "Automated workflow: Ahrefs Keyword Research Workflow. This workflow integrates 9 different services: stickyNote, httpRequest, code, lmChatGoogleGemini, agent. It contains 18 nodes and follows best practices for error handling and security.", "notes": "Excellent quality workflow: Ahrefs Keyword Research Workflow. This workflow has been optimized for production use with comprehensive error handling, security, and documentation." }