AutomationFlowsAI & RAG › Watch Web Pages for Changes with Google Gemini, Google Sheets, and Slack

Watch Web Pages for Changes with Google Gemini, Google Sheets, and Slack

ByOka Hironobu @okp29 on n8n.io

This workflow checks a specified web page every 6 hours, stores snapshots in Google Sheets, and when the page text changes it uses Google Gemini to summarise what changed and posts the summary to a Slack channel. Runs every 6 hours on a schedule. Fetches the configured URL via…

Cron / scheduled trigger★★★★☆ complexityAI-powered17 nodesHTTP RequestGoogle SheetsChain LlmGoogle Gemini ChatSlack
AI & RAG Trigger: Cron / scheduled Nodes: 17 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Chainllm → Google Sheets 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": "44Vh3I9HIJgB6Ghz",
  "meta": {
    "builderVariant": "mcp",
    "aiBuilderAssisted": true
  },
  "name": "Watch any web page for changes and get an AI summary of what changed with Gemini",
  "tags": [],
  "nodes": [
    {
      "id": "395aa903-d197-4ae9-b98c-f1808a405dc8",
      "name": "Schedule Every 6 Hours",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        48,
        48
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "390145e7-de1f-467c-a076-46eac05e79b4",
      "name": "Set Page to Watch",
      "type": "n8n-nodes-base.set",
      "position": [
        272,
        48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "label",
              "name": "label",
              "type": "string",
              "value": "Competitor pricing"
            },
            {
              "id": "url",
              "name": "url",
              "type": "string",
              "value": "https://example.com/pricing"
            },
            {
              "id": "sel",
              "name": "selector",
              "type": "string",
              "value": "main"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "9f56cec2-3d7f-4e12-81ff-1a71d9479ee8",
      "name": "Fetch Page",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        480,
        48
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "abf2270d-6883-4ed9-b9cd-49f0f1dcb6b5",
      "name": "Clean Page Text",
      "type": "n8n-nodes-base.code",
      "position": [
        944,
        32
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let html = String($json.data || $json.body || '');\nconst sel = $('Set Page to Watch').item.json.selector;\nlet text = html.replace(/<script[\\s\\S]*?<\\/script>/gi, ' ').replace(/<style[\\s\\S]*?<\\/style>/gi, ' ').replace(/<[^>]+>/g, ' ').replace(/&nbsp;/g, ' ').replace(/\\s+/g, ' ').trim();\ntext = text.slice(0, 6000);\nreturn { label: $('Set Page to Watch').item.json.label, url: $('Set Page to Watch').item.json.url, currentText: text };"
      },
      "typeVersion": 2
    },
    {
      "id": "e5b9ccc5-87e5-4e74-8eda-e442b04b76c9",
      "name": "Get Last Snapshot",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1168,
        32
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Snapshots"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "8ca8a77d-4f5f-43b2-a67f-a85f1a454b9b",
      "name": "Detect Change",
      "type": "n8n-nodes-base.code",
      "position": [
        1360,
        32
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const cur = $('Clean Page Text').item.json;\nconst prev = ($json && $json.Text != null) ? String($json.Text) : '';\nconst current = String(cur.currentText || '');\nconst isFirstRun = prev.length === 0;\nconst changed = !isFirstRun && prev !== current;\nreturn { label: cur.label, url: cur.url, previousText: prev, currentText: current, changed: changed, isFirstRun: isFirstRun };"
      },
      "typeVersion": 2
    },
    {
      "id": "61e0a4e8-7427-42c9-a941-3c5fc7f08f39",
      "name": "Changed?",
      "type": "n8n-nodes-base.if",
      "position": [
        1552,
        32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.changed }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "1e66c817-7ce1-436f-a6d2-ce296572c7bf",
      "name": "Summarise Change with Gemini",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1808,
        16
      ],
      "parameters": {
        "text": "=You are a monitoring assistant. Below are the previous and current text of a web page we watch called \"{{ $json.label }}\". In two or three sentences, describe what actually changed in plain language (new prices, new features, removed items, wording changes). If the differences are trivial or cosmetic, say \"No meaningful change.\" Do not list the whole page.\n\nPREVIOUS:\n{{ $json.previousText }}\n\nCURRENT:\n{{ $json.currentText }}",
        "batching": {},
        "promptType": "define"
      },
      "typeVersion": 1.9
    },
    {
      "id": "260b8e60-37aa-4bb9-b28a-0cd80af69576",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1744,
        176
      ],
      "parameters": {
        "options": {
          "temperature": 0.2
        },
        "modelName": "models/gemini-3.1-flash-lite"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "a398b3ad-71ce-42d6-866e-92dc8a2a7fb8",
      "name": "Notify Change",
      "type": "n8n-nodes-base.slack",
      "position": [
        2096,
        16
      ],
      "parameters": {
        "text": "=*:eyes: Change detected: {{ $(\"Detect Change\").item.json.label }}*\n{{ $json.text }}\n{{ $(\"Detect Change\").item.json.url }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "#monitoring"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "1af7fb81-0327-4637-921f-a0eee21d8cf0",
      "name": "Update Snapshot (Changed)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2272,
        16
      ],
      "parameters": {
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Snapshots"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "09b1f4fe-9a26-419c-8283-c38ff534f368",
      "name": "Update Snapshot (Baseline)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1808,
        336
      ],
      "parameters": {
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Snapshots"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c8a66d61-e446-4732-9652-51f02774f78b",
      "name": "Report Fetch Failure",
      "type": "n8n-nodes-base.slack",
      "position": [
        720,
        144
      ],
      "parameters": {
        "text": "=*:x: Could not fetch {{ $(\"Set Page to Watch\").item.json.label }}*\nThe page did not respond. It will be retried on the next run.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "#monitoring"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "690f8f9e-cdef-43d8-af8b-c2d9cf41415c",
      "name": "Overview Sticky",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -592,
        -144
      ],
      "parameters": {
        "width": 568,
        "height": 660,
        "content": "## Watch any web page for changes and get an AI summary of what changed\n\n### How it works\nInstead of re-reading a competitor page or a policy page yourself, this template watches it for you. On a schedule it fetches the page, strips it down to clean text, and compares it against the last snapshot stored in Google Sheets. On the very first run it just saves a baseline. When the text later changes, a Basic LLM Chain with Google Gemini reads the old and new versions and explains in two or three sentences what actually changed - a new price, a new feature, removed wording - rather than dumping the whole page, and calls out trivial edits as no meaningful change. Real changes are posted to Slack and the snapshot is updated so each change is reported once. Fetch failures are reported and retried next run.\n\n### Setup\n1. Connect Google Gemini (PaLM) API, Google Sheets and Slack.\n2. In Set Page to Watch, enter the label and URL to monitor.\n3. Point the Snapshots sheet at your spreadsheet and pick a Slack channel.\n\n### Customization tips\nWatch several pages by turning the config into sheet rows and looping."
      },
      "typeVersion": 1
    },
    {
      "id": "756eb461-ee41-47c2-a526-065a47955418",
      "name": "S1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 860,
        "height": 664,
        "content": "## 1. Fetch & clean\nRun on a schedule, fetch the page and strip it to clean text. Fetch failures go to Slack."
      },
      "typeVersion": 1
    },
    {
      "id": "26179022-e2ef-4f0a-88e9-8a5e69db21e7",
      "name": "S2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 620,
        "height": 664,
        "content": "## 2. Compare to last snapshot\nRead the previous snapshot from Sheets and diff it. First run saves a baseline silently."
      },
      "typeVersion": 1
    },
    {
      "id": "0ae55fcc-25c5-4804-b8d7-55e269847ce5",
      "name": "S3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1520,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 948,
        "height": 676,
        "content": "## 3. Summarise & notify (Basic LLM Chain)\nOn a real change, Gemini explains what changed, Slack is notified, and the snapshot is updated so each change is reported once."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "d42d94aa-06f0-45ef-b46e-0378ff1b3226",
  "nodeGroups": [],
  "connections": {
    "Changed?": {
      "main": [
        [
          {
            "node": "Summarise Change with Gemini",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Snapshot (Baseline)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Page": {
      "main": [
        [
          {
            "node": "Clean Page Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Report Fetch Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Detect Change": {
      "main": [
        [
          {
            "node": "Changed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Change": {
      "main": [
        [
          {
            "node": "Update Snapshot (Changed)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean Page Text": {
      "main": [
        [
          {
            "node": "Get Last Snapshot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Last Snapshot": {
      "main": [
        [
          {
            "node": "Detect Change",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Page to Watch": {
      "main": [
        [
          {
            "node": "Fetch Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Every 6 Hours": {
      "main": [
        [
          {
            "node": "Set Page to Watch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Summarise Change with Gemini",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Summarise Change with Gemini": {
      "main": [
        [
          {
            "node": "Notify Change",
            "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 checks a specified web page every 6 hours, stores snapshots in Google Sheets, and when the page text changes it uses Google Gemini to summarise what changed and posts the summary to a Slack channel. Runs every 6 hours on a schedule. Fetches the configured URL via…

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

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

This workflow is the AI analysis and alerting engine for a complete social media monitoring system. It's designed to work with data scraped from X (formerly Twitter) using a tool like the Apify Tweet

Google Sheets, Google Gemini Chat, Google Sheets Tool +4
AI & RAG

Categories Content Creation AI Automation Publishing Social Media

Google Docs, HTTP Request, Slack +7
AI & RAG

Automatically identifies overdue sales leads and generates personalized follow-up emails using AI. Runs every weekday Reads leads from Google Sheets Filters leads with no contact for 5+ days Downloads

Google Sheets, Chain Llm, Google Gemini Chat +3
AI & RAG

SMB finance teams, SaaS companies, and accounting professionals who need to automate transaction reconciliation between Stripe payments and their accounting ledgers. Perfect for businesses processing

HTTP Request, Google Sheets, Chain Llm +3
AI & RAG

DevOps engineers, site reliability teams, and business owners who need to know the moment their website goes down — and want AI-powered diagnostics instead of just a ping alert.

HTTP Request, Chain Llm, Google Gemini Chat +3