This workflow corresponds to n8n.io template #11609 — we link there as the canonical source.
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": "y0Yk7da21T4u9zlp",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Product Price Monitor with Mailgun and MongoDB",
"tags": [],
"nodes": [
{
"id": "00179d1d-75c0-41f8-bcbb-a753082c1d2a",
"name": "Weekly Schedule",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-1088,
192
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks"
}
]
}
},
"typeVersion": 1
},
{
"id": "6d1a639a-c5a9-41dd-ad97-beb9c733ba15",
"name": "Define Product Sources",
"type": "n8n-nodes-base.code",
"position": [
-880,
192
],
"parameters": {
"jsCode": "// \ud83d\udc47 Add / remove products or change URLs as needed\nreturn [\n {\n json: {\n productName: 'Winter Jacket A',\n url: 'https://example-store.com/winter-jacket-a'\n }\n },\n {\n json: {\n productName: 'Snow Boots B',\n url: 'https://example-store.com/snow-boots-b'\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "9acc21b1-b2b7-4606-89da-d45b2f55f7d8",
"name": "Iterate Products",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-688,
192
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "de6ef7e6-4d83-4c31-99ed-c5a50293c153",
"name": "Scrape Product Page",
"type": "n8n-nodes-scrapegraphai.scrapegraphAi",
"position": [
-480,
192
],
"parameters": {
"userPrompt": "Extract the following as JSON: {\"name\": \"string\", \"currentPrice\": \"string\", \"currency\": \"string\", \"availability\": \"string\"}. Make sure numbers include currency symbols if present.",
"websiteUrl": "={{ $json.url }}"
},
"typeVersion": 1
},
{
"id": "b63b922b-ee78-4146-8bbd-181e80edb92f",
"name": "Clean & Enrich Data",
"type": "n8n-nodes-base.code",
"position": [
-288,
192
],
"parameters": {
"jsCode": "// Normalise price, add timestamp, and fallback names\nconst items = $input.all();\nreturn items.map(item => {\n const d = item.json;\n const priceNumber = parseFloat(String(d.currentPrice || '').replace(/[^0-9.]/g, ''));\n return {\n json: {\n productName: d.productName || d.name || 'Unknown',\n url: item.json.url,\n price: priceNumber || null,\n currency: d.currency || 'USD',\n availability: d.availability || 'Unknown',\n scrapedAt: new Date().toISOString()\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "c1a311df-1dcf-4ecb-b23c-8fece9b10623",
"name": "Has Price Data?",
"type": "n8n-nodes-base.if",
"position": [
-80,
192
],
"parameters": {
"options": {},
"conditions": {
"number": [
{
"value1": "={{ $json.price }}",
"value2": 0,
"operation": "larger"
}
]
}
},
"typeVersion": 2
},
{
"id": "39f368d0-bc40-4211-b3ad-6c0990310304",
"name": "Store Price Record",
"type": "n8n-nodes-base.baserow",
"position": [
224,
624
],
"parameters": {
"tableId": "={{ $env.BASEROW_TABLE_ID || 1 }}",
"operation": "create"
},
"typeVersion": 1
},
{
"id": "834efe28-05bf-4039-a536-42b455aba1a0",
"name": "Is Price Below Threshold?",
"type": "n8n-nodes-base.if",
"position": [
416,
576
],
"parameters": {
"options": {},
"conditions": {
"number": [
{
"value1": "={{ $json.price }}",
"value2": "={{ $env.PRICE_ALERT_THRESHOLD || 50 }}",
"operation": "smaller"
}
]
}
},
"typeVersion": 2
},
{
"id": "77826ac3-1205-426e-80e1-9081aa4a65ea",
"name": "Prepare Mailchimp Content",
"type": "n8n-nodes-base.set",
"position": [
752,
176
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "fc9f07cc-34aa-4439-8813-65596391ae0e",
"name": "Log Missing Price",
"type": "n8n-nodes-base.code",
"position": [
832,
368
],
"parameters": {
"jsCode": "console.warn('No price found for', $json.productName);\nreturn items;"
},
"typeVersion": 2
},
{
"id": "a9707a76-7567-41c2-9ecd-58e7928eae4f",
"name": "Error Handler",
"type": "n8n-nodes-base.code",
"position": [
304,
-208
],
"parameters": {
"jsCode": "const err = $input.item.json;\nconsole.error('Scrape error', err);\nreturn items;"
},
"typeVersion": 2
},
{
"id": "4aa7e23e-604c-4ed0-b250-4ec5324a5cbc",
"name": "Workflow Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1680,
-208
],
"parameters": {
"width": 550,
"height": 898,
"content": "## How it works\n\nEvery week the Schedule Trigger wakes the workflow and hands a list of product URLs to ScrapeGraphAI. The AI scraper visits each ecommerce page, understands the layout and extracts the product name, current price, currency and availability. A Code node then normalises the data, adds a timestamp and converts the price to a pure number so it can be compared later. If a valid price exists the row is pushed into a Baserow table for long-term analytics and seasonal trend dashboards. The same data is instantly evaluated against a threshold: if the current price is lower than your desired floor the workflow prepares a short campaign payload and fires it to Mailchimp, letting your merchandising team know about potential restock or promotion opportunities.\n\n## Setup steps\n\n1. Add ScrapeGraphAI credentials in n8n\n2. Enter your product URLs inside the \u201cDefine Product Sources\u201d Code node\n3. Create a Baserow database and note the table ID\n4. Supply Baserow API token in credentials\n5. Connect your Mailchimp account and choose an audience list\n6. Adjust the PRICE_ALERT_THRESHOLD constant in the \u201cIs Price Below Threshold?\u201d IF node\n7. Enable the workflow and wait for the next scheduled run"
},
"typeVersion": 1
},
{
"id": "cafed690-ea6d-4135-a39c-1c5f8bee15eb",
"name": "Section \u2013 Data Collection",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1088,
-208
],
"parameters": {
"color": 7,
"width": 754,
"height": 862,
"content": "## Data Collection\n\nData collection starts with the **Weekly Schedule** node, which triggers automatically every seven days so you never miss a pricing shift. The **Define Product Sources** Code node is where you keep your curated list of URLs\u2014feel free to paste in as many product links as you like, the list can be adjusted at any time without changing any other part of the workflow. A **Split in Batches** node streams those items one at a time to avoid hammering target sites and keeps rate-limiting under control. Finally, **Scrape Product Page** uses ScrapeGraphAI to pull structured fields\u2014name, price, currency and availability\u2014without needing fragile CSS selectors. That AI extraction makes the workflow resilient when websites change their layout, giving you accurate data week after week."
},
"typeVersion": 1
},
{
"id": "a92431dd-3a11-4d87-8c44-94a1184fac74",
"name": "Section \u2013 Data Processing",
"type": "n8n-nodes-base.stickyNote",
"position": [
-320,
-208
],
"parameters": {
"color": 7,
"width": 450,
"height": 846,
"content": "## Data Processing\n\nAfter scraping, each item passes through **Clean & Enrich Data** where raw text is turned into analytics-ready values. Prices often include currency symbols or thousand separators, so the JavaScript here strips everything to a decimal number, adds a fallback currency, and stamps the exact scrape time. The subsequent **Has Price Data?** IF node is your quality gate: only records with a valid numeric price continue downstream. Splitting good and bad data early prevents garbage from polluting your dataset. Invalid items fall to a separate logger so you keep visibility on problem pages without breaking the workflow. This structured, validated payload is now ready for storage or alerting."
},
"typeVersion": 1
},
{
"id": "b25f321f-f0b9-4e03-8aba-c7a0b79630ad",
"name": "Section \u2013 Data Storage",
"type": "n8n-nodes-base.stickyNote",
"position": [
144,
224
],
"parameters": {
"color": 7,
"width": 450,
"height": 622,
"content": "## Data Storage\n\nThe **Store Price Record** node writes every clean observation into Baserow so you build a historical dataset automatically. Because Baserow offers instant spreadsheets backed by a relational database, you can slice, chart and export your price history without extra tooling. Fields are mapped explicitly\u2014product name, numeric price, currency, current availability, the page URL and a timestamp\u2014so downstream analysts always know what each cell means. If you prefer your own field IDs, simply swap them in the node\u2019s mapping. Once data lands here you can power dashboards, feed BI tools or export CSVs for seasonal trend modelling."
},
"typeVersion": 1
},
{
"id": "82981fc7-5858-47f8-a60c-485e024de5c3",
"name": "Section \u2013 Alerting",
"type": "n8n-nodes-base.stickyNote",
"position": [
704,
-144
],
"parameters": {
"color": 7,
"width": 450,
"height": 686,
"content": "## Alerting\n\nPrice intelligence is only useful when people hear about it quickly. The **Is Price Below Threshold?** node compares the freshly scraped price against a configurable constant (set via environment variable or directly in the node). When a bargain surfaces, **Prepare Mailchimp Content** crafts a short yet informative message that includes the product name, new price and a direct link. The final **Send Price Alert** node pushes that content to Mailchimp using your existing list so buyers or planners receive an inbox notification within seconds. By leveraging Mailchimp rather than a simple email node you gain open-rate tracking, click analytics and unsubscribe management out of the box."
},
"typeVersion": 1
},
{
"id": "0de8a479-546b-4347-ac1b-d65e20395e37",
"name": "Section \u2013 Error Handling",
"type": "n8n-nodes-base.stickyNote",
"position": [
128,
-480
],
"parameters": {
"color": 7,
"width": 450,
"height": 430,
"content": "## Error Handling\n\nScraping can fail for many reasons\u2014CAPTCHAs, site redesigns, or a brief connection hiccup. Rather than stopping the entire run, errors from **Scrape Product Page** route to **Error Handler** where they\u2019re logged to the n8n console. Meanwhile, missing data detected in **Has Price Data?** is captured by **Log Missing Price** so you still know which URLs need attention. Having two distinct pathways\u2014one for technical scraping errors and another for business-logic gaps\u2014keeps operations transparent and prevents silent data loss. Update these Code nodes to push errors into a monitoring stack like Sentry or Slack if you prefer real-time alerts."
},
"typeVersion": 1
},
{
"id": "9de47146-f482-461b-a18c-05983288744f",
"name": "Send a message",
"type": "n8n-nodes-base.slack",
"position": [
928,
160
],
"parameters": {
"otherOptions": {}
},
"typeVersion": 2.3
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "8e287d5c-1946-445d-a2c5-20b43f10b2b1",
"connections": {
"Has Price Data?": {
"main": [
[
{
"node": "Store Price Record",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Missing Price",
"type": "main",
"index": 0
}
]
]
},
"Weekly Schedule": {
"main": [
[
{
"node": "Define Product Sources",
"type": "main",
"index": 0
}
]
]
},
"Iterate Products": {
"main": [
[
{
"node": "Scrape Product Page",
"type": "main",
"index": 0
}
]
]
},
"Store Price Record": {
"main": [
[
{
"node": "Is Price Below Threshold?",
"type": "main",
"index": 0
}
]
]
},
"Clean & Enrich Data": {
"main": [
[
{
"node": "Has Price Data?",
"type": "main",
"index": 0
}
]
]
},
"Scrape Product Page": {
"main": [
[
{
"node": "Clean & Enrich Data",
"type": "main",
"index": 0
},
{
"node": "Error Handler",
"type": "main",
"index": 0
}
]
]
},
"Define Product Sources": {
"main": [
[
{
"node": "Iterate Products",
"type": "main",
"index": 0
}
]
]
},
"Is Price Below Threshold?": {
"main": [
[
{
"node": "Prepare Mailchimp Content",
"type": "main",
"index": 0
}
]
]
},
"Prepare Mailchimp Content": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
⚠️ COMMUNITY TEMPLATE DISCLAIMER: This is a community-contributed template that uses ScrapeGraphAI (a community node). Please ensure you have the ScrapeGraphAI community node installed in your n8n instance before using this template.
Source: https://n8n.io/workflows/11609/ — 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 contains community nodes that are only compatible with the self-hosted version of n8n.
Sales managers and team leads Business development representatives Marketing teams managing lead generation CRM administrators and sales operations Account executives and sales representatives Sales e
ASO teams tracking daily keyword positions Growth & marketing standups that want quick rank visibility Lightweight historical logging without a full BI stack Runs on a schedule (e.g., weekly) Queries
⚠️ COMMUNITY TEMPLATE DISCLAIMER: This is a community-contributed template that uses ScrapeGraphAI (a community node). Please ensure you have the ScrapeGraphAI community node installed in your n8n ins
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.