This workflow corresponds to n8n.io template #13736 — 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": "Track Google Rankings Automatically with Decodo & Google Sheets",
"tags": [],
"nodes": [
{
"id": "sched_1",
"name": "Schedule Run",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
240,
192
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.3
},
{
"id": "set_input_1",
"name": "Input Defaults",
"type": "n8n-nodes-base.set",
"position": [
448,
192
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a1",
"name": "keyword",
"type": "string",
"value": "best standing desk"
},
{
"id": "a2",
"name": "country",
"type": "string",
"value": "USA"
},
{
"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": "Fetch Google SERP",
"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": "Check 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 Payload Results",
"type": "n8n-nodes-base.splitOut",
"position": [
1200,
176
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "results"
},
"typeVersion": 1
},
{
"id": "set_norm_1",
"name": "Map Result Row",
"type": "n8n-nodes-base.set",
"position": [
1920,
176
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "n1",
"name": "keyword",
"type": "string",
"value": "={{ $(\"Input Defaults\").item.json.keyword }}"
},
{
"id": "n2",
"name": "country",
"type": "string",
"value": "={{ $(\"Input Defaults\").item.json.country }}"
},
{
"id": "n3",
"name": "language",
"type": "string",
"value": "={{ $(\"Input Defaults\").item.json.language }}"
},
{
"id": "n4",
"name": "device",
"type": "string",
"value": "={{ $(\"Input Defaults\").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": "Check Row Valid",
"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($(\"Input Defaults\").item.json.top_n || 5) }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "gs_success_1",
"name": "Save 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 Empty 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": "={{ $(\"Input Defaults\").item.json.keyword }}"
},
{
"id": "e3",
"name": "country",
"type": "string",
"value": "={{ $(\"Input Defaults\").item.json.country }}"
},
{
"id": "e4",
"name": "language",
"type": "string",
"value": "={{ $(\"Input Defaults\").item.json.language }}"
},
{
"id": "e5",
"name": "device",
"type": "string",
"value": "={{ $(\"Input Defaults\").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 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": "Save 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 List",
"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 Items",
"type": "n8n-nodes-base.splitOut",
"position": [
1680,
176
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "organic"
},
"typeVersion": 1
},
{
"id": "a747d799-124f-4c76-a0cc-53c05ef39d91",
"name": "Flow Summary",
"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": "Results Mapping Note",
"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": "Error Mapping Note",
"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": "Input Example Note",
"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": "e4a63262-905a-4245-846a-b4a502554ccc",
"connections": {
"Schedule Run": {
"main": [
[
{
"node": "Input Defaults",
"type": "main",
"index": 0
}
]
]
},
"Check Results": {
"main": [
[
{
"node": "Split Payload Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Empty Error",
"type": "main",
"index": 0
}
]
]
},
"Input Defaults": {
"main": [
[
{
"node": "Fetch Google SERP",
"type": "main",
"index": 0
}
]
]
},
"Map Result Row": {
"main": [
[
{
"node": "Check Row Valid",
"type": "main",
"index": 0
}
]
]
},
"Build Row Error": {
"main": [
[
{
"node": "Save SERP Errors",
"type": "main",
"index": 0
}
]
]
},
"Check Row Valid": {
"main": [
[
{
"node": "Save SERP Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Row Error",
"type": "main",
"index": 0
}
]
]
},
"Build Empty Error": {
"main": [
[
{
"node": "Save SERP Errors",
"type": "main",
"index": 0
}
]
]
},
"Fetch Google SERP": {
"main": [
[
{
"node": "Check Results",
"type": "main",
"index": 0
}
]
]
},
"Split Organic Items": {
"main": [
[
{
"node": "Map Result Row",
"type": "main",
"index": 0
}
]
]
},
"Extract Organic List": {
"main": [
[
{
"node": "Split Organic Items",
"type": "main",
"index": 0
}
]
]
},
"Split Payload Results": {
"main": [
[
{
"node": "Extract Organic List",
"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
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.
Source: https://n8n.io/workflows/13736/ — 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 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, savi
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