AINode.jsAutomation

Product Price Monitor with AI Alerts

TT
TopicTrick Team
Product Price Monitor with AI Alerts

Product Price Monitor with AI Alerts

Never miss a price drop again. This tool monitors product prices on any e-commerce site, stores price history, and sends intelligent alerts when prices drop — with GPT-4o analysing whether it is actually a good deal.

This is Tool 9 of the Build 50 AI Automation Tools course.


What You'll Build

  • Monitor multiple products across different sites
  • Store price history in SQLite
  • Detect price drops and send email/webhook alerts
  • AI analysis of whether a price is genuinely good value

Setup

bash
mkdir price-monitor && cd price-monitor
npm init -y
npm install express puppeteer openai better-sqlite3 node-cron nodemailer dotenv
bash
# .env
OPENAI_API_KEY=sk-your-key-here
SMTP_HOST=smtp.gmail.com
SMTP_USER=you@gmail.com
SMTP_PASS=your-app-password
ALERT_EMAIL=alerts@yourdomain.com
PORT=3000

Database Setup

js
// src/db.js
import Database from 'better-sqlite3';

const db = new Database('prices.db');

db.exec(`
  CREATE TABLE IF NOT EXISTS products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    url TEXT UNIQUE NOT NULL,
    target_price REAL,
    alert_threshold REAL DEFAULT 0.05,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  );

  CREATE TABLE IF NOT EXISTS price_history (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    product_id INTEGER REFERENCES products(id),
    price REAL NOT NULL,
    currency TEXT DEFAULT 'USD',
    in_stock INTEGER DEFAULT 1,
    checked_at DATETIME DEFAULT CURRENT_TIMESTAMP
  );
`);

export const queries = {
  addProduct: db.prepare('INSERT OR IGNORE INTO products (name, url, target_price, alert_threshold) VALUES (?, ?, ?, ?)'),
  getProducts: db.prepare('SELECT * FROM products'),
  logPrice: db.prepare('INSERT INTO price_history (product_id, price, currency, in_stock) VALUES (?, ?, ?, ?)'),
  getLatestPrice: db.prepare('SELECT * FROM price_history WHERE product_id = ? ORDER BY checked_at DESC LIMIT 1'),
  getPriceHistory: db.prepare('SELECT * FROM price_history WHERE product_id = ? ORDER BY checked_at DESC LIMIT 30'),
};

export default db;

Price Extraction Service

js
// src/services/priceService.js
import puppeteer from 'puppeteer';
import OpenAI from 'openai';
import { queries } from '../db.js';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

async function extractPrice(url) {
  const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
  try {
    const page = await browser.newPage();
    await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36');
    await page.goto(url, { waitUntil: 'networkidle2', timeout: 30_000 });

    const text = await page.evaluate(() => {
      document.querySelectorAll('script,style,nav,footer,header').forEach(el => el.remove());
      return document.body?.innerText?.slice(0, 5000) || '';
    });

    const response = await openai.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [
        {
          role: 'system',
          content: `Extract price information from this product page text.
Return JSON: {"price": number, "currency": "USD", "inStock": true/false, "productName": "string"}
Return null for price if not found.`,
        },
        { role: 'user', content: text },
      ],
      response_format: { type: 'json_object' },
      temperature: 0,
    });

    return JSON.parse(response.choices[0].message.content);
  } finally {
    await browser.close();
  }
}

async function analyzePrice(product, currentPrice, history) {
  if (history.length < 2) return null;

  const prices = history.map(h => h.price);
  const avg = prices.reduce((a, b) => a + b, 0) / prices.length;
  const min = Math.min(...prices);

  const response = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [
      {
        role: 'system',
        content: `Analyze this price data and give a buying recommendation.
Return JSON: {"recommendation": "buy_now | wait | good_deal", "reasoning": "string", "dealScore": 1-10}`,
      },
      {
        role: 'user',
        content: `Product: ${product.name}
Current price: $${currentPrice}
Average price (30 days): $${avg.toFixed(2)}
All-time low: $${min}
Target price: $${product.target_price || 'not set'}
Price history: ${JSON.stringify(prices)}`,
      },
    ],
    response_format: { type: 'json_object' },
    temperature: 0.3,
  });

  return JSON.parse(response.choices[0].message.content);
}

export async function checkProduct(product) {
  const priceData = await extractPrice(product.url);
  if (!priceData?.price) return { success: false, error: 'Price not found' };

  const lastPrice = queries.getLatestPrice.get(product.id);
  queries.logPrice.run(product.id, priceData.price, priceData.currency || 'USD', priceData.inStock ? 1 : 0);

  const history = queries.getPriceHistory.all(product.id);
  const analysis = await analyzePrice(product, priceData.price, history);

  const priceDrop = lastPrice ? (lastPrice.price - priceData.price) / lastPrice.price : 0;
  const isAlert = priceDrop >= (product.alert_threshold || 0.05) ||
    (product.target_price && priceData.price <= product.target_price);

  return {
    product: product.name,
    currentPrice: priceData.price,
    lastPrice: lastPrice?.price,
    priceChange: priceDrop,
    inStock: priceData.inStock,
    analysis,
    shouldAlert: isAlert,
  };
}

export async function checkAllProducts() {
  const products = queries.getProducts.all();
  const results = [];

  for (const product of products) {
    try {
      const result = await checkProduct(product);
      results.push({ success: true, ...result });
    } catch (err) {
      results.push({ success: false, product: product.name, error: err.message });
    }
    await new Promise(r => setTimeout(r, 2000 + Math.random() * 2000));
  }

  return results;
}

Server + Scheduler

js
// src/server.js
import 'dotenv/config';
import express from 'express';
import cron from 'node-cron';
import { checkAllProducts, checkProduct } from './services/priceService.js';
import { queries } from './db.js';

const app = express();
app.use(express.json());

app.post('/products', (req, res) => {
  const { name, url, targetPrice, alertThreshold } = req.body;
  if (!name || !url) return res.status(400).json({ error: 'name and url required' });
  queries.addProduct.run(name, url, targetPrice || null, alertThreshold || 0.05);
  res.json({ success: true, message: `Monitoring ${name}` });
});

app.get('/products', (req, res) => {
  const products = queries.getProducts.all();
  res.json({ products });
});

app.post('/check', async (req, res, next) => {
  try {
    const results = await checkAllProducts();
    res.json({ success: true, results });
  } catch (err) { next(err); }
});

// Check every 6 hours
cron.schedule('0 */6 * * *', async () => {
  console.log('Checking prices...');
  const results = await checkAllProducts();
  const alerts = results.filter(r => r.shouldAlert);
  if (alerts.length > 0) {
    console.log(`ALERT: ${alerts.length} price drops detected`);
    // Send email alerts here
  }
});

app.use((err, _req, res, _next) => res.status(500).json({ error: err.message }));
app.listen(process.env.PORT ?? 3000, () => console.log('Price Monitor running'));

Testing

bash
# Add a product to monitor
curl -X POST http://localhost:3000/products \
  -H "Content-Type: application/json" \
  -d '{"name": "Sony WH-1000XM5", "url": "https://example-store.com/product/sony-wh1000xm5", "targetPrice": 280}'

# Trigger an immediate check
curl -X POST http://localhost:3000/check

Sample alert response:

json
{
  "product": "Sony WH-1000XM5",
  "currentPrice": 249.99,
  "lastPrice": 349.99,
  "priceChange": 0.286,
  "inStock": true,
  "analysis": {
    "recommendation": "buy_now",
    "reasoning": "Price dropped 28.6% — lowest in 30 days and below your target price of $280",
    "dealScore": 9
  },
  "shouldAlert": true
}

Build 50 AI Automation Tools — Tool 9 of 50

Price monitoring is live. Continue to Tool 10 to build an AI SEO content analyzer.


    Summary

    • SQLite with better-sqlite3 stores price history with zero setup — perfect for local monitoring tools
    • AI price extraction works across any e-commerce site without site-specific selectors
    • AI deal analysis scores whether the current price is genuinely worth buying
    • node-cron runs checks every 6 hours automatically in the background
    • Extend with Nodemailer for email alerts or Twilio for SMS when price targets are hit

    Continue to Tool 10: SEO Content Analyzer →