{
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Amazon Price-Drop Alerts with Scavio + Google Sheets",
  "nodes": [
    {
      "id": "sticky-master",
      "name": "How it works",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        180,
        160
      ],
      "parameters": {
        "width": 580,
        "height": 680,
        "content": "## Amazon Price-Drop Alerts with Scavio + Google Sheets\n\n### How it works\nThis workflow tracks any number of Amazon products from a Google Sheet and sends a Gmail alert the moment the price hits or drops below your target. It is **edge-triggered** \u2014 one email per drop, no spam at flat prices \u2014 by writing the alerted price back to the sheet after every notification and only re-alerting if the price drops further.\n\nPowered by [Scavio](https://scavio.dev), a real-time search API for Google, Amazon, Walmart, YouTube, and Reddit. Free tier: 500 credits per month.\n\n### Setup steps\n- [ ] Install the **Scavio** community node: Settings -> Community Nodes -> install `n8n-nodes-scavio`.\n- [ ] Get a free Scavio API key at https://dashboard.scavio.dev (500 credits/mo, 1 credit per Amazon product call).\n- [ ] Create a Google Sheet with one tab; add three columns: `product_url`, `price_threshold`, `last_alerted_price` (leave blank). Add one row per product.\n- [ ] Attach credentials: Scavio API on **Live price**, Google Sheets OAuth2 on **Read watchlist** + **Save state**, Gmail OAuth2 on **Email**.\n- [ ] Open **Read watchlist** and **Save state** and pick your spreadsheet/tab from the dropdown.\n- [ ] Open **Email** and set `sendTo` to your alert recipient.\n- [ ] Activate the workflow.\n\n### Tips\n- To re-arm an alert (you checked but didn't buy), clear the `last_alerted_price` cell.\n- To stop tracking, delete the row.\n- Drop the schedule from 6h to daily to fit ~16 products on the free tier."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-feedback",
      "name": "Feedback",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        180,
        880
      ],
      "parameters": {
        "color": 4,
        "width": 580,
        "height": 100,
        "content": "### How can we improve this workflow?\n### [>>> Share your feedback at scavio.dev/feedback](https://scavio.dev)"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-cluster-a",
      "name": "Cluster: Read watchlist",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        840,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 280,
        "content": "## 1. Read watchlist\n\nFires every 6 hours and reads every row from the Google Sheet (one row = one product)."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-cluster-b",
      "name": "Cluster: Get current price",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1400,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 940,
        "height": 280,
        "content": "## 2. Get current price\n\nFor each product, throttles to 2s between requests, extracts the ASIN from any Amazon URL format, and calls Scavio for the live price."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-cluster-c",
      "name": "Cluster: Decide and notify",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2380,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 740,
        "height": 500,
        "content": "## 3. Decide and notify\n\nEdge-triggered: alerts only when the price drops below threshold for the first time, or drops further than the previously alerted level. Sends a Gmail alert and writes `last_alerted_price` back to the sheet so the next run knows."
      },
      "typeVersion": 1
    },
    {
      "id": "1",
      "name": "Every 6 Hours",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        900,
        320
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2",
      "name": "Read watchlist",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1120,
        320
      ],
      "parameters": {
        "options": {},
        "operation": "read",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Watchlist"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "3",
      "name": "Loop products",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1460,
        320
      ],
      "parameters": {
        "options": {},
        "batchSize": 1
      },
      "typeVersion": 3
    },
    {
      "id": "4",
      "name": "Throttle",
      "type": "n8n-nodes-base.wait",
      "position": [
        1680,
        320
      ],
      "parameters": {
        "unit": "seconds",
        "amount": 2
      },
      "typeVersion": 1.1
    },
    {
      "id": "5",
      "name": "Parse URL",
      "type": "n8n-nodes-base.code",
      "position": [
        1900,
        320
      ],
      "parameters": {
        "jsCode": "const url = String($json.product_url ?? '').trim();\nconst targetPrice = Number($json.price_threshold ?? 0);\n\nconst asin =\n  url.match(/\\/dp\\/([A-Z0-9]{10})/)?.[1] ??\n  url.match(/\\/gp\\/product\\/([A-Z0-9]{10})/)?.[1] ??\n  url.match(/\\/gp\\/aw\\/d\\/([A-Z0-9]{10})/)?.[1] ??\n  url.match(/\\/product\\/([A-Z0-9]{10})/)?.[1] ??\n  url.match(/\\b([B][0-9A-Z]{9})\\b/)?.[1] ??\n  null;\nconst domain = url.match(/amazon\\.([a-z.]+?)\\//)?.[1] ?? 'com';\n\nif (!asin) {\n  throw new Error(`Could not extract ASIN from URL: \"${url}\". Use a standard Amazon product URL containing /dp/ASIN.`);\n}\n\nreturn [{ json: { asin, domain, productUrl: url, targetPrice } }];",
        "language": "javaScript"
      },
      "typeVersion": 2
    },
    {
      "id": "6",
      "name": "Live price",
      "type": "n8n-nodes-scavio.scavio",
      "position": [
        2100,
        320
      ],
      "parameters": {
        "query": "={{ $json.asin }}",
        "domain": "={{ $json.domain }}",
        "resource": "amazon",
        "operation": "product"
      },
      "credentials": {
        "scavioApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "continueOnFail": true
    },
    {
      "id": "7",
      "name": "Decide alert",
      "type": "n8n-nodes-base.code",
      "position": [
        2440,
        320
      ],
      "parameters": {
        "jsCode": "const product = $json.data ?? $json;\nconst row = $('Read watchlist').item.json;\nconst parsed = $('Parse URL').item.json;\n\nconst currentPrice = Number(product.price?.value ?? product.price ?? product.priceValue ?? 0);\nconst targetPrice = Number(parsed.targetPrice);\n\nconst lastAlertedRaw = String(row.last_alerted_price ?? '').trim();\nconst lastAlertedPrice = lastAlertedRaw === '' ? null : Number(lastAlertedRaw);\n\nconst shouldAlert =\n  currentPrice > 0 &&\n  targetPrice > 0 &&\n  currentPrice <= targetPrice &&\n  (lastAlertedPrice === null || currentPrice < lastAlertedPrice);\n\nreturn [{\n  json: {\n    productUrl: parsed.productUrl,\n    asin: parsed.asin,\n    title: product.title ?? null,\n    currentPrice,\n    targetPrice,\n    lastAlertedPrice,\n    shouldAlert,\n  },\n}];",
        "language": "javaScript"
      },
      "typeVersion": 2
    },
    {
      "id": "8",
      "name": "Should alert?",
      "type": "n8n-nodes-base.if",
      "position": [
        2660,
        320
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c1",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.shouldAlert }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "9",
      "name": "Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2880,
        260
      ],
      "parameters": {
        "sendTo": "you@example.com",
        "message": "={{ $json.title ?? $json.productUrl }} is now ${{ $json.currentPrice }} (target ${{ $json.targetPrice }}{{ $json.lastAlertedPrice ? ', previous alert ' + $json.lastAlertedPrice : '' }}).\n\n{{ $json.productUrl }}\n\n\u2014 Scavio price tracker",
        "options": {},
        "subject": "=Price hit target: {{ $json.title ?? $json.productUrl }}",
        "emailType": "text",
        "operation": "send"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "10",
      "name": "Save state",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2880,
        480
      ],
      "parameters": {
        "columns": {
          "value": {
            "product_url": "={{ $('Decide alert').item.json.productUrl }}",
            "last_alerted_price": "={{ $('Decide alert').item.json.currentPrice }}"
          },
          "schema": [
            {
              "id": "product_url",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "product_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "price_threshold",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "price_threshold",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_alerted_price",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "last_alerted_price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "product_url"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Watchlist"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Email": {
      "main": [
        [
          {
            "node": "Save state",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Throttle": {
      "main": [
        [
          {
            "node": "Parse URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse URL": {
      "main": [
        [
          {
            "node": "Live price",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Live price": {
      "main": [
        [
          {
            "node": "Decide alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save state": {
      "main": [
        [
          {
            "node": "Loop products",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decide alert": {
      "main": [
        [
          {
            "node": "Should alert?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 6 Hours": {
      "main": [
        [
          {
            "node": "Read watchlist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop products": {
      "main": [
        [],
        [
          {
            "node": "Throttle",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Should alert?": {
      "main": [
        [
          {
            "node": "Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop products",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read watchlist": {
      "main": [
        [
          {
            "node": "Loop products",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}