AutomationFlowsData & Sheets › Track Url-level Keyword Rankings in Google Sheets with Dataforseo

Track Url-level Keyword Rankings in Google Sheets with Dataforseo

ByDataForSEO @dataforseo on n8n.io

This workflow automatically monitors how specific URLs rank for their target keywords in Google search results and stores your full ranking history in Google Sheets.

Cron / scheduled trigger★★★★☆ complexity19 nodesGoogle SheetsN8N Nodes Dataforseo
Data & Sheets Trigger: Cron / scheduled Nodes: 19 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #15184 — 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 →

Download .json
{
  "id": "ja1ZFW7wTlDady1i",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Track keyword position dynamics by URL in Google Sheets with DataForSEO",
  "tags": [],
  "nodes": [
    {
      "id": "f7140313-2cd1-4c50-8145-54ff993174d4",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        -16
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9,
              "weeksInterval": 2
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "35b93b54-9482-46b7-b5d4-61dd27789ffb",
      "name": "Create sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1088,
        -16
      ],
      "parameters": {
        "title": "={{ $json.url }}",
        "options": {},
        "operation": "create",
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Ms5Mtrf7RH13rhSqSWPWJyUTU0477Vj4qbF7qRloQ98",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Ms5Mtrf7RH13rhSqSWPWJyUTU0477Vj4qbF7qRloQ98/edit?usp=drivesdk",
          "cachedResultName": "Positions tracking - Output"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "25f68bcd-afa7-4fb1-b887-599cd7b3fa13",
      "name": "Get live google organic SERP regular",
      "type": "n8n-nodes-dataforseo.dataForSeoSerpApi",
      "position": [
        2224,
        -16
      ],
      "parameters": {
        "depth": 20,
        "target": "={{ $('Normalize URL').item.json.url }}",
        "keyword": "={{ $('Loop over items (each keyword)').item.json.Keyword }}",
        "language_name": "english",
        "location_name": "united states"
      },
      "credentials": {
        "dataForSeoApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "4ea70828-9a12-49b2-b656-6b8d93e21396",
      "name": "Append or update row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3008,
        -16
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "Keyword"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Normalize URL').item.json.url }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Ms5Mtrf7RH13rhSqSWPWJyUTU0477Vj4qbF7qRloQ98",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Ms5Mtrf7RH13rhSqSWPWJyUTU0477Vj4qbF7qRloQ98/edit?usp=drivesdk",
          "cachedResultName": "Positions tracking - Output"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "5334a925-5b20-402a-a181-1f80e007e1f9",
      "name": "Append row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1760,
        -160
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "Keyword",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Keyword",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "Keyword"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Normalize URL').item.json.url }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Ms5Mtrf7RH13rhSqSWPWJyUTU0477Vj4qbF7qRloQ98",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Ms5Mtrf7RH13rhSqSWPWJyUTU0477Vj4qbF7qRloQ98/edit?usp=drivesdk",
          "cachedResultName": "Positions tracking - Output"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "f2d5e1e0-e533-4072-a331-88d9bf345e0f",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        1984,
        -16
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "889e65ab-c1bb-4ebe-b2e0-483d5d694824",
      "name": "Normalize URL",
      "type": "n8n-nodes-base.code",
      "position": [
        864,
        -16
      ],
      "parameters": {
        "jsCode": "let url = $input.first().json.URL;\nlet clean = url.split('#')[0];\nclean = clean.split('?')[0];\nif (clean.length > 1 && clean.endsWith('/')) {\n  clean = clean.slice(0, -1);\n}\n\nreturn {\n  url: clean.trim()\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "719451fb-56f3-481f-89df-102ad637e5cd",
      "name": "Calculate delta & status",
      "type": "n8n-nodes-base.code",
      "position": [
        2656,
        -16
      ],
      "parameters": {
        "jsCode": "let delta = null;\nconst prevPosition = $input.first().json['position_' + $now.plus({days: -14}).toFormat('yyyy-MM-dd')] || null;\nconst currentPosition = $('Get live google organic SERP regular').first()?.json?.tasks?.[0]?.result?.[0]?.items?.[0]?.rank_absolute || null;\nif (\n  prevPosition \n  && prevPosition !== 'Not Found'\n  && prevPosition !== 'Error'\n  && currentPosition\n) {\n  delta = prevPosition - currentPosition;\n}\n\nlet status = 'error';\n\nif ($('Get live google organic SERP regular').first().json.tasks?.[0]?.result?.[0]?.items == null) {\n  status = 'not_found';\n} else {\n  status = 'found';\n}\n\nreturn {\n  delta: delta,\n  status: status\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "671dfbea-8dcf-4225-a3c1-d35127a1e798",
      "name": "Prepare data for GS",
      "type": "n8n-nodes-base.set",
      "position": [
        2832,
        -16
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={\n\"Keyword\": {{ $('Loop over items (each keyword)').item.json.Keyword.trim() }}\n \"position_{{ $now.format('yyyy-MM-dd') }}\": {{ $('Get live google organic SERP regular').item.json.tasks[0].result[0].items[0].rank_absolute }},\n\"delta_{{ $now.format('yyyy-MM-dd') }}\": {{ $json.delta }},\n\"matched_url_{{ $now.format('yyyy-MM-dd') }}\": {{ $('Get live google organic SERP regular').item.json.tasks[0].result[0].items[0].url }},\n\"status_{{ $now.format('yyyy-MM-dd') }}\": {{ $json.status }}\n}\n "
      },
      "typeVersion": 3.4
    },
    {
      "id": "c4d897ba-1801-4f1e-80d2-65f5a87d75c2",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        -272
      ],
      "parameters": {
        "color": 6,
        "width": 1104,
        "height": 416,
        "content": "## Create a new sheet if it doesn't exist\nCreate or select a Google Sheets connection. Select output spreadsheet."
      },
      "typeVersion": 1
    },
    {
      "id": "9019d097-8f6b-4f3b-9243-a711675dfe96",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2160,
        -272
      ],
      "parameters": {
        "color": 6,
        "width": 976,
        "height": 416,
        "content": "## Get SERP data with DataForSEO and save it to Google Sheets\nCreate or select a Google Sheets connection, and select the output spreadsheet.\nCreate or select a DataForSEO connection."
      },
      "typeVersion": 1
    },
    {
      "id": "3609c7a5-906a-4945-8b5d-a67bbd50e1df",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        -272
      ],
      "parameters": {
        "color": 6,
        "width": 432,
        "height": 416,
        "content": "## Get active keywords from the input in Google Sheets\nCreate or select a Google Sheets connection. Select the input table with keywords. The sheet must have the same columns as in [this Example](https://docs.google.com/spreadsheets/d/1WPeLL-5futt5PzcUvbQRX3k2RXCEpliddJWuNlfdLR4/edit?usp=sharing)."
      },
      "typeVersion": 1
    },
    {
      "id": "a1111093-728a-4a25-b71a-6f000ec69ac5",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -544,
        -384
      ],
      "parameters": {
        "width": 480,
        "height": 624,
        "content": "This workflow automatically retrieves URLs and their target keywords from Google Sheets, checks their current rankings on Google search via DataForSEO, and writes the results into a historical Google Sheets report where each tab represents a separate URL and each run adds new date-based columns with rankings and changes.\n\n## How it works\n1. Triggers automatically every two weeks.\n2. Fetches keywords and URLs from your input in Google Sheets (if set, fetches only active records).\n3. Checks the top-20 Google search results for your keywords and URLs using the DataForSEO SERP API.\n4. Creates a dedicated sheet for each URL, saves the current position in Google Sheets, calculates the ranking delta, and logs status. \n5. Adds new columns for each date of the run, creating a historical data record in Google Sheets.\n\n## Setup steps\n\n1. Create or select your DataForSEO connection (use your [API login and password](https://app.dataforseo.com/api-access)).\n2. Create or select a Google Sheets connection for the input and output spreadsheets.\n3. Prepare the input sheet with keywords and required columns (URL, Keyword, Active).\n4. Set the preferred workflow schedule."
      },
      "typeVersion": 1
    },
    {
      "id": "e303f2c2-a6f7-4ff3-a990-d349dea3cb41",
      "name": "Get keywords and URLs",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        224,
        -16
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JLgnLCFD1qhdbr1-rbeTgi1Sv3sIynMvmRRssJBQJqw/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JLgnLCFD1qhdbr1-rbeTgi1Sv3sIynMvmRRssJBQJqw",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JLgnLCFD1qhdbr1-rbeTgi1Sv3sIynMvmRRssJBQJqw/edit?usp=drivesdk",
          "cachedResultName": "Positions tracking  - Input"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "dc168507-20eb-4712-a778-66bd841e168d",
      "name": "Filter (only active)",
      "type": "n8n-nodes-base.filter",
      "position": [
        432,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "31a61fed-0f56-42f5-94bd-0216e3a8b592",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.Status }}",
              "rightValue": "Active"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "2d710aa6-088a-435c-b83f-94c96306f331",
      "name": "Loop over items (each keyword)",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        656,
        -16
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "c9db71a9-96d3-481e-b907-cc3380eb9d77",
      "name": "Is a new sheet created?",
      "type": "n8n-nodes-base.if",
      "position": [
        1296,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "fd006d00-db1b-4407-a2ef-6185a3626aa9",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.spreadsheetId }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "05e7082f-a801-429e-97fe-2a8db2b8619e",
      "name": "Prepare column data for GS",
      "type": "n8n-nodes-base.set",
      "position": [
        1536,
        -160
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={\n  \"Keyword\": \"\"\n}\n "
      },
      "typeVersion": 3.4
    },
    {
      "id": "ab41c942-ec14-4a3d-a093-d35743b31663",
      "name": "Find the row with the keyword",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2448,
        -16
      ],
      "parameters": {
        "options": {
          "returnFirstMatch": true
        },
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Loop over items (each keyword)').item.json.Keyword }}",
              "lookupColumn": "=Keyword"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Normalize URL').item.json.url }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Ms5Mtrf7RH13rhSqSWPWJyUTU0477Vj4qbF7qRloQ98",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Ms5Mtrf7RH13rhSqSWPWJyUTU0477Vj4qbF7qRloQ98/edit?usp=drivesdk",
          "cachedResultName": "Positions tracking - Output"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "af44906b-98a4-4b62-b175-7dcd75d987b1",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Get live google organic SERP regular",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create sheet": {
      "main": [
        [
          {
            "node": "Is a new sheet created?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize URL": {
      "main": [
        [
          {
            "node": "Create sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get keywords and URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append row in sheet": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare data for GS": {
      "main": [
        [
          {
            "node": "Append or update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter (only active)": {
      "main": [
        [
          {
            "node": "Loop over items (each keyword)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get keywords and URLs": {
      "main": [
        [
          {
            "node": "Filter (only active)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is a new sheet created?": {
      "main": [
        [
          {
            "node": "Prepare column data for GS",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Calculate delta & status": {
      "main": [
        [
          {
            "node": "Prepare data for GS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare column data for GS": {
      "main": [
        [
          {
            "node": "Append row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append or update row in sheet": {
      "main": [
        [
          {
            "node": "Loop over items (each keyword)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find the row with the keyword": {
      "main": [
        [
          {
            "node": "Calculate delta & status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop over items (each keyword)": {
      "main": [
        [],
        [
          {
            "node": "Normalize URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get live google organic SERP regular": {
      "main": [
        [
          {
            "node": "Find the row with the keyword",
            "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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automatically monitors how specific URLs rank for their target keywords in Google search results and stores your full ranking history in Google Sheets.

Source: https://n8n.io/workflows/15184/ — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

With this n8n automation, you no longer have to manually dig through backlink reports to find broken links. The workflow automatically retrieves them with the DataForSEO Backlinks API and generates a

Google Sheets, N8N Nodes Dataforseo, Asana
Data & Sheets

This workflow automatically fetches up to 100 ranked keywords for specific URLs based on Google search data and stores your full ranking history in Google Sheets.

Google Sheets, N8N Nodes Dataforseo
Data & Sheets

Overview 🌐

Google Sheets, N8N Nodes Dataforseo
Data & Sheets

SEO analysts and marketers who want to capture and analyze source references from Google’s AI Mode answers, track competitor mentions or measure brand visibility in generative search features.

N8N Nodes Dataforseo, Google Sheets
Data & Sheets

SEOs and digital marketers who want to track the sources Google cites in its AI Overview SERP feature for specific keywords, monitor competitor visibility, or their website’s presence in AI-generated

N8N Nodes Dataforseo, Google Sheets