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"
}'
selectornarrows the check to one part of the page. Leave it out to watch all of the page's visible text.ignore_patternsare regular expressions matched against each line. Whatever they match is taken out of both versions before they're compared, so a timestamp or a visitor counter never counts as a change.- The first check runs at once and becomes the baseline. Creating the monitor is free; each check costs one credit.
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
- Confirm changes.
"confirm": truereports a change only when two checks in a row see it, which quiets rotating banners and A/B tests. - Leave parts out.
ignore_selectorsdrops elements such as ad slots or a live chat widget before the text is read. - Pages built by script. If a page has no text until its JavaScript runs,
Watchmint reads it in a browser.
"render": truealways does.
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.