{
  "id": "5y2giMadGf4zbUCl",
  "name": "Template 5: Competitor Pricing Intelligence",
  "tags": [],
  "nodes": [
    {
      "id": "0f9a9e65-61ae-4923-b160-b1a14ff56b25",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -656,
        160
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "0181d398-e4b6-4df5-a71c-aa6b390d3823",
      "name": "Get Competitor URL",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -432,
        160
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iczXABow_m8et6KB7FCb4YkrKPxJL_CeyZekvNQO9KY/edit#gid=0",
          "cachedResultName": "Lead"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1iczXABow_m8et6KB7FCb4YkrKPxJL_CeyZekvNQO9KY",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iczXABow_m8et6KB7FCb4YkrKPxJL_CeyZekvNQO9KY/edit?usp=drivesdk",
          "cachedResultName": "Competitor Pricing Intelligence"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "f37ae69f-40ee-43d4-95a2-59d81f3f5c59",
      "name": "Loop Over Competitor URL",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -208,
        160
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "6aa1248a-80b5-4f7b-b0bf-99bf24d577f1",
      "name": "Scrape URLS",
      "type": "n8n-nodes-scrapeunblocker.scrapeUnblocker",
      "position": [
        144,
        16
      ],
      "parameters": {
        "url": "={{$json[\"Competitor URL\"]}}",
        "proxy_country": "US"
      },
      "credentials": {},
      "typeVersion": 1
    },
    {
      "id": "67023246-3263-463d-b303-7374b907328b",
      "name": "parse data",
      "type": "n8n-nodes-base.code",
      "position": [
        352,
        16
      ],
      "parameters": {
        "jsCode": "const html = String(items[0].json);\n\n// ---------------------\n// Product Name\n// ---------------------\nlet productName = null;\n\n// Shopify JSON\nlet match =\n  html.match(/\"product_title\"\\s*:\\s*\"([^\"]+)\"/i) ||\n  html.match(/\"title\"\\s*:\\s*\"Airpods[^\"]+\"/i);\n\nif (match) {\n  productName = match[1] || match[0].replace(/\"title\"\\s*:\\s*\"/, \"\").replace(/\"$/, \"\");\n}\n\n// Product H1 fallback\nif (!productName) {\n  match = html.match(\n    /<h1[^>]*class=\"[^\"]*product__title[^\"]*\"[^>]*>(.*?)<\\/h1>/is\n  );\n\n  if (match) {\n    productName = match[1]\n      .replace(/<[^>]*>/g, \"\")\n      .replace(/\\s+/g, \" \")\n      .trim();\n  }\n}\n\n// ---------------------\n// Price\n// ---------------------\nconst priceMatch =\n  html.match(/Rs\\.\\s*([\\d,]+)/i);\n\nconst competitorPrice = priceMatch\n  ? Number(priceMatch[1].replace(/,/g, \"\"))\n  : null;\n\n// ---------------------\n// Stock\n// ---------------------\nconst soldOut =\n  /Sold out/i.test(html) ||\n  /\"available\"\\s*:\\s*false/i.test(html) ||\n  /inventory_quantity\"\\s*:\\s*0/i.test(html);\n\nreturn [\n  {\n    json: {\n      productName,\n      competitorPrice,\n      stockStatus: soldOut ? \"Out of Stock\" : \"In Stock\",\n    },\n  },\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "04051299-d29b-41d6-9a50-1b98a3a76313",
      "name": "structured the data",
      "type": "n8n-nodes-base.code",
      "position": [
        592,
        16
      ],
      "parameters": {
        "jsCode": "const yourPrice = Number(\n  $items(\"Get Competitor URL\")[0].json[\"Your Price\"]\n);\n\nconst competitorPrice = Number($json.competitorPrice);\nconst stockStatus = ($json.stockStatus || \"\").toLowerCase();\n\nconst inStock = stockStatus === \"in stock\";\n\nconst undercut = competitorPrice < yourPrice && inStock;\n\nconst priceGap = Number((yourPrice - competitorPrice).toFixed(2));\n\nconst percentageDifference = Number(\n  ((priceGap / yourPrice) * 100).toFixed(2)\n);\n\nreturn [\n  {\n    json: {\n      ...$json,\n      yourPrice,\n      undercut,\n      priceGap,\n      percentageDifference,\n      lastChecked: new Date().toISOString(),\n    },\n  },\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "893a585d-42e2-4a19-9746-e427844f5cd1",
      "name": "Update data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        832,
        16
      ],
      "parameters": {
        "columns": {
          "value": {
            "Price Gap": "={{ $json.priceGap }}",
            "row_number": "={{ $('Loop Over Competitor URL').item.json.row_number }}",
            "Last Checked": "={{ $json.lastChecked }}",
            "Product Name": "={{ $('Loop Over Competitor URL').item.json[\"Product Name\"] }}",
            "Stock Status": "={{ $json.stockStatus }}",
            "Competitor Price": "={{ $json.competitorPrice }}"
          },
          "schema": [
            {
              "id": "Product Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Product Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Your Price",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Your Price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Competitor URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Competitor URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Competitor Price",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Competitor Price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stock Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Stock Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Price Gap",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Price Gap",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Last Checked",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Last Checked",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "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": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iczXABow_m8et6KB7FCb4YkrKPxJL_CeyZekvNQO9KY/edit#gid=0",
          "cachedResultName": "Lead"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1iczXABow_m8et6KB7FCb4YkrKPxJL_CeyZekvNQO9KY",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iczXABow_m8et6KB7FCb4YkrKPxJL_CeyZekvNQO9KY/edit?usp=drivesdk",
          "cachedResultName": "Competitor Pricing Intelligence"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "6a98ede1-b2b5-41e4-93fd-c3c60b0a6628",
      "name": "undercut=true?",
      "type": "n8n-nodes-base.if",
      "position": [
        1056,
        16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a8a10e9b-dd33-452e-a7b4-d41b5c8da4b8",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $('structured the data').item.json.undercut }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "ee5c2ec2-2b8c-42ee-a8a9-8bddd244c8fd",
      "name": "Send alert",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1360,
        144
      ],
      "parameters": {
        "sendTo": "Your email",
        "message": "=The following product is being undercut.  Product: {{$json.productName}}  Your Price: Rs. {{$json.yourPrice}}  Competitor Price: Rs. {{$json.competitorPrice}}  Price Gap: Rs. {{$json.priceGap}}  Difference: {{$json.percentageDifference}}%  Stock: {{$json.stockStatus}}  Checked: {{$json.lastChecked}}",
        "options": {},
        "subject": "Competitor Pricing Alert",
        "emailType": "text"
      },
      "typeVersion": 2.2
    },
    {
      "id": "290389c2-56c0-4a80-b188-b6c7e6f110e1",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1600,
        -192
      ],
      "parameters": {
        "width": 704,
        "height": 752,
        "content": "## Competitor Pricing Intelligence\n\n### How It Works\n* **Schedule & Source:** Triggers automatically to fetch product details and competitor links from a designated Google Sheet.\n* **Batch Scraping:** Loops through competitor URLs one by one, retrieving live page HTML via **ScrapeUnblocker** (US proxy).\n* **Extraction:** Uses JavaScript regex on Shopify HTML patterns to extract `productName`, `competitorPrice`, and `stockStatus`.\n* **Gap Analysis:** Compares live competitor pricing against `Your Price`, calculating the price \n\n### Quick Setup Checklist\n1. **Google Sheets:** Authenticate account and verify spreadsheet ID, sheet name (`Lead`), and row matching setup (`row_number`).\n2. **ScrapeUnblocker:** Ensure valid API credentials for scraping.\n3. **Gmail:** Connect your account and specify the recipient address in the **Send alert** node.\n\n### Customization\n* **HTML Selectors:** Update regex in `parse data` if targeting non-Shopify sites or custom currency symbols.\n* **Undercut Thresholds:** Adjust logic in `structured the data` to refine margin targets or minimum gap requirements."
      },
      "typeVersion": 1
    },
    {
      "id": "42a0c503-889b-43c9-8c85-27cc9fecb546",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -752,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 704,
        "height": 576,
        "content": "## 1. Trigger & Competitor Data Fetch\nTriggers on a scheduled basis, fetches target competitor URLs and baseline pricing from Google Sheets, and initializes the batch loop."
      },
      "typeVersion": 1
    },
    {
      "id": "f19d23d3-367b-42a0-a796-f8700ccfde35",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 736,
        "height": 592,
        "content": "## 2. Page Scraping & Data Extraction\nScrapes competitor product pages using ScrapeUnblocker and extracts product titles, live prices, and stock availability via regex."
      },
      "typeVersion": 1
    },
    {
      "id": "39889074-a644-49c6-8cf2-1b6870e6ed39",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        784,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 768,
        "height": 592,
        "content": "## 3. Margin Analysis, Sheet Update & Alerting\nCalculates price gaps, updates row records in Google Sheets, checks for price undercutting, and sends an email notification if action is required."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "42e39542-20d4-4162-b4ad-b28751e6900e",
  "nodeGroups": [],
  "connections": {
    "Send alert": {
      "main": [
        [
          {
            "node": "Loop Over Competitor URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "parse data": {
      "main": [
        [
          {
            "node": "structured the data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape URLS": {
      "main": [
        [
          {
            "node": "parse data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update data": {
      "main": [
        [
          {
            "node": "undercut=true?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "undercut=true?": {
      "main": [
        [
          {
            "node": "Send alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Competitor URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Competitor URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Competitor URL": {
      "main": [
        [
          {
            "node": "Loop Over Competitor URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "structured the data": {
      "main": [
        [
          {
            "node": "Update data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Competitor URL": {
      "main": [
        [],
        [
          {
            "node": "Scrape URLS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}