AutomationFlowsWeb Scraping › Track Google Keyword Rankings with Apify, Openai, Google Sheets and Gmail

Track Google Keyword Rankings with Apify, Openai, Google Sheets and Gmail

ByDr. Firas @drfiras on n8n.io

This workflow runs daily to scrape Google SERPs for a list of keywords using Apify, logs ranking results to Google Sheets, generates an HTML SEO summary with OpenAI, and emails the report via Gmail. Runs every day at 8 AM on a schedule. Reads the configured keywords, target…

Cron / scheduled trigger★★★★☆ complexityAI-powered16 nodesHTTP RequestGoogle SheetsChain LlmOpenAI ChatGmail
Web Scraping Trigger: Cron / scheduled Nodes: 16 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #17313 — we link there as the canonical source.

This workflow follows the Chainllm → Gmail 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 →

Download .json
{
  "id": "ITcZfdCK8V0QaprP",
  "meta": {
    "builderVariant": "mcp",
    "aiBuilderAssisted": true
  },
  "name": "Track keyword rankings on Google with Apify and send AI SEO reports by email",
  "tags": [
    {
      "id": "BDQCNxQKN4A7X8jf",
      "name": "apify",
      "createdAt": "2026-07-14T19:34:02.338Z",
      "updatedAt": "2026-07-14T19:34:02.338Z"
    },
    {
      "id": "MwvO3sW35Gd7mwdB",
      "name": "seo",
      "createdAt": "2026-07-14T19:34:02.329Z",
      "updatedAt": "2026-07-14T19:34:02.329Z"
    },
    {
      "id": "wPS89f8pvHV6tTxu",
      "name": "google-sheets",
      "createdAt": "2026-07-14T19:34:02.347Z",
      "updatedAt": "2026-07-14T19:34:02.347Z"
    },
    {
      "id": "yh5MHSGdSao9Ntar",
      "name": "openai",
      "createdAt": "2026-07-14T19:34:02.344Z",
      "updatedAt": "2026-07-14T19:34:02.344Z"
    }
  ],
  "nodes": [
    {
      "id": "a63b5f11-a918-46a6-8d88-7861082d2563",
      "name": "Run daily at 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        112
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "c22a8345-a8dd-40c1-82f6-29fbdc1d2a85",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        112
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-keywords",
              "name": "keywords",
              "type": "string",
              "value": "best running shoes, trail running shoes"
            },
            {
              "id": "cfg-target-domain",
              "name": "target_domain",
              "type": "string",
              "value": "yourwebsite.com"
            },
            {
              "id": "cfg-country",
              "name": "country_code",
              "type": "string",
              "value": "us"
            },
            {
              "id": "cfg-language",
              "name": "language_code",
              "type": "string",
              "value": "en"
            },
            {
              "id": "cfg-results",
              "name": "results_per_keyword",
              "type": "number",
              "value": 10
            },
            {
              "id": "cfg-model",
              "name": "openai_model",
              "type": "string",
              "value": "gpt-4o"
            },
            {
              "id": "cfg-email",
              "name": "report_email",
              "type": "string",
              "value": "you@example.com"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "381dc9c8-f876-400e-9873-0ca0c39764e6",
      "name": "Prepare keyword list",
      "type": "n8n-nodes-base.set",
      "position": [
        448,
        112
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "kw-list",
              "name": "keyword_list",
              "type": "array",
              "value": "={{ $json.keywords.split(\",\").map(k => k.trim()).filter(k => k.length > 0) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "7103c4e4-b05a-4a92-a5f2-3c28eb4e5e56",
      "name": "Split keywords into items",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        672,
        112
      ],
      "parameters": {
        "options": {
          "destinationFieldName": "keyword"
        },
        "fieldToSplitOut": "keyword_list"
      },
      "typeVersion": 1
    },
    {
      "id": "1110594b-eb25-485b-87c1-f44c532b8462",
      "name": "Scrape Google results with Apify",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        896,
        112
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/apify~google-search-scraper/run-sync-get-dataset-items",
        "method": "POST",
        "options": {
          "timeout": 180000,
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 2000
            }
          }
        },
        "jsonBody": "={{ JSON.stringify({ queries: $json.keyword, countryCode: $(\"Configuration\").first().json.country_code, languageCode: $(\"Configuration\").first().json.language_code, resultsPerPage: $(\"Configuration\").first().json.results_per_keyword, maxPagesPerQuery: 1, mobileResults: false }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "0bc738cc-1f50-4088-b8a3-5e1f94711c7d",
      "name": "Extract and rank SERP results",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        160
      ],
      "parameters": {
        "jsCode": "const config = $('Configuration').first().json;\nconst targetDomain = String(config.target_domain || '').toLowerCase().replace(/^www\\./, '');\nconst checkedAt = new Date().toISOString().slice(0, 10);\nconst rows = [];\n\nfor (const item of $input.all()) {\n  const page = item.json;\n  const keyword = (page.searchQuery && page.searchQuery.term) ? page.searchQuery.term : '';\n  const results = Array.isArray(page.organicResults) ? page.organicResults : [];\n\n  for (let i = 0; i < results.length; i++) {\n    const r = results[i];\n    let domain = '';\n    try {\n      domain = new URL(r.url).hostname.toLowerCase().replace(/^www\\./, '');\n    } catch (e) {\n      domain = '';\n    }\n\n    rows.push({\n      json: {\n        checked_at: checkedAt,\n        keyword: keyword,\n        rank: r.position || (i + 1),\n        title: r.title || '',\n        url: r.url || '',\n        domain: domain,\n        description: r.description || '',\n        is_target_domain: targetDomain !== '' && domain.endsWith(targetDomain)\n      }\n    });\n  }\n}\n\nreturn rows;"
      },
      "typeVersion": 2
    },
    {
      "id": "f68463e0-c817-4a5b-8d0a-9e72b7c7550c",
      "name": "Log rankings to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1376,
        160
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Keyword rankings"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "49f86837-5865-4f52-bf1d-4d515eebd5a2",
      "name": "Combine rankings for AI analysis",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1616,
        112
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "rankings"
      },
      "typeVersion": 1
    },
    {
      "id": "e3549218-e59d-4eed-a763-0c982b453637",
      "name": "Generate AI SEO report",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1792,
        112
      ],
      "parameters": {
        "text": "=Here is the Google SERP ranking data collected today for my tracked keywords.\n\nTarget domain to monitor: {{ $(\"Configuration\").first().json.target_domain }}\nKeywords tracked: {{ $(\"Configuration\").first().json.keywords }}\nDate: {{ $now.toFormat(\"yyyy-MM-dd\") }}\n\nRanking data (JSON):\n{{ JSON.stringify($json.rankings) }}\n\nWrite the SEO report now.",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "You are a senior SEO analyst. You receive raw Google SERP ranking data (keyword, rank, title, url, domain, is_target_domain) and produce a concise, actionable SEO report.\n\nYour report must contain:\n1. A short executive summary (2-3 sentences).\n2. For each keyword: the current position of the target domain (or state clearly that it is not ranking in the tracked results), and the top 3 competitors with their positions.\n3. A \"Key opportunities\" section with 3-5 concrete, prioritized SEO actions based on the data.\n\nFormat the entire report as clean, simple HTML (use only h2, h3, p, ul, li, strong tags - no markdown, no CSS, no html/head/body wrapper). Keep it under 500 words. Be factual: never invent rankings that are not in the data."
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.9
    },
    {
      "id": "fbdf1ba6-139d-4498-b253-fb304cfbc550",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1872,
        336
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Configuration').first().json.openai_model }}"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "54fa19aa-ba63-4f7a-a16f-e91e8a2c7c77",
      "name": "Email the SEO report",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2144,
        112
      ],
      "parameters": {
        "sendTo": "={{ $('Configuration').first().json.report_email }}",
        "message": "={{ $json.text }}",
        "options": {
          "senderName": "SEO Rank Tracker",
          "appendAttribution": false
        },
        "subject": "=SEO ranking report - {{ $now.toFormat(\"yyyy-MM-dd\") }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "3d922f1d-c766-4d2f-9ef6-30644ed04950",
      "name": "Sticky Note 4613280d",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -800,
        -512
      ],
      "parameters": {
        "color": 4,
        "width": 640,
        "height": 2040,
        "content": "# Track keyword rankings on Google with Apify and send AI SEO reports by email\n# \ud83d\udce5  [Open full documentation on Notion](https://automatisation.notion.site/Course-Track-keyword-rankings-on-Google-with-Apify-and-send-AI-SEO-reports-by-email-39d3d6550fd981078ad0fc8553e63a21)\n\n## How it works\nThis workflow tracks where websites rank on Google for your target keywords, every day, without any manual checking.\n\n1. A schedule trigger runs the workflow daily at 8 AM.\n2. The **Configuration** node holds every variable you may want to change (keywords, target domain, country, language, AI model, report email).\n3. Each keyword is sent to the **Apify Google Search Scraper**, which returns the top organic results for your country and language.\n4. A Code node flattens the results into clean rows: keyword, rank, title, URL, domain, description, and whether the result belongs to your target domain.\n5. Every row is appended to a **Google Sheets** ranking log, building a day-by-day SEO history.\n6. All rankings are combined and sent to **OpenAI**, which writes a short SEO report: where your domain ranks, who the top competitors are, and 3-5 prioritized opportunities.\n7. The report lands in your inbox via **Gmail** as a clean HTML email.\n\n## Setup\n1. Open the **Configuration** node and set your keywords (comma-separated), target domain, country code, language code, results per keyword, OpenAI model and report email. This is the only node you need to edit.\n2. Select your **Apify** credential on the \"Scrape Google results with Apify\" node (header auth with your Apify API token).\n3. Select your **Google Sheets** credential and pick the spreadsheet + tab on the \"Log rankings to Google Sheets\" node. Create a sheet with the columns: checked_at, keyword, rank, title, url, domain, description, is_target_domain.\n4. Select your **OpenAI** credential on the \"OpenAI Chat Model\" node.\n5. Select your **Gmail** credential on the \"Email the SEO report\" node.\n6. Run the workflow once manually to verify, then activate it.\n\n## Requirements\n- An [Apify](https://apify.com) account and API token (the Google Search Scraper actor is used via the REST API)\n- An OpenAI API key\n- A Google account with Sheets and Gmail access\n\n## Customization\n- Change the schedule (hourly, weekly) in the trigger node.\n- Track more keywords: just extend the comma-separated list in Configuration.\n- Increase `results_per_keyword` (up to 100) to monitor deeper SERP positions.\n- Localize your tracking with `country_code` and `language_code` (e.g. fr / fr for France).\n- Edit the system prompt in \"Generate AI SEO report\" to change the tone or structure of the report.\n- Replace Gmail with Slack or Telegram to receive the report elsewhere.\n\nNeed help customizing?\nContact me for consulting and support : [Linkedin](https://www.linkedin.com/in/doctor-firass/)\n\n# MY NEW YOUTUBE CHANNEL\n\ud83d\udc49 [Subscribe to my new YouTube channel](https://www.youtube.com/@DrFiras_AI). Here I'll share videos and Shorts with practical tutorials and FREE templates for n8n.\n\n[![The AI Doctor](https://www.dr-firas.com/the-ai-doctor.png)](https://www.youtube.com/@DrFiras_AI)"
      },
      "typeVersion": 1
    },
    {
      "id": "6ce91983-82d8-462c-bd1c-dc8e6c36cd49",
      "name": "Sticky Note 03b9eeb7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 460,
        "height": 460,
        "content": "## 1. Schedule & configuration\nThe workflow runs daily. Set ALL your variables in the **Configuration** node: keywords, target domain, country, language, results per keyword, OpenAI model and report email."
      },
      "typeVersion": 1
    },
    {
      "id": "fd8c31f7-fa21-4cbc-9908-6499b16abbc9",
      "name": "Sticky Note 5bbeb576",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 680,
        "height": 460,
        "content": "## 2. Scrape Google results with Apify\nEach keyword becomes one item and is sent to the Apify Google Search Scraper (REST API, synchronous run). Requests are throttled to 1 per 2 seconds."
      },
      "typeVersion": 1
    },
    {
      "id": "f7971bd0-9a68-4617-9d14-8348c9e24c76",
      "name": "Sticky Note 4333a547",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 420,
        "height": 460,
        "content": "## 3. Parse & log rankings\nA Code node flattens the SERP results into clean rows (keyword, rank, title, URL, domain, target-domain flag) and appends them to your Google Sheets ranking history."
      },
      "typeVersion": 1
    },
    {
      "id": "acf5a00e-678a-4694-bbe4-f55d91d1ed91",
      "name": "Sticky Note 70a5333e",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 760,
        "height": 620,
        "content": "## 4. AI SEO report by email\nAll rankings are combined into one payload. OpenAI writes a short HTML report (your positions, top competitors, prioritized opportunities) and Gmail delivers it to your inbox."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "a1ec7ea2-6f90-4a25-bb47-964c1e9d8e55",
  "nodeGroups": [],
  "connections": {
    "Configuration": {
      "main": [
        [
          {
            "node": "Prepare keyword list",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate AI SEO report",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Run daily at 8 AM": {
      "main": [
        [
          {
            "node": "Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare keyword list": {
      "main": [
        [
          {
            "node": "Split keywords into items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate AI SEO report": {
      "main": [
        [
          {
            "node": "Email the SEO report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split keywords into items": {
      "main": [
        [
          {
            "node": "Scrape Google results with Apify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract and rank SERP results": {
      "main": [
        [
          {
            "node": "Log rankings to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log rankings to Google Sheets": {
      "main": [
        [
          {
            "node": "Combine rankings for AI analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine rankings for AI analysis": {
      "main": [
        [
          {
            "node": "Generate AI SEO report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Google results with Apify": {
      "main": [
        [
          {
            "node": "Extract and rank SERP results",
            "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 runs daily to scrape Google SERPs for a list of keywords using Apify, logs ranking results to Google Sheets, generates an HTML SEO summary with OpenAI, and emails the report via Gmail. Runs every day at 8 AM on a schedule. Reads the configured keywords, target…

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

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

This workflow runs daily to scrape competitor Shopify app reviews via Apify, logs and deduplicates new low-rated reviews in Google Sheets, looks up the reviewer’s website with Serper, enriches contact

OpenAI Chat, Google Sheets, Hunter +5
Web Scraping

This n8n workflow automates the process of collecting job and decision-maker data, crafting AI-generated referral messages, and drafting them in Gmail—all using a combination of Apify, Google Sheets,

Google Sheets, Chain Llm, Google Gemini Chat +3
Web Scraping

Automate Sales Meeting Prep With Ai & Apify Sent To Whatsapp. Uses gmail, googleCalendar, lmChatOpenAi, informationExtractor. Event-driven trigger; 61 nodes.

Gmail, Google Calendar, OpenAI Chat +5
Web Scraping

This n8n template builds a meeting assistant that compiles timely reminders of upcoming meetings filled with email history and recent LinkedIn activity of other people on the invite. This is then disc

Gmail, Google Calendar, OpenAI Chat +5
Web Scraping

Automate_Sales_Meeting_Prep_with_AI___APIFY_Sent_To_WhatsApp. Uses gmail, googleCalendar, lmChatOpenAi, informationExtractor. Event-driven trigger; 61 nodes.

Gmail, Google Calendar, OpenAI Chat +5