This workflow corresponds to n8n.io template #17398 — 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 →
{
"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
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs on a schedule to read competitor product URLs and your price from Google Sheets, scrapes each competitor page using ScrapeUnblocker, calculates price differences and stock status, writes results back to the sheet, and sends a Gmail alert when a competitor…
Source: https://n8n.io/workflows/17398/ — 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.
This workflow runs every 45 minutes to monitor U.S. House periodic transaction reports from House.gov and insider Form 4 filings from the SEC EDGAR Atom feed, enriches each filing with trade details f
This workflow runs every 5 hours to scrape domain drop lists from ExpiredDomains.net (including the GoDaddy expired domains section) via ScrapeUnblocker, deduplicates against a Google Sheets log, and
This workflow runs on a frequent schedule to read monitored product URLs from Google Sheets, scrape each product page with ScrapeUnblocker, detect stock-status changes, update the sheet with the lates
YOUR_ID 4. Uses gmail, googleDrive, googleSheets, httpRequest. Scheduled trigger; 53 nodes.
special-day-email-sender. Uses googleSheets, gmail. Scheduled trigger; 43 nodes.