This workflow corresponds to n8n.io template #13737 — we link there as the canonical source.
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 →
{
"id": "EdbRd7sQPXqlrRrY",
"name": "scheduled_monitor-keyword-rankings-decodo",
"tags": [],
"nodes": [
{
"id": "sched_1",
"name": "Daily Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
240,
192
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.3
},
{
"id": "set_input_1",
"name": "Set Search Input",
"type": "n8n-nodes-base.set",
"position": [
448,
192
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a1",
"name": "keyword",
"type": "string",
"value": "best running shoes"
},
{
"id": "a2",
"name": "country",
"type": "string",
"value": "id"
},
{
"id": "a3",
"name": "language",
"type": "string",
"value": "en"
},
{
"id": "a4",
"name": "device",
"type": "string",
"value": "desktop"
},
{
"id": "a5",
"name": "top_n",
"type": "number",
"value": 5
}
]
}
},
"typeVersion": 3.4
},
{
"id": "http_decodo_1",
"name": "Decodo Search",
"type": "@decodo/n8n-nodes-decodo.decodo",
"position": [
688,
192
],
"parameters": {
"geo": "={{ $json.country }}",
"query": "={{ $json.keyword }}",
"locale": "={{ $json.language }}",
"operation": "google_search"
},
"credentials": {
"decodoApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"continueOnFail": true
},
{
"id": "if_has_results_1",
"name": "Has Results",
"type": "n8n-nodes-base.if",
"position": [
960,
192
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c1",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ Array.isArray($json.results) ? $json.results.length : 0 }}",
"rightValue": 1
}
]
}
},
"typeVersion": 2.3
},
{
"id": "split_out_1",
"name": "Split Results",
"type": "n8n-nodes-base.splitOut",
"position": [
1200,
176
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "results"
},
"typeVersion": 1
},
{
"id": "set_norm_1",
"name": "Map SERP Row",
"type": "n8n-nodes-base.set",
"position": [
1920,
176
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "n1",
"name": "keyword",
"type": "string",
"value": "={{ $(\"Set Search Input\").item.json.keyword }}"
},
{
"id": "n2",
"name": "country",
"type": "string",
"value": "={{ $(\"Set Search Input\").item.json.country }}"
},
{
"id": "n3",
"name": "language",
"type": "string",
"value": "={{ $(\"Set Search Input\").item.json.language }}"
},
{
"id": "n4",
"name": "device",
"type": "string",
"value": "={{ $(\"Set Search Input\").item.json.device }}"
},
{
"id": "n5",
"name": "rank",
"type": "string",
"value": "={{ $json.organic.pos_overall || $json.organic.pos || \"\" }}"
},
{
"id": "n6",
"name": "title",
"type": "string",
"value": "={{ $json.organic.title || \"\" }}"
},
{
"id": "n7",
"name": "url",
"type": "string",
"value": "={{ $json.organic.url || \"\" }}"
},
{
"id": "n8",
"name": "description",
"type": "string",
"value": "={{ $json.organic.desc || \"\" }}"
},
{
"id": "n9",
"name": "checkedAt",
"type": "string",
"value": "={{ $now.toISO() }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "if_valid_row_1",
"name": "Valid Row",
"type": "n8n-nodes-base.if",
"position": [
2160,
176
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "u1",
"operator": {
"type": "string",
"operation": "notEmpty"
},
"leftValue": "={{ $json.url || \"\" }}",
"rightValue": ""
},
{
"id": "u2",
"operator": {
"type": "number",
"operation": "lte"
},
"leftValue": "={{ Number($json.rank || 999) }}",
"rightValue": "={{ Number($(\"Set Search Input\").item.json.top_n || 5) }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "gs_success_1",
"name": "Write SERP Results",
"type": "n8n-nodes-base.googleSheets",
"position": [
2448,
160
],
"parameters": {
"columns": {
"value": {
"URL": "={{ $json.url }}",
"Rank": "={{ $json.rank }}",
"Title": "={{ $json.title }}",
"Keyword": "={{ $json.keyword }}",
"Description": "={{ $json.description }}"
},
"schema": [
{
"id": "Keyword",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Keyword",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Title",
"type": "string",
"display": true,
"required": false,
"displayName": "Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "URL",
"type": "string",
"display": true,
"required": false,
"displayName": "URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Description",
"type": "string",
"display": true,
"required": false,
"displayName": "Description",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Rank",
"type": "string",
"display": true,
"required": false,
"displayName": "Rank",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Au8cuHixZqIgrfYd5QZcViMyhq9Ee-5qhNWcmCRGePI/edit#gid=0",
"cachedResultName": " SERP_Results"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1Au8cuHixZqIgrfYd5QZcViMyhq9Ee-5qhNWcmCRGePI",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Au8cuHixZqIgrfYd5QZcViMyhq9Ee-5qhNWcmCRGePI/edit?usp=drivesdk",
"cachedResultName": "Keyword monitoring"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "set_err_no_results_1",
"name": "Build No-Result Error",
"type": "n8n-nodes-base.set",
"position": [
2448,
320
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "e1",
"name": "checkedAt",
"type": "string",
"value": "={{ $now.toISO() }}"
},
{
"id": "e2",
"name": "keyword",
"type": "string",
"value": "={{ $(\"Set Search Input\").item.json.keyword }}"
},
{
"id": "e3",
"name": "country",
"type": "string",
"value": "={{ $(\"Set Search Input\").item.json.country }}"
},
{
"id": "e4",
"name": "language",
"type": "string",
"value": "={{ $(\"Set Search Input\").item.json.language }}"
},
{
"id": "e5",
"name": "device",
"type": "string",
"value": "={{ $(\"Set Search Input\").item.json.device }}"
},
{
"id": "e6",
"name": "errorStage",
"type": "string",
"value": "no_results"
},
{
"id": "e7",
"name": "errorMessage",
"type": "string",
"value": "={{ $json.message || ($json.error && $json.error.message) || \"Decodo returned empty results or unexpected payload.\" }}"
},
{
"id": "e8",
"name": "httpStatus",
"type": "string",
"value": "={{ $json.statusCode || $json.code || \"\" }}"
},
{
"id": "e9",
"name": "rawPreview",
"type": "string",
"value": "={{ JSON.stringify($json).slice(0, 300) }}"
},
{
"id": "e10",
"name": "workflowId",
"type": "string",
"value": "EdbRd7sQPXqlrRrY"
},
{
"id": "e11",
"name": "executionId",
"type": "string",
"value": "={{ $execution.id || \"\" }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "set_err_bad_item_1",
"name": "Build Invalid-Row Error",
"type": "n8n-nodes-base.set",
"position": [
2672,
208
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "f1",
"name": "checkedAt",
"type": "string",
"value": "={{ $json.checkedAt || $now.toISO() }}"
},
{
"id": "f2",
"name": "keyword",
"type": "string",
"value": "={{ $json.keyword || \"\" }}"
},
{
"id": "f3",
"name": "country",
"type": "string",
"value": "={{ $json.country || \"\" }}"
},
{
"id": "f4",
"name": "language",
"type": "string",
"value": "={{ $json.language || \"\" }}"
},
{
"id": "f5",
"name": "device",
"type": "string",
"value": "={{ $json.device || \"\" }}"
},
{
"id": "f6",
"name": "errorStage",
"type": "string",
"value": "invalid_item"
},
{
"id": "f7",
"name": "errorMessage",
"type": "string",
"value": "={{ \"Result missing URL. title=\" + ($json.title || \"\") }}"
},
{
"id": "f8",
"name": "httpStatus",
"type": "string",
"value": ""
},
{
"id": "f9",
"name": "rawPreview",
"type": "string",
"value": "={{ JSON.stringify($json).slice(0, 300) }}"
},
{
"id": "f10",
"name": "workflowId",
"type": "string",
"value": "EdbRd7sQPXqlrRrY"
},
{
"id": "f11",
"name": "executionId",
"type": "string",
"value": "={{ $execution.id || \"\" }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "gs_error_1",
"name": "Write SERP Errors",
"type": "n8n-nodes-base.googleSheets",
"position": [
2848,
320
],
"parameters": {
"columns": {
"value": {
"keyword": "={{ $json.keyword }}",
" checkedAt": "={{ $json.checkedAt.toDateTime()}}",
"errorStage": "={{ $json.errorStage }}",
"httpStatus": "={{ $json.httpStatus }}",
"rawPreview": "={{ $json.rawPreview }}",
"errorMessage": "={{ $json.errorMessage }}"
},
"schema": [
{
"id": " checkedAt",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": " checkedAt",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "keyword",
"type": "string",
"display": true,
"required": false,
"displayName": "keyword",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "errorStage",
"type": "string",
"display": true,
"required": false,
"displayName": "errorStage",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "errorMessage",
"type": "string",
"display": true,
"required": false,
"displayName": "errorMessage",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "httpStatus",
"type": "string",
"display": true,
"required": false,
"displayName": "httpStatus",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "rawPreview",
"type": "string",
"display": true,
"required": false,
"displayName": "rawPreview",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "SERP_Errors"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1Au8cuHixZqIgrfYd5QZcViMyhq9Ee-5qhNWcmCRGePI",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Au8cuHixZqIgrfYd5QZcViMyhq9Ee-5qhNWcmCRGePI/edit?usp=drivesdk",
"cachedResultName": "Keyword monitoring"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "extract_organic_1",
"name": "Extract Organic",
"type": "n8n-nodes-base.set",
"position": [
1440,
176
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "o1",
"name": "organic",
"type": "array",
"value": "={{ ($json.results && $json.results.content && $json.results.content.results && $json.results.content.results.results && $json.results.content.results.results.organic) || ($json.content && $json.content.results && $json.content.results.results && $json.content.results.results.organic) || [] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "split_organic_1",
"name": "Split Organic",
"type": "n8n-nodes-base.splitOut",
"position": [
1680,
176
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "organic"
},
"typeVersion": 1
},
{
"id": "a747d799-124f-4c76-a0cc-53c05ef39d91",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-368,
-288
],
"parameters": {
"width": 500,
"height": 750,
"content": "## Daily SERP monitor: capture top search results\n\n### How it works\n1. Runs on a daily schedule and loads the configured keyword, country, language, device, and top_n.\n2. Calls Decodo to perform a Google search for the keyword and returns SERP payloads.\n3. Extracts organic results, splits them into rows, and maps title, URL, description, rank, and timestamp.\n4. Validates rows (URL present and rank \u2264 top_n). Valid rows are appended to the SERP_Results Google Sheet; invalid rows or API/no-result errors are logged to SERP_Errors with a timestamp and raw preview.\n\n### Setup\n- [ ] Connect your Google Sheets account used to store results.\n- [ ] Add Decodo API credentials in n8n (Decodo Search node).\n- [ ] Edit the \"Set Search Input\" node to set keyword, country, language, device, and top_n.\n- [ ] Update the Google Sheet ID and sheet names (SERP_Results and SERP_Errors).\n- [ ] Set schedule time or run a manual test run.\n- [ ] Verify appended rows and review SERP_Errors for any failures."
},
"typeVersion": 1
},
{
"id": "24fc6079-d7de-404b-bfd6-4cf252d67266",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2320,
16
],
"parameters": {
"color": 7,
"width": 748,
"height": 444,
"content": "## Store Results & Log Errors\nKeeps only valid top-ranked rows and appends them to the results sheet. If there are no results or a row is missing key fields, it writes an error entry to the errors sheet."
},
"typeVersion": 1
},
{
"id": "86f89d19-935b-4f42-99d8-1232d668a2af",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
176,
16
],
"parameters": {
"color": 7,
"width": 700,
"height": 444,
"content": "## Schedule & Search Setup\nRuns daily and defines thesearch and scrape with decodo"
},
"typeVersion": 1
},
{
"id": "1b4a86e5-b42c-4c26-a498-a1a987093699",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
896,
16
],
"parameters": {
"color": 7,
"width": 1404,
"height": 444,
"content": "## Parse & Filter Top Results\nBreaks the response into items, extracts organic listings, maps fields, and keeps only valid rows within the top-N ranks."
},
"typeVersion": 1
}
],
"active": true,
"settings": {
"timezone": "Asia/Jakarta",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "71f2c5da-68db-4b15-bd18-0cc09a9efcef",
"connections": {
"Valid Row": {
"main": [
[
{
"node": "Write SERP Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Invalid-Row Error",
"type": "main",
"index": 0
}
]
]
},
"Has Results": {
"main": [
[
{
"node": "Split Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Build No-Result Error",
"type": "main",
"index": 0
}
]
]
},
"Map SERP Row": {
"main": [
[
{
"node": "Valid Row",
"type": "main",
"index": 0
}
]
]
},
"Daily Trigger": {
"main": [
[
{
"node": "Set Search Input",
"type": "main",
"index": 0
}
]
]
},
"Decodo Search": {
"main": [
[
{
"node": "Has Results",
"type": "main",
"index": 0
}
]
]
},
"Split Organic": {
"main": [
[
{
"node": "Map SERP Row",
"type": "main",
"index": 0
}
]
]
},
"Split Results": {
"main": [
[
{
"node": "Extract Organic",
"type": "main",
"index": 0
}
]
]
},
"Extract Organic": {
"main": [
[
{
"node": "Split Organic",
"type": "main",
"index": 0
}
]
]
},
"Set Search Input": {
"main": [
[
{
"node": "Decodo Search",
"type": "main",
"index": 0
}
]
]
},
"Build No-Result Error": {
"main": [
[
{
"node": "Write SERP Errors",
"type": "main",
"index": 0
}
]
]
},
"Build Invalid-Row Error": {
"main": [
[
{
"node": "Write SERP Errors",
"type": "main",
"index": 0
}
]
]
}
}
}
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.
decodoApigoogleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Automate your SEO competitive analysis with this daily SERP tracker. This workflow uses Decodo to scrape Google Search results for specific keywords and logs rankings directly into Google Sheets, saving SEO teams hours of manual reporting.
Source: https://n8n.io/workflows/13737/ — 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.
Automatically monitor your keyword positions on Google every day. This workflow uses Decodo to pull live search results and logs them straight into Google Sheets, no manual checking needed.
This workflow automates video distribution to 9 social platforms simultaneously using Blotato's API. It includes both a scheduled publisher (checks Google Sheets for videos marked "Ready") and a subwo
YogiAI. Uses googleSheets, googleSheetsTool, httpRequest, stopAndError. Scheduled trigger; 61 nodes.
This workflow monitors Google Calendar for events indicating that a customer will visit the company today or the next day, retrieves the required details, and sends reminder notifications to the relev
Useful if a team is working within a single instance and you want to be notified of what workflows have changed since you last visited them. Another use-case might be monitoring your managed instances