Watchmint.

Watch a web page for changes with an API and get a webhook

Updated

You want to know when a page changes: a competitor's pricing, a vendor's terms, an API's changelog. Re-reading it yourself on a timer means storing the old copy, diffing it, and filtering out the parts that change on every load. Watchmint does all three and sends you a webhook with what changed.

1. Get an API key and a webhook

Create a free account, then make a key under API keys in the dashboard. Under Webhooks, add the HTTPS endpoint that should hear about changes and keep its signing secret (shown once). Its id is in the list of webhooks.

The free plan includes 300 checks a month and checks a page at most hourly.

2. Start watching the page

curl -X POST https://api-watchmint.plxi.us/v1/monitors/create \
  -H "X-API-Key: $WATCHMINT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://competitor.example/pricing",
    "name": "Competitor pricing",
    "every_minutes": 60,
    "selector": "#pricing",
    "ignore_patterns": ["Updated \d+ (minutes?|hours?) ago"],
    "webhook_id": "YOUR_WEBHOOK_ID"
  }'

To watch how the page looks instead of what it says, send "watch": "visual" (or "both"). A visual check takes a screenshot and reports a change when at least threshold percent of it differs, 1% by default.

3. Handle the webhook

When a check finds a change, your endpoint gets a monitor.changed POST. Check the signature against the raw body before you parse it:

import crypto from 'node:crypto';
import express from 'express';

const app = express();
app.post('/webhooks/watchmint', express.raw({ type: 'application/json' }), (req, res) => {
  const want = 'sha256=' + crypto.createHmac('sha256', process.env.WATCHMINT_WEBHOOK_SECRET).update(req.body).digest('hex');
  const got = String(req.get('X-Watchmint-Signature') || '');
  if (got.length !== want.length || !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want))) return res.sendStatus(401);
  const { event, data } = JSON.parse(req.body);
  if (event === 'monitor.changed') {
    console.log(`${data.monitor.name}: ${data.change.summary}`);
    for (const line of data.change.added) console.log('+', line);
    for (const line of data.change.removed) console.log('-', line);
  }
  res.sendStatus(200);
});
app.listen(3000);

data.change has the added and removed lines (the first 50 of each), a unified diff, and for visual checks visual_changed_pct and links to the before, after and highlighted images. If a page fails three checks in a row (it's down, answers with an error, or shows a bot check) you get monitor.error; failed checks cost nothing.

4. Cut the noise further

5. Read changes later

You don't need a webhook. POST /v1/monitors/changes returns the newest changes, for one monitor or all of them, and is free:

curl -X POST https://api-watchmint.plxi.us/v1/monitors/changes \
  -H "X-API-Key: $WATCHMINT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"since": "2026-10-30T00:00:00Z", "limit": 10}'

Changes are kept for 90 days. To pause a monitor, call POST /v1/monitors/update with "paused": true; to stop for good, POST /v1/monitors/delete.