AutomationFlowsWeb Scraping › Monitor Dynamic Website Changes with Firecrawl, Sheets & Gmail Alerts

Monitor Dynamic Website Changes with Firecrawl, Sheets & Gmail Alerts

ByFranz @agents-by-franz on n8n.io

Never miss important website updates again! This workflow automatically tracks changes on dynamic websites (think React apps, JavaScript-heavy sites) and sends you instant email notifications when something changes. Perfect for keeping tabs on competitors, monitoring product…

Webhook trigger★★★★☆ complexity22 nodesHTTP RequestGoogle SheetsGmail
Web Scraping Trigger: Webhook Nodes: 22 Complexity: ★★★★☆ Added:
Monitor Dynamic Website Changes with Firecrawl, Sheets & Gmail Alerts — n8n workflow card showing HTTP Request, Google Sheets, Gmail integration

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

This workflow follows the Gmail → 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": "psYzSydCkrbiJU2j",
  "name": "n8n_Challenge_Haeng_Franz_Rott",
  "tags": [],
  "nodes": [
    {
      "id": "042eb5f7-dbf1-47c5-a03f-f4e965a48d62",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "notes": "Trigger node \u2013 waits for an HTTP request on /webhook/n8n-challenge and starts the workflow. The final response is supplied by the Respond* nodes.",
      "position": [
        -1820,
        -140
      ],
      "parameters": {
        "path": "example_path",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 1
    },
    {
      "id": "71f3e740-7974-4302-94c8-161daa6c71c7",
      "name": "Firecrawl HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Calls Firecrawl API to scrape the target web page in Markdown and HTML. Uses Bearer-token authentication configured in credentials.",
      "onError": "continueErrorOutput",
      "position": [
        -1600,
        -140
      ],
      "parameters": {
        "url": "<insert_url>",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"url\": \"https://we-hang.com/pages/n8n-automation-expert-challenge\",\n  \"formats\": [\n    \"markdown\",\n    \"html\"\n  ]\n} ",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e8e42131-3da1-46ea-814a-247082658576",
      "name": "Get Timestamp",
      "type": "n8n-nodes-base.code",
      "notes": "JavaScript Code node \u2013 attaches a millisecond timestamp to the scraped content (legacy high-port flag kept untouched for compatibility).",
      "onError": "continueErrorOutput",
      "position": [
        -1380,
        -140
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\nconst updatedItems = items.map((item) => {\n  if (item.json.data.port > 5000) {\n    item.json.data[\"high-port\"] = true;\n  }\n  return item.json;\n});\n\nconst timestamp = new Date().getTime();\n\nreturn { updatedItems, timestamp };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7262962a-e5c8-4e87-9b17-80b43557782a",
      "name": "Update Current Content",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Writes the freshly scraped content into row 2 of the *comparison* sheet (columns current_timestamp & current_content).",
      "onError": "continueErrorOutput",
      "position": [
        -1160,
        -140
      ],
      "parameters": {
        "columns": {
          "value": {
            "row_number": "2",
            "current_content": "={{ $json.updatedItems[0].data.markdown }}",
            "current_timestamp": "={{ $json.timestamp }}"
          },
          "schema": [
            {
              "id": "last_timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_content",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_content",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "current_timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "current_timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "current_content",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "current_content",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 725909638,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit#gid=725909638",
          "cachedResultName": "comparison"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit?usp=drivesdk",
          "cachedResultName": "H\u00e4ng_Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "d26b1096-0288-4ca7-8a0c-388996226728",
      "name": "Read Current and Latest Content",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Loads the same row 2, giving us both last_content and current_content for comparison.",
      "onError": "continueErrorOutput",
      "position": [
        -940,
        -140
      ],
      "parameters": {
        "options": {
          "returnAllMatches": "returnFirstMatch"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 725909638,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit#gid=725909638",
          "cachedResultName": "comparison"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit?usp=drivesdk",
          "cachedResultName": "H\u00e4ng_Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4,
      "alwaysOutputData": true
    },
    {
      "id": "52c5313a-1ebb-4875-8c8b-61b407bc6cab",
      "name": "Is Equal?",
      "type": "n8n-nodes-base.if",
      "notes": "IF node \u2013 checks whether the scraped content changed. True branch \u2192 unchanged, False branch \u2192 changed.",
      "onError": "continueErrorOutput",
      "position": [
        -720,
        -140
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.last_content }}",
              "value2": "={{ $json.current_content }}"
            }
          ]
        },
        "combineOperation": "any"
      },
      "executeOnce": true,
      "typeVersion": 1
    },
    {
      "id": "cd72f620-e31f-4c0d-8835-59ed187a1ccd",
      "name": "Respond Unchanged",
      "type": "n8n-nodes-base.respondToWebhook",
      "notes": "Returns a 200 OK response to the original webhook caller when no change was detected.",
      "position": [
        -500,
        -240
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "3ac13426-967f-46a8-af12-a5cac4bd4b20",
      "name": "Read Latest and Latest Content",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Reads the current sheet row again on the \u2018changed\u2019 path so subsequent code nodes have the freshest values.",
      "onError": "continueErrorOutput",
      "position": [
        -500,
        -40
      ],
      "parameters": {
        "options": {
          "returnAllMatches": "returnFirstMatch"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 725909638,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit#gid=725909638",
          "cachedResultName": "comparison"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit?usp=drivesdk",
          "cachedResultName": "H\u00e4ng_Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4,
      "alwaysOutputData": true
    },
    {
      "id": "6ca28b23-f8da-4dd1-af41-ca42f76b69f7",
      "name": "Extract Differences",
      "type": "n8n-nodes-base.code",
      "notes": "Splits last & current content into arrays, determines new vs missing segments, attaches another timestamp.",
      "onError": "continueErrorOutput",
      "position": [
        -280,
        -240
      ],
      "parameters": {
        "jsCode": "const items = $input.all().map((item) => item.json);\n\nconst result = items.map((item) => {\n  const lastContent = item.last_content.split(\",\");\n  const currentContent = item.current_content.split(\",\");\n  const newContent = currentContent.filter((i) => !lastContent.includes(i));\n  const missingContent = lastContent.filter((i) => !currentContent.includes(i));\n  return {\n    ...item,\n    newContent,\n    missingContent,\n    timestamp: new Date().getTime(),\n  };\n});\n\nreturn result;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "ec0188a2-f4c0-4f8d-b155-fe5eb67faf8f",
      "name": "Gmail",
      "type": "n8n-nodes-base.gmail",
      "notes": "Sends a plain-text email detailing what changed (first diff only) to n8nchallenge@we-hang.com.",
      "onError": "continueErrorOutput",
      "position": [
        -60,
        -240
      ],
      "parameters": {
        "sendTo": "=example@gmail.com",
        "message": "=Guten Tag, \n\nEs gab eine \u00c4nderung an der beobachteten Website:\n\nUm \"{{ $json.timestamp }}\" wurde \"{{ $json.missingContent[0] }}\" durch \"{{ $json.newContent[0] }}\" ersetzt.\n\nViele Gr\u00fc\u00dfe ",
        "options": {},
        "subject": "Relevante Inhalts\u00e4nderung auf Website",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "1b85582b-48e1-4f70-8df2-fa5dbd505ddd",
      "name": "Append Log Row",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Adds a new row to *Log* sheet with timestamp & full current content \u2013 creates a historical audit trail.",
      "onError": "continueErrorOutput",
      "position": [
        -60,
        -40
      ],
      "parameters": {
        "columns": {
          "value": {
            "content": "={{ $json.current_content }}",
            "timestamp": "={{ $json.current_timestamp }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "content",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "content",
              "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/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit#gid=0",
          "cachedResultName": "Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit?usp=drivesdk",
          "cachedResultName": "H\u00e4ng_Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "a57f34af-ddca-4973-8c52-7947006cc5fa",
      "name": "Update Latest Content",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Moves current values into the *last_* columns (row 2) so the next run will compare against them.",
      "onError": "continueErrorOutput",
      "position": [
        -60,
        160
      ],
      "parameters": {
        "columns": {
          "value": {
            "row_number": "2",
            "last_content": "={{ $json.current_content }}",
            "last_timestamp": "={{ $json.current_timestamp }}"
          },
          "schema": [
            {
              "id": "last_timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_content",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_content",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "current_timestamp",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "current_timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "current_content",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "current_content",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 725909638,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit#gid=725909638",
          "cachedResultName": "comparison"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JashxHNjWMu0IGg7ck6pDxtH6Eo-jVvIIMAWWRspc8g/edit?usp=drivesdk",
          "cachedResultName": "H\u00e4ng_Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "69c262ab-fcc5-480f-acff-01c982b68673",
      "name": "Respond Changed",
      "type": "n8n-nodes-base.respondToWebhook",
      "notes": "Returns a success response to the original HTTP call confirming that a change was processed.",
      "position": [
        160,
        60
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "ccbaaf84-52cf-40dd-9147-e09e5c91e71e",
      "name": "\ud83d\udccb SETUP OVERVIEW",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2140,
        -840
      ],
      "parameters": {
        "width": 400,
        "height": 460,
        "content": "\ud83c\udfaf WORKFLOW PURPOSE:\nThis workflow monitors a webpage for content changes and sends email notifications when changes are detected.\n\n\ud83d\udcca KEY COMPONENTS:\n\u2022 Firecrawl API for web scraping\n\u2022 Google Sheets for data storage & comparison\n\u2022 Gmail for notifications\n\u2022 Webhook trigger for external activation\n\n\ud83d\udd04 WORKFLOW FLOW:\nWebhook \u2192 Scrape \u2192 Compare \u2192 Email (if changed) \u2192 Log \u2192 Respond"
      },
      "typeVersion": 1
    },
    {
      "id": "1adc1940-ff74-4454-b841-6e9379c76d9e",
      "name": "\ud83d\udd10 CREDENTIALS SETUP",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1700,
        -840
      ],
      "parameters": {
        "width": 400,
        "height": 460,
        "content": "\u2699\ufe0f REQUIRED CREDENTIALS:\n\n1\ufe0f\u20e3 FIRECRAWL API:\n\u2022 Sign up at https://firecrawl.dev\n\u2022 Get API key from dashboard\n\u2022 Create 'Bearer YOUR_TOKEN_HERE' credential in n8n\n\u2022 Name: 'Bearer YOUR_TOKEN_HERE account'\n\n2\ufe0f\u20e3 GOOGLE SHEETS:\n\u2022 Enable Google Sheets API in Google Cloud Console\n\u2022 Create OAuth2 credentials\n\u2022 Add credential in n8n as 'Google Sheets account'\n\n3\ufe0f\u20e3 GMAIL:\n\u2022 Enable Gmail API in Google Cloud Console\n\u2022 Create OAuth2 credentials\n\u2022 Add credential in n8n as 'Gmail account'"
      },
      "typeVersion": 1
    },
    {
      "id": "38fc9213-614c-467e-af3f-18fe8c62a99e",
      "name": "\ud83d\udcca GOOGLE SHEETS SETUP",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1320,
        100
      ],
      "parameters": {
        "width": 400,
        "height": 300,
        "content": "\ud83d\udccb SPREADSHEET STRUCTURE:\n\n\ud83d\uddc2\ufe0f Create a Google Sheet with 2 tabs:\n\n1\ufe0f\u20e3 'Log' sheet (gid=0):\n\u2022 Column A: timestamp\n\u2022 Column B: content\n\n2\ufe0f\u20e3 'comparison' sheet (gid=725909638):\n\u2022 Column A: last_timestamp\n\u2022 Column B: last_content  \n\u2022 Column C: current_timestamp\n\u2022 Column D: current_content\n\u2022 Column E: row_number\n\u2022 Row 2: Set 'row_number' to '2'\n\n\ud83d\udcdd Copy the spreadsheet ID from the URL and update all Google Sheets nodes with your ID."
      },
      "typeVersion": 1
    },
    {
      "id": "d9a32777-2a21-46ba-a179-534f27616b28",
      "name": "\u2699\ufe0f CONFIGURATION",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1240,
        -840
      ],
      "parameters": {
        "width": 400,
        "height": 460,
        "content": "\ud83d\udd27 CUSTOMIZATION SETTINGS:\n\n\ud83c\udf10 TARGET WEBSITE:\n\u2022 Update Firecrawl node 'url' parameter\n\n\ud83d\udce7 EMAIL SETTINGS:\n\u2022 Update Gmail node 'sendTo' field\n\u2022 Customize subject and message template\n\n\ud83e\ude9d WEBHOOK PATH:\n\u2022 Can be customized in Webhook node\n\n\ud83d\udcc4 SCRAPING FORMAT:\n\u2022 Currently set to markdown + html\n\u2022 Modify in Firecrawl node if needed"
      },
      "typeVersion": 1
    },
    {
      "id": "6b09600c-1a63-4b96-bf3a-eaeb6dfd02f0",
      "name": "\ud83e\uddea TESTING & ACTIVATION",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        980,
        -840
      ],
      "parameters": {
        "width": 400,
        "height": 460,
        "content": "\u2705 TESTING STEPS:\n\n1\ufe0f\u20e3 MANUAL TEST:\n\u2022 Click 'Execute Workflow' button\n\u2022 Check if all nodes execute successfully\n\u2022 Verify Google Sheets get populated\n\u2022 Check if email is sent (if content differs)\n\n2\ufe0f\u20e3 WEBHOOK TEST:\n\u2022 Activate workflow (toggle switch)\n\u2022 Copy webhook URL from Webhook node\n\u2022 Test with curl or Postman:\n  curl -X POST [your-webhook-url]\n\n3\ufe0f\u20e3 TROUBLESHOOTING:\n\u2022 Check node execution logs for errors\n\u2022 Verify all credentials are properly configured\n\u2022 Ensure Google Sheets permissions are correct"
      },
      "typeVersion": 1
    },
    {
      "id": "a43347e4-f5b3-4122-b3b8-45ff925a3402",
      "name": "\ud83e\udd16 AUTOMATION SETUP",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -780,
        -840
      ],
      "parameters": {
        "width": 400,
        "height": 460,
        "content": "\u23f0 SCHEDULING OPTIONS:\n\n\ud83d\udd04 CRON JOB (Recommended):\n\u2022 Set up external cron job to call webhook\n\u2022 Example: */30 * * * * (every 30 minutes)\n\u2022 Command: curl -X POST [webhook-url]\n\n\u26a1 N8N CRON TRIGGER:\n\u2022 Replace Webhook with Cron Trigger node\n\u2022 Set desired interval\n\u2022 Remove Respond nodes (not needed)\n\n\ud83c\udf10 EXTERNAL MONITORING:\n\u2022 Use services like UptimeRobot\n\u2022 Configure to call webhook at intervals\n\u2022 Provides additional monitoring layer"
      },
      "typeVersion": 1
    },
    {
      "id": "8184238c-654e-4b3e-b5f9-e9e8f423fe0f",
      "name": "\ud83d\udd27 MAINTENANCE & MONITORING",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -340,
        -840
      ],
      "parameters": {
        "width": 400,
        "height": 460,
        "content": "\ud83d\udcc8 MONITORING:\n\n\ud83d\udcca EXECUTION HISTORY:\n\u2022 Check n8n execution log regularly\n\u2022 Monitor for failed executions\n\u2022 Set up n8n error notifications\n\n\ud83d\udccb LOG REVIEW:\n\u2022 Check Google Sheets 'Log' tab\n\u2022 Verify timestamps are updating\n\u2022 Monitor content changes over time\n\n\ud83d\udea8 ERROR HANDLING:\n\u2022 All nodes have 'Continue on Error' enabled\n\u2022 Failed nodes won't stop the workflow\n\u2022 Check individual node outputs for issues"
      },
      "typeVersion": 1
    },
    {
      "id": "4b6fd3db-6276-4885-b704-d5709a9ad630",
      "name": "\ud83d\udd12 SECURITY & BEST PRACTICES",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        100,
        -840
      ],
      "parameters": {
        "width": 400,
        "height": 460,
        "content": "\ud83d\udee1\ufe0f SECURITY CONSIDERATIONS:\n\n\ud83d\udd10 API KEYS:\n\u2022 Store all credentials securely in n8n\n\u2022 Never hardcode API keys in workflow\n\u2022 Regularly rotate API keys\n\n\ud83d\udce7 EMAIL SECURITY:\n\u2022 Use dedicated email for notifications\n\u2022 Consider email filtering rules\n\u2022 Monitor for spam/bounce issues\n\n\ud83c\udf10 WEBHOOK SECURITY:\n\u2022 Consider adding authentication\n\u2022 Use HTTPS endpoints only\n\u2022 Monitor webhook access logs\n\n\ud83d\udcbe DATA PRIVACY:\n\u2022 Regularly clean old log entries\n\u2022 Be mindful of scraped content sensitivity"
      },
      "typeVersion": 1
    },
    {
      "id": "10a36c1e-34ef-46dd-83a7-0cd4c6034a6c",
      "name": "\ud83d\udd0d COMMON ISSUES & SOLUTIONS",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        540,
        -840
      ],
      "parameters": {
        "width": 400,
        "height": 460,
        "content": "\u26a0\ufe0f COMMON PROBLEMS:\n\n\ud83d\udeab FIRECRAWL ERRORS:\n\u2022 Check API key validity\n\u2022 Verify target URL is accessible\n\u2022 Check rate limits on Firecrawl account\n\n\ud83d\udcca GOOGLE SHEETS ISSUES:\n\u2022 Verify sheet ID and tab names\n\u2022 Check OAuth permissions\n\u2022 Ensure sheets have correct structure\n\n\ud83d\udce7 EMAIL PROBLEMS:\n\u2022 Check Gmail API quotas\n\u2022 Verify recipient email address\n\u2022 Check spam folders\n\n\ud83e\ude9d WEBHOOK ISSUES:\n\u2022 Ensure workflow is activated\n\u2022 Check webhook URL format\n\u2022 Verify n8n instance is accessible"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "732c1f33-081b-46d4-8fa3-1b497856198b",
  "connections": {
    "Gmail": {
      "main": [
        [
          {
            "node": "Respond Changed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Firecrawl HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Equal?": {
      "main": [
        [
          {
            "node": "Respond Unchanged",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Read Latest and Latest Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Timestamp": {
      "main": [
        [
          {
            "node": "Update Current Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Log Row": {
      "main": [
        [
          {
            "node": "Respond Changed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Differences": {
      "main": [
        [
          {
            "node": "Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Latest Content": {
      "main": [
        [
          {
            "node": "Respond Changed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Firecrawl HTTP Request": {
      "main": [
        [
          {
            "node": "Get Timestamp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Current Content": {
      "main": [
        [
          {
            "node": "Read Current and Latest Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Latest and Latest Content": {
      "main": [
        [
          {
            "node": "Extract Differences",
            "type": "main",
            "index": 0
          },
          {
            "node": "Append Log Row",
            "type": "main",
            "index": 0
          },
          {
            "node": "Update Latest Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Current and Latest Content": {
      "main": [
        [
          {
            "node": "Is Equal?",
            "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

Never miss important website updates again! This workflow automatically tracks changes on dynamic websites (think React apps, JavaScript-heavy sites) and sends you instant email notifications when something changes. Perfect for keeping tabs on competitors, monitoring product…

Source: https://n8n.io/workflows/5510/ — 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

Automate Your Job Search: Find Job Listings on LinkedIn, Indeed, Glassdoor, Upwork & Adzuna!

HTTP Request, OpenRouter Chat, Agent +3
Web Scraping

&gt; Watch the full Youtube Video Tutorial [](https://youtu.be/Y-wUr2-UYZk)

Data Table, HTTP Request, Google Sheets +1
Web Scraping

This automated n8n workflow scrapes job listings from Upwork using Apify, processes and cleans the data, and generates daily email reports with job summaries. The system uses Google Sheets for data st

Google Sheets, HTTP Request, Gmail
Web Scraping

This workflow automatically scrapes business leads from Google Maps on a daily schedule and ensures only high-quality, unique leads are processed. New businesses are cleaned, validated, and deduplicat

Google Sheets, Gmail, HTTP Request
Web Scraping

This n8n workflow automates the process of scraping LinkedIn profiles using the Apify platform and organizing the extracted data into Google Sheets for easy analysis and follow-up. Lead Generation: Ex

Google Sheets, HTTP Request, Gmail