{
  "id": "QAneGL7LmmVuAy1G",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "monitoring competitor prices - for free",
  "tags": [
    {
      "id": "3ytagip6FSiGNhyx",
      "name": "video",
      "createdAt": "2025-05-31T12:22:41.809Z",
      "updatedAt": "2025-05-31T12:22:41.809Z"
    },
    {
      "id": "AAeX8YOWlYNI5sCL",
      "name": "production",
      "createdAt": "2025-05-31T12:22:39.329Z",
      "updatedAt": "2025-05-31T12:22:39.329Z"
    },
    {
      "id": "xoQDVZ3WQVEfZeAf",
      "name": "final",
      "createdAt": "2025-05-31T12:22:36.344Z",
      "updatedAt": "2025-05-31T12:22:36.344Z"
    }
  ],
  "nodes": [
    {
      "id": "780a213c-0e81-4d95-910e-a467f316ebf6",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2460,
        160
      ],
      "parameters": {
        "width": 420,
        "height": 100,
        "content": "**Fires every morning at 8:00 AM server time to kick off the price-check cycle). Then it Reads the \u201cmaster\u201d Google Sheet tab, pulling each row\u2019s product_url + last_price for processing**"
      },
      "typeVersion": 1
    },
    {
      "id": "9b9932ec-8cfd-4096-965b-592b13a1fb8e",
      "name": "Daily 8 AM Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2420,
        290
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6427045b-8b73-42f0-9dcc-768cbfac9c4a",
      "name": "Fetch Product List from Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2200,
        290
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "Replace with your google sheet url by downloading the googlesheet in the description of this automation",
          "cachedResultName": "product_data"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "18ZAjxmwanf6MS7JfrEX6ett6J-tOQi58B257hE6sOS4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/18ZAjxmwanf6MS7JfrEX6ett6J-tOQi58B257hE6sOS4/edit?usp=drivesdk",
          "cachedResultName": "competitor_price_drop_automation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "0b4a1398-bb29-4f42-9475-eb08ab4dabfc",
      "name": "Process Each Product in Batches of 1",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1980,
        290
      ],
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "ccf9f9ca-b380-457e-9fc2-c6ffe1636984",
      "name": "Pause Between Requests",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1760,
        40
      ],
      "parameters": {
        "amount": 20
      },
      "typeVersion": 1.1
    },
    {
      "id": "aa49653b-cf9f-449b-953d-a9a19ac74187",
      "name": "Load Product Page HTML",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1540,
        40
      ],
      "parameters": {
        "url": "={{ $json.product_url }}",
        "options": {},
        "jsonHeaders": "{\n  \"User-Agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:119.0) Gecko/20100101 Firefox/119.0\",\n  \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n  \"Accept-Language\": \"en-IN,en;q=0.9\",\n  \"Accept-Encoding\": \"gzip, deflate, br\"\n}",
        "sendHeaders": true,
        "specifyHeaders": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "5dbc3d45-96d6-4b71-8d3f-2513f960e265",
      "name": "Extract Current Price from HTML",
      "type": "n8n-nodes-base.html",
      "position": [
        -1320,
        40
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "current_price",
              "cssSelector": ".price__regular > span.price-item--regular"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "76ad9683-bae1-4f95-af09-44598d9448d1",
      "name": "Normalize Price Values",
      "type": "n8n-nodes-base.code",
      "position": [
        -1100,
        40
      ],
      "parameters": {
        "jsCode": "// HTML Extract result\nconst scraped = items[0].json;\n\n// Original data from Wait node\nconst original = $items(\"Pause Between Requests\")[0].json;\n\n// Clean and parse price\nconst priceNum = parseFloat((scraped.current_price || \"\").replace(/[^0-9.]+/g, \"\"));\n\n// Original price from Google Sheet\nconst lastPrice = parseFloat(original.price);\n\nreturn [{\n  json: {\n    product_url: original.product_url,\n    row_number: original.row_number,\n    last_price: lastPrice,\n    current_price: priceNum\n  }\n}];\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5feae925-0642-40d1-89be-c220a5a1c8a2",
      "name": "Compute Price Change",
      "type": "n8n-nodes-base.code",
      "position": [
        -880,
        40
      ],
      "parameters": {
        "jsCode": "// Get the incoming item\nconst item = items[0].json;\n\n// Compute whether the price changed\nconst priceChanged = item.last_price !== item.current_price;\n\n// Calculate percentage difference (will be NaN if last_price is null/0)\nlet priceDiffPct = null;\nif (item.last_price && item.last_price !== 0) {\n  priceDiffPct = (\n    ((item.current_price - item.last_price) / item.last_price) *\n    100\n  ).toFixed(2);\n}\n\n// Return everything, including product_url and last_price\nreturn [\n  {\n    json: {\n      product_url: item.product_url,\n      last_price: item.last_price,\n      current_price: item.current_price,\n      price_changed: priceChanged,\n      price_diff_pct: priceDiffPct !== null ? parseFloat(priceDiffPct) : null,\n      timestamp: new Date().toISOString(),\n    },\n  },\n];\n\n\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5dea2082-d5c5-4c13-8c71-ab7ac498632d",
      "name": "Clean Up Parsed Fields",
      "type": "n8n-nodes-base.code",
      "position": [
        -660,
        40
      ],
      "parameters": {
        "jsCode": "// Function node: 'items' is an array of incoming items\nif (!items.length) {\n  return [];\n}\n\nconst input = items[0].json;\nconst cleaned = {};\n\nfor (const key in input) {\n  const cleanKey = key.replace(/\\t/g, '').trim();\n  cleaned[cleanKey] = input[key];\n}\n\nreturn [\n  {\n    json: {\n      product_url: cleaned.product_url,\n      last_price: cleaned.last_price,\n      current_price: cleaned.current_price,\n      price_changed: cleaned.price_changed,\n      price_diff_pct: cleaned.price_diff_pct,\n      timestamp: cleaned.timestamp,\n    },\n  },\n];\n\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "21c27cb9-c6bc-4cc3-9ad1-3d37fc6fbc3d",
      "name": "Is Price Changed?",
      "type": "n8n-nodes-base.if",
      "position": [
        -440,
        40
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "5e285834-82ef-4c77-b96b-97661760f996",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.price_changed}}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "50274daa-48c7-4c58-aaf5-264013582f97",
      "name": "Build Telegram Alert Message",
      "type": "n8n-nodes-base.code",
      "position": [
        -220,
        140
      ],
      "parameters": {
        "jsCode": "const itemsFormatted = [];\n\nfor (const item of items) {\n  const data = item.json;\n\n  // Determine alert type\n  const isDrop = data.price_diff_pct < 0;\n  const alertType = isDrop ? \"\ud83d\udcc9 *Price Drop Alert*\" : \"\ud83d\udcc8 *Price Hike Alert*\";\n\n  // Format timestamp to IST\n  const date = new Date(data.timestamp);\n  const istOffset = 5.5 * 60 * 60 * 1000;\n  const istDate = new Date(date.getTime() + istOffset);\n  const formattedDate = istDate.toLocaleString(\"en-IN\", {\n    day: \"2-digit\",\n    month: \"short\",\n    year: \"numeric\",\n    hour: \"2-digit\",\n    minute: \"2-digit\",\n    hour12: true\n  });\n\n  // Build message\n  const message = `${alertType}\n\n\ud83d\udecd\ufe0f *Product:* (${data.product_url})\n\ud83d\udcb8 *Last Price:* $${data.last_price}\n\ud83d\udcb0 *Current Price:* $${data.current_price}\n\ud83d\udcca *Change:* ${data.price_diff_pct > 0 ? \"+\" : \"\"}${data.price_diff_pct}%\n\ud83d\udd52 *Time:* ${formattedDate}`;\n\n  itemsFormatted.push({ json: { message } });\n}\n\nreturn itemsFormatted;\n\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f62b1f32-fe79-43c3-ad17-0ff31fcdc489",
      "name": "Log Price History to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -220,
        -60
      ],
      "parameters": {
        "columns": {
          "value": {
            "last_price": "={{ $json.last_price }}",
            "timestamp\t": "={{ new Date($json.timestamp).toLocaleString('en-IN', {\n  timeZone: 'Asia/Kolkata',\n  day: '2-digit',\n  month: 'short',\n  year: 'numeric',\n  hour: '2-digit',\n  minute: '2-digit',\n  hour12: true\n}) }}\n",
            "product_url": "={{ $json.product_url }}",
            "current_price": "={{ $json.current_price }}",
            "price_diff_pct": "={{ $json.price_diff_pct}}",
            "price_changed        ": "={{ $json.price_changed }}"
          },
          "schema": [
            {
              "id": "timestamp\t",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp\t",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "product_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "product_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "current_price",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "current_price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_price",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "last_price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "price_changed        ",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "price_changed        ",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "price_diff_pct",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "price_diff_pct",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "useAppend": true
        },
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 2110110902,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/18ZAjxmwanf6MS7JfrEX6ett6J-tOQi58B257hE6sOS4/edit#gid=2110110902",
          "cachedResultName": "price_tracking"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "18ZAjxmwanf6MS7JfrEX6ett6J-tOQi58B257hE6sOS4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/18ZAjxmwanf6MS7JfrEX6ett6J-tOQi58B257hE6sOS4/edit?usp=drivesdk",
          "cachedResultName": "competitor_price_drop_automation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "d5555b69-d7ee-4ab0-a851-5288de0d39cf",
      "name": "Pause Before Updating Sheet",
      "type": "n8n-nodes-base.wait",
      "position": [
        0,
        -60
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "ade41bb7-fbe3-4e08-a7fa-6c85291dc9fa",
      "name": "Update Last Price in Master Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        220,
        -60
      ],
      "parameters": {
        "columns": {
          "value": {
            "price": "={{ $json.current_price }}",
            "product_url": "={{ $json.product_url }}"
          },
          "schema": [
            {
              "id": "product_url",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "product_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "price",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "product_url"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "Replace with your google sheet url by downloading the googlesheet in the description of this automation",
          "cachedResultName": "product_data"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "18ZAjxmwanf6MS7JfrEX6ett6J-tOQi58B257hE6sOS4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/18ZAjxmwanf6MS7JfrEX6ett6J-tOQi58B257hE6sOS4/edit?usp=drivesdk",
          "cachedResultName": "competitor_price_drop_automation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "9ebdbe49-fda1-42f5-99d9-a5e1e3ab800f",
      "name": "Send Price Alert via Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        0,
        215
      ],
      "parameters": {
        "text": "={{$json[\"message\"]}}",
        "chatId": "123456789",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "dcb4eff8-9d44-4016-a89a-787be943d0e9",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2040,
        440
      ],
      "parameters": {
        "height": 100,
        "content": "**Iterates through all products in groups (e.g., 1 at a time) so we don\u2019t overwhelm the target site with parallel requests.**"
      },
      "typeVersion": 1
    },
    {
      "id": "1a306c68-e652-4d63-b11e-96366c4a59ed",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1560,
        -60
      ],
      "parameters": {
        "width": 360,
        "height": 80,
        "content": "**fires an HTTP GET for each product_url, then uses a CSS selector to pull the live price text from the returned HTML.**"
      },
      "typeVersion": 1
    },
    {
      "id": "81f8701a-219f-43a3-aceb-78f2298273cc",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1120,
        180
      ],
      "parameters": {
        "width": 380,
        "height": 100,
        "content": "**Strips currency symbols/commas to convert strings into numbers; compares last_price vs. current_price to calculate % change and set price_changed=true if different**"
      },
      "typeVersion": 1
    },
    {
      "id": "7fd2108e-5fcb-4ba6-938b-6c940f8a305f",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -740,
        -100
      ],
      "parameters": {
        "width": 220,
        "height": 120,
        "content": "\n**Removes stray whitespace or malformed keys (e.g., stray tabs) so downstream nodes can read fields consistently**"
      },
      "typeVersion": 1
    },
    {
      "id": "7a525b63-e871-46b9-b0b0-4e25a6c7f2bb",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -540,
        180
      ],
      "parameters": {
        "height": 100,
        "content": "**Only products with price_changed=true proceed to alerting and logging; unchanged items exit here.**"
      },
      "typeVersion": 1
    },
    {
      "id": "a6352172-9fa3-47b7-8bf0-c948fbe03701",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -180,
        420
      ],
      "parameters": {
        "width": 380,
        "height": 80,
        "content": "\n**Formats a human\u2010readable message (product name/URL, old vs. new price, % change, timestamp) and pushes it to the configured Telegram chat**\n"
      },
      "typeVersion": 1
    },
    {
      "id": "90b702f6-6e75-452d-9315-3f70c4157aab",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -200,
        -160
      ],
      "parameters": {
        "width": 520,
        "height": 80,
        "content": "\n**Appends a row to Price_History with timestamp, % change, old vs. new price; then waits 1 minute before overwriting last_price in the master sheet with current_price.**\n"
      },
      "typeVersion": 1
    },
    {
      "id": "3e3a4498-248d-408e-903b-916a035db039",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1300,
        -520
      ],
      "parameters": {
        "width": 580,
        "content": "## Detailed Explanations of this automation\n \nYou can see the detailed step-by-step instructions here:\n\n- [Free n8n Web Scraping Competitor Price Tracking](https://www.blog.datahut.co/post/free-n8n-web-scraping-competitor-price-tracking)\n- [YouTube Tutorial](https://www.youtube.com/watch?v=a9esT732mmE)\n\n\n\n\n\n\nIs this conversation helpful so far?\n\n\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "2c1bce5e-8208-42c3-8fe8-308b5855695c",
  "connections": {
    "Is Price Changed?": {
      "main": [
        [
          {
            "node": "Build Telegram Alert Message",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log Price History to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily 8 AM Trigger": {
      "main": [
        [
          {
            "node": "Fetch Product List from Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute Price Change": {
      "main": [
        [
          {
            "node": "Clean Up Parsed Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean Up Parsed Fields": {
      "main": [
        [
          {
            "node": "Is Price Changed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Product Page HTML": {
      "main": [
        [
          {
            "node": "Extract Current Price from HTML",
            "type": "main",
            "index": 0
          },
          {
            "node": "Process Each Product in Batches of 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Price Values": {
      "main": [
        [
          {
            "node": "Compute Price Change",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pause Between Requests": {
      "main": [
        [
          {
            "node": "Load Product Page HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Price History to Sheet": {
      "main": [
        [
          {
            "node": "Pause Before Updating Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pause Before Updating Sheet": {
      "main": [
        [
          {
            "node": "Update Last Price in Master Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Telegram Alert Message": {
      "main": [
        [
          {
            "node": "Send Price Alert via Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Product List from Sheet": {
      "main": [
        [
          {
            "node": "Process Each Product in Batches of 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Price Alert via Telegram": {
      "main": [
        [
          {
            "node": "Process Each Product in Batches of 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Current Price from HTML": {
      "main": [
        [
          {
            "node": "Normalize Price Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Each Product in Batches of 1": {
      "main": [
        [],
        [
          {
            "node": "Pause Between Requests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}