This workflow corresponds to n8n.io template #15483 — we link there as the canonical source.
This workflow follows the Airtable → Slack 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 →
{
"id": "YxAoRB4mMYOwQeXl",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "AI Website Change Monitor powered by Bright Data",
"tags": [],
"nodes": [
{
"id": "0dcad4a0-fef6-431a-9ba4-13373161862f",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-816,
256
],
"parameters": {
"color": 7,
"width": 540,
"height": 768,
"content": "# \ud83d\udc40 AI Website Change Monitor powered by Bright Data\n\nMonitor any list of URLs (competitor pricing, product pages, docs, regulations\u2026) and get notified the moment something visibly changes.\n\n## How it works\n\n1. **Schedule Trigger** runs the workflow on your interval\n2. **Airtable** returns the active URLs to monitor\n3. **Bright Data Web Unlocker** fetches each page (handles JS, CAPTCHAs, geo-blocks)\n4. **Claude Sonnet 4.6** compares the new HTML to the previous snapshot, ignoring noise (timestamps, build IDs, CSS\u2026)\n5. The new snapshot is saved back to your CRM\n6. If a real change is detected, **Slack** notifies your team\n\n## Why Bright Data?\n\n- Bypasses CAPTCHAs, bot detection and geo-blocks\n- Renders JS-heavy pages (SPAs, dynamic content)\n- Reliable for daily monitoring at scale\n\n## Prerequisites\n\n- A [Bright Data](https://brightdata.com) account with a Web Unlocker zone\n- An [Anthropic](https://anthropic.com) API key (any Claude model works)\n- A CRM/DB to store URLs and snapshots (Airtable shown by default)\n- A [Slack](https://slack.com) workspace and channel\n\n## Setup (~10 min)\n\n1. Connect Airtable, Bright Data, Anthropic and Slack credentials\n2. Set the Schedule Trigger interval (hourly, daily\u2026)\n3. Add URLs to your CRM with `Active = TRUE`\n4. Activate the workflow"
},
"typeVersion": 1
},
{
"id": "e62e7037-e2a6-4630-9096-80ec8884d767",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-224,
448
],
"parameters": {
"color": 4,
"width": 760,
"height": 496,
"content": "## \ud83d\ude80 1. Trigger & Get URLs\n\nThe **Schedule Trigger** kicks off the run, **Airtable** returns the active URLs, and the **loop** processes them one by one (so a single failure won't kill the run).\n\n### \ud83d\udd04 Swap-friendly\n- Trigger \u2192 Webhook, Cron, Form, Chat\n- CRM \u2192 HubSpot, Notion, Sheets, Postgres, Supabase\u2026"
},
"typeVersion": 1
},
{
"id": "8c4071ef-a920-42f0-88ee-e46a932e9874",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
480,
752
],
"parameters": {
"color": 3,
"width": 476,
"height": 480,
"content": "## \ud83c\udf10 2. Scrape with Bright Data\n\n**Bright Data Web Unlocker** fetches each page as clean HTML, then **Truncate HTML** keeps the first 90,000 chars to fit the LLM context window.\n\n- Handles JS, CAPTCHAs, geo-blocks\n- Adjust the truncation limit per model"
},
"typeVersion": 1
},
{
"id": "921edfb9-4579-480f-80ed-7970256ca108",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
992,
960
],
"parameters": {
"color": 5,
"width": 360,
"height": 320,
"content": "## \ud83e\udde0 3. AI Diff Analysis\n\n**Claude Sonnet 4.6** compares the new HTML against the previous snapshot. Returns:\n- `FIRST_RUN` if no previous version\n- `NO_CHANGE` if only noise changed\n- A 2-3 sentence summary otherwise\n\n\ud83d\udd04 Swap for any LLM (GPT-5, Gemini, Mistral\u2026)"
},
"typeVersion": 1
},
{
"id": "20d1c3e2-b303-4a82-995a-e9eed06d8285",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1376,
1264
],
"parameters": {
"color": 6,
"width": 608,
"height": 460,
"content": "## \ud83d\udcbe 4. Save & Notify\n\n1. **Save Snapshot** stores the new HTML + AI summary back to your CRM\n2. **Change Detected?** filters out `NO_CHANGE` and `FIRST_RUN`\n3. **Notify Slack** posts an alert only when something real happened\n\n### \ud83d\udd04 Swap Slack for anything\nEmail, Discord, Teams, Telegram, SMS, Notion, Google Docs, a CRM task, a webhook\u2026"
},
"typeVersion": 1
},
{
"id": "5754edee-2f98-45b9-bbf9-34bc8010bac1",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-144,
624
],
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"typeVersion": 1.3
},
{
"id": "89dff558-f14e-4ef3-90a1-caf1c5c49b12",
"name": "Get URLs to Monitor",
"type": "n8n-nodes-base.airtable",
"position": [
48,
736
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "YOUR_AIRTABLE_BASE_ID",
"cachedResultUrl": "",
"cachedResultName": "Web Monitor"
},
"table": {
"__rl": true,
"mode": "list",
"value": "YOUR_AIRTABLE_TABLE_ID",
"cachedResultUrl": "",
"cachedResultName": "URLs"
},
"options": {},
"operation": "search",
"filterByFormula": "{Active} = TRUE()"
},
"typeVersion": 2.2
},
{
"id": "aa8c6b1c-4424-42ff-9ea2-371d27117cfd",
"name": "Loop Over URLs",
"type": "n8n-nodes-base.splitInBatches",
"position": [
272,
832
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "99a10fa0-d2ed-48c9-a117-85f08411d0bf",
"name": "Scrape Page with Bright Data",
"type": "@brightdata/n8n-nodes-brightdata.brightData",
"position": [
512,
944
],
"parameters": {
"url": "={{ $json.fields.URL }}",
"zone": {
"__rl": true,
"mode": "list",
"value": "n8n_unlocker",
"cachedResultName": "n8n_unlocker"
},
"country": {
"__rl": true,
"mode": "list",
"value": "us"
},
"requestOptions": {}
},
"typeVersion": 1
},
{
"id": "9a2e0561-c1c3-4879-860c-64c856bba9cd",
"name": "Truncate HTML",
"type": "n8n-nodes-base.code",
"position": [
720,
1104
],
"parameters": {
"jsCode": "const fullHtml = String($input.item.json);\n\nreturn {\n json: {\n body: fullHtml.substring(0, 90000)\n }\n};"
},
"typeVersion": 2
},
{
"id": "c9bae1dd-1557-4f1d-be80-d52e8d046c2e",
"name": "AI Diff Analyzer",
"type": "@n8n/n8n-nodes-langchain.anthropic",
"position": [
912,
1232
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "claude-sonnet-4-6",
"cachedResultName": "claude-sonnet-4-6"
},
"options": {},
"messages": {
"values": [
{
"content": "=Compare two versions of a webpage and detect if anything visibly changed.\n\nPage: {{ $('Get URLs to Monitor').item.json.fields.Label }}\nURL: {{ $('Get URLs to Monitor').item.json.fields.URL }}\n\nPREVIOUS VERSION:\n{{ $('Get URLs to Monitor').item.json.fields['Last HTML'] }}\n\nCURRENT VERSION:\n{{ $json.body }}\n\nInstructions:\n- If PREVIOUS VERSION is empty, respond exactly: FIRST_RUN\n- Compare both versions for VISIBLE content changes (pricing, features, copy, products)\n- Ignore: HTML tags, timestamps, build IDs, CSS, scripts, meta tags, dynamic IDs\n- If only structural/technical noise changed, respond exactly: NO_CHANGE\n- Otherwise, in 2-3 sentences describe what visibly changed\n\nOutput: plain text only, no preamble."
}
]
}
},
"typeVersion": 1
},
{
"id": "b61b123c-a2c2-418b-8ce8-8fc878038862",
"name": "Save Snapshot to CRM",
"type": "n8n-nodes-base.airtable",
"position": [
1232,
1328
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "YOUR_AIRTABLE_BASE_ID",
"cachedResultUrl": "",
"cachedResultName": "Web Monitor"
},
"table": {
"__rl": true,
"mode": "list",
"value": "YOUR_AIRTABLE_TABLE_ID",
"cachedResultUrl": "",
"cachedResultName": "URLs"
},
"columns": {
"value": {
"id": "={{ $('Get URLs to Monitor').item.json.id }}",
"Last HTML": "={{ $('Truncate HTML').item.json.body }}",
"Last Checked At": "={{ $now.toISO() }}",
"Last Change Summary": "={{ $json.content[0].text }}"
},
"schema": [
{
"id": "id",
"type": "string",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "id",
"defaultMatch": true
},
{
"id": "URL",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Label",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Label",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Last HTML",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Last HTML",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Last Checked At",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Last Checked At",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Last Change Summary",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Last Change Summary",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Active",
"type": "boolean",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Active",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update"
},
"typeVersion": 2.2
},
{
"id": "cce179b9-e70c-4116-a3b1-7822fef4dd41",
"name": "Change Detected?",
"type": "n8n-nodes-base.if",
"position": [
1488,
1456
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "fd5232fd-376c-4eb9-b9da-7c4e5f030395",
"operator": {
"type": "string",
"operation": "notContains"
},
"leftValue": "={{ $json.fields['Last Change Summary'] }}",
"rightValue": "NO_CHANGE"
},
{
"id": "63748665-b174-4d08-a644-c713d4215412",
"operator": {
"type": "string",
"operation": "notContains"
},
"leftValue": "={{ $json.fields['Last Change Summary'] }}",
"rightValue": "FIRST_RUN"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "647c0dc3-c072-4ece-8e60-7e29f8fb1917",
"name": "Notify Slack",
"type": "n8n-nodes-base.slack",
"position": [
1728,
1568
],
"parameters": {
"text": "=\ud83d\udd14 Website change detected: {{ $json.fields.Label }}\n\n\ud83d\udd17 {{ $json.fields.URL }}\n\n\ud83d\udcdd What changed:\n{{ $json.fields['Last Change Summary'] }}\n\n\u23f0 Detected: {{ $now.toFormat('LLL d, HH:mm') }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SLACK_CHANNEL_ID",
"cachedResultName": "monitoring"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"typeVersion": 2.4
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "3cb62f12-afce-436b-bcc9-5ed48d77628b",
"connections": {
"Notify Slack": {
"main": [
[
{
"node": "Loop Over URLs",
"type": "main",
"index": 0
}
]
]
},
"Truncate HTML": {
"main": [
[
{
"node": "AI Diff Analyzer",
"type": "main",
"index": 0
}
]
]
},
"Loop Over URLs": {
"main": [
[],
[
{
"node": "Scrape Page with Bright Data",
"type": "main",
"index": 0
}
]
]
},
"AI Diff Analyzer": {
"main": [
[
{
"node": "Save Snapshot to CRM",
"type": "main",
"index": 0
}
]
]
},
"Change Detected?": {
"main": [
[
{
"node": "Notify Slack",
"type": "main",
"index": 0
}
],
[
{
"node": "Loop Over URLs",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Get URLs to Monitor",
"type": "main",
"index": 0
}
]
]
},
"Get URLs to Monitor": {
"main": [
[
{
"node": "Loop Over URLs",
"type": "main",
"index": 0
}
]
]
},
"Save Snapshot to CRM": {
"main": [
[
{
"node": "Change Detected?",
"type": "main",
"index": 0
}
]
]
},
"Scrape Page with Bright Data": {
"main": [
[
{
"node": "Truncate HTML",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
A schedule trigger periodically fetches the list of URLs to monitor from your CRM (Airtable by default) Bright Data Web Unlocker scrapes each page (handles JS, CAPTCHAs, geo-blocks) Claude Sonnet 4.6 compares the new HTML against the previous snapshot and ignores noise…
Source: https://n8n.io/workflows/15483/ — 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.
Complaints arrive via Gmail or a web form webhook Claude AI classifies each complaint: fault category, priority (P1/P2/P3), tenant tone, and drafts an acknowledgement email The right technician is loo
A daily Schedule Trigger pulls active LinkedIn job-search URLs from Airtable Bright Data Web Unlocker scrapes each search results page GPT extracts structured job data from the raw HTML A second GPT c
This workflow automatically fetches product reviews from your WooCommerce store, analyzes the sentiment of each review using AI, stores the results in Airtable and sends a summary of positive, neutral
Automatically enriches every new LinkedIn lead added to your Airtable base with AI-structured data and notifies your team in Slack. Airtable Trigger polls for new leads every minute BrightData scrapes
A scheduled process aggregates content from eight distinct data sources and standardizes all inputs into a unified format. AI models perform sentiment scoring, detect conspiracy or misinformation sign