This workflow corresponds to n8n.io template #17524 — 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": "jtgXNhQVsYEsbwPj",
"name": "Template 7: Sneaker & Limited Drop Availability Alert",
"tags": [],
"nodes": [
{
"id": "47b6ab0e-714f-4ea8-b42e-0e80a0939cee",
"name": "Sneaker Monitor Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-672,
144
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "292bb755-f438-4b88-aa57-8c91fb6e39b0",
"name": "Get Product URLs",
"type": "n8n-nodes-base.googleSheets",
"position": [
-448,
144
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Hvk4WqCeZ8hiN_654QI2E9XO25HjKIU1Qo5kKMPMgow/edit#gid=0",
"cachedResultName": "Products"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1Hvk4WqCeZ8hiN_654QI2E9XO25HjKIU1Qo5kKMPMgow",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Hvk4WqCeZ8hiN_654QI2E9XO25HjKIU1Qo5kKMPMgow/edit?usp=drivesdk",
"cachedResultName": "Sneaker Drop Monitor"
}
},
"typeVersion": 4.7
},
{
"id": "6ee1794c-a9d3-4249-ac4d-1fd86209e6af",
"name": "Fetch Product Page",
"type": "n8n-nodes-scrapeunblocker.scrapeUnblocker",
"position": [
32,
16
],
"parameters": {
"url": "={{ $json[\"Product URL\"] }}",
"proxy_country": "US"
},
"credentials": {},
"typeVersion": 1
},
{
"id": "3062cabc-c846-456c-bb16-ddbf2e698115",
"name": "Loop Through Products",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-224,
144
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "f91913ea-c86c-43eb-b486-c254a6165d60",
"name": "Extract Product Details",
"type": "n8n-nodes-base.code",
"position": [
304,
0
],
"parameters": {
"jsCode": "const html = items[0].json;\n\n// ---------- Product Name ----------\nconst nameMatch = html.match(\n /<div class=\"col-sm-6 product_main\">[\\s\\S]*?<h1>\\s*([^<]+)\\s*<\\/h1>/i\n);\n\nconst productName = nameMatch\n ? nameMatch[1].trim()\n : \"\";\n\n// ---------- Price ----------\nconst priceMatch = html.match(\n /<p class=\"price_color\">\\s*\u00a3([\\d.]+)\\s*<\\/p>/i\n);\n\nconst price = priceMatch\n ? parseFloat(priceMatch[1])\n : null;\n\n// ---------- Stock ----------\nconst stockMatch = html.match(\n /<p class=\"instock availability\">([\\s\\S]*?)<\\/p>/i\n);\n\nlet stockText = \"\";\n\nif (stockMatch) {\n stockText = stockMatch[1]\n .replace(/<[^>]+>/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nconst currentStatus = stockText.toLowerCase().includes(\"in stock\")\n ? \"In Stock\"\n : \"Out of Stock\";\n\n// ---------- Quantity ----------\nconst qtyMatch = stockText.match(/(\\d+)\\s+available/i);\n\nconst quantity = qtyMatch\n ? Number(qtyMatch[1])\n : 0;\n\n// ---------- Rating ----------\nconst ratingMatch = html.match(\n /<p class=\"star-rating\\s+(One|Two|Three|Four|Five)\"/i\n);\n\nconst ratingMap = {\n One: 1,\n Two: 2,\n Three: 3,\n Four: 4,\n Five: 5\n};\n\nconst rating = ratingMatch\n ? ratingMap[ratingMatch[1]]\n : 0;\n\n// ---------- Previous Status ----------\nconst previousStatus =\n$(\"Get Product URLs\").first().json[\"Last Known Status\"] || \"Out of Stock\";\n\n// ---------- Output ----------\nreturn [{\n json: {\n productName,\n price,\n stockText,\n quantity,\n rating,\n currentStatus,\n previousStatus,\n statusChanged: previousStatus !== currentStatus,\n lastChecked: new Date().toISOString()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "28557dd1-b706-4aee-922e-a91ae0cce7f3",
"name": "Stock Status Changed?",
"type": "n8n-nodes-base.if",
"position": [
512,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "9302dd11-b67e-4f60-b671-ac64c48b465a",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.statusChanged }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.3
},
{
"id": "c4e09fc8-ec53-4154-8289-e463ed3e614e",
"name": "Update Product Status",
"type": "n8n-nodes-base.googleSheets",
"position": [
784,
-16
],
"parameters": {
"columns": {
"value": {
"Last Price": "={{ $json.price }}",
"row_number": "={{ $('Loop Through Products').item.json.row_number }}",
"Last Checked": "={{ $json.lastChecked }}",
"Product Name": "={{ $json.productName }}",
"Last Known Status": "={{ $json.currentStatus }}"
},
"schema": [
{
"id": "Product Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Product Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Product URL",
"type": "string",
"display": true,
"required": false,
"displayName": "Product URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Last Known Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Last Known Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Last Price",
"type": "string",
"display": true,
"required": false,
"displayName": "Last Price",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Available Sizes",
"type": "string",
"display": true,
"required": false,
"displayName": "Available Sizes",
"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/1Hvk4WqCeZ8hiN_654QI2E9XO25HjKIU1Qo5kKMPMgow/edit#gid=0",
"cachedResultName": "Products"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1Hvk4WqCeZ8hiN_654QI2E9XO25HjKIU1Qo5kKMPMgow",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Hvk4WqCeZ8hiN_654QI2E9XO25HjKIU1Qo5kKMPMgow/edit?usp=drivesdk",
"cachedResultName": "Sneaker Drop Monitor"
}
},
"typeVersion": 4.7
},
{
"id": "1269d7a7-236d-4dec-be39-57bcdc5863a9",
"name": "Send Restock Alert",
"type": "n8n-nodes-base.gmail",
"position": [
1040,
144
],
"parameters": {
"sendTo": "YOUR EMAIL",
"message": "=<p>Hello,</p> <p>A monitored product has changed its availability status.</p> <table border=\"1\" cellpadding=\"8\" cellspacing=\"0\" style=\"border-collapse:collapse;\"> <tr> <td><strong>Product</strong></td> <td>{{ $('Stock Status Changed?').item.json.productName }}</td> </tr> <tr> <td><strong>Status</strong></td> <td>{{ $('Stock Status Changed?').item.json.currentStatus }}</td> </tr> <tr> <td><strong>Price</strong></td> <td>\u00a3{{ $('Stock Status Changed?').item.json.price }}</td> </tr> <tr> <td><strong>Available Quantity</strong></td> <td>{{ $('Stock Status Changed?').item.json.quantity }}</td> </tr> <tr> <td><strong>Rating</strong></td> <td>{{ $('Stock Status Changed?').item.json.rating }} / 5</td> </tr> <tr> <td><strong>Last Checked</strong></td> <td>{{ $('Stock Status Changed?').item.json.lastChecked }}</td> </tr> </table> <p> <a href=\"{{$('Get Product URLs').item.json['Product URL']}}\"> View Product </a> </p> <p>This notification was automatically generated by the Sneaker / Limited Drop Availability Monitor.</p>",
"options": {},
"subject": "=\ud83d\udea8 Restock Alert: {{ $json[\"Product Name\"] }} is Back in Stock"
},
"typeVersion": 2.2
},
{
"id": "f184f3a1-d46f-46e2-b03e-e628f413efec",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1456,
-288
],
"parameters": {
"width": 608,
"height": 688,
"content": "## Sneaker / Limited Drop Availability Monitor\n\n### How It Works\n* **Schedule & Source:** Triggers on a minute-based interval, reading target product URLs and their last known statuses from a Google Sheet.\n* **Batch Scraping:** Loops through each product item sequentially and fetches product page HTML using **ScrapeUnblocker** with a US proxy.\n* **Data Extraction:** Parses raw HTML via regex to extract product name, price, stock status, available quantity, and star rating.\n\n\n### Quick Setup Checklist\n1. **Google Sheets:** Connect credentials and verify spreadsheet ID, tab name (`Products`), and columns (`Product URL`, `Last Known Status`, `row_number`).\n2. **ScrapeUnblocker:** Connect active API credentials for proxy-enabled scraping.\n3. **Gmail:** Authenticate your Google account and replace `YOUR EMAIL` in the **Send Restock Alert** node.\n\n### Customization\n* **Interval:** Adjust the trigger frequency in `Sneaker Monitor Trigger` based on drop urgency and API limits.\n* **Regex Selectors:** Update parsing patterns in `Extract Product Details` if monitoring e-commerce sites with different HTML layouts."
},
"typeVersion": 1
},
{
"id": "6147c1e6-7eed-487d-86a7-8793573fabf5",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-752,
-160
],
"parameters": {
"color": 7,
"width": 912,
"height": 576,
"content": "## 1. Trigger & Target Products Fetch\nTriggers periodically based on the set interval, retrieves product links from Google Sheets, and initializes the processing loop."
},
"typeVersion": 1
},
{
"id": "8c0b3498-d45d-4b41-9c07-3ffd37ab7cdf",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
-176
],
"parameters": {
"color": 7,
"width": 496,
"height": 592,
"content": "## 2. Scraping & State Evaluation\nScrapes product pages via ScrapeUnblocker, extracts pricing and stock metrics using JS regex, and checks if the availability status has changed."
},
"typeVersion": 1
},
{
"id": "90256c7a-35c2-4f12-8447-8c042dd64703",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
720,
-176
],
"parameters": {
"color": 7,
"width": 544,
"height": 592,
"content": "## 3. Sheet Update & Restock Alerting\nUpdates the Google Sheet record with the latest status and timestamp, then dispatches an automated HTML alert email via Gmail when a restock is detected."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "5b870d20-878d-457e-9aa3-ef0bd03002b4",
"nodeGroups": [],
"connections": {
"Get Product URLs": {
"main": [
[
{
"node": "Loop Through Products",
"type": "main",
"index": 0
}
]
]
},
"Fetch Product Page": {
"main": [
[
{
"node": "Extract Product Details",
"type": "main",
"index": 0
}
]
]
},
"Send Restock Alert": {
"main": [
[
{
"node": "Loop Through Products",
"type": "main",
"index": 0
}
]
]
},
"Loop Through Products": {
"main": [
[],
[
{
"node": "Fetch Product Page",
"type": "main",
"index": 0
}
]
]
},
"Stock Status Changed?": {
"main": [
[
{
"node": "Update Product Status",
"type": "main",
"index": 0
}
],
[
{
"node": "Loop Through Products",
"type": "main",
"index": 0
}
]
]
},
"Update Product Status": {
"main": [
[
{
"node": "Send Restock Alert",
"type": "main",
"index": 0
}
]
]
},
"Extract Product Details": {
"main": [
[
{
"node": "Stock Status Changed?",
"type": "main",
"index": 0
}
]
]
},
"Sneaker Monitor Trigger": {
"main": [
[
{
"node": "Get Product 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 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 latest details, and send a restock alert email via Gmail. Runs on a minute-based…
Source: https://n8n.io/workflows/17524/ — 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 schedule to read competitor product URLs and your price from Google Sheets, scrapes each competitor page using ScrapeUnblocker, calculates price differences and stock status, w
YOUR_ID 4. Uses gmail, googleDrive, googleSheets, httpRequest. Scheduled trigger; 53 nodes.
special-day-email-sender. Uses googleSheets, gmail. Scheduled trigger; 43 nodes.