How to Monitor Website Changes Automatically
You need to know when a website changes. Your competitor ships a feature. Your site breaks. A third-party integration goes down.
Right now, you're checking manually. Or you've built a Puppeteer scraper that's 400 lines of error handling.
There's a simpler way: screenshot the page on a cron, compare with ImageMagick.
The Problem: Manual Change Detection
Typical change monitoring workflow:
- Set up a cron job
- Launch Puppeteer browser
- Take a screenshot
- Compare pixels with previous screenshot
- Send alert on difference
- Manage browser lifecycle
Issues:
- 300MB+ Puppeteer dependency
- Browser launch overhead per check
- Image comparison is complex (pixel drift, anti-aliasing)
- Fails in serverless / small VPS
- Maintenance burden on cron infrastructure
The Solution: PageBolt + ImageMagick
One API call per screenshot. Zero browser management. Compare with standard CLI tools.
# Install ImageMagick
apt-get install imagemagick
# Create monitoring directory
mkdir -p screenshots comparisons
# Set your API key
export PAGEBOLT_API_KEY=your_api_key
Node.js Website Monitoring
Monitor a single website for changes:
const fs = require('fs');
const { spawn } = require('child_process');
const apiKey = process.env.PAGEBOLT_API_KEY;
async function captureAndCompare(url, siteName) {
const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
const filename = `screenshots/${siteName}-${timestamp}.png`;
const previousDir = `screenshots`;
// Capture current state
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: url,
format: 'png',
width: 1280,
height: 720
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const buffer = await response.arrayBuffer();
fs.writeFileSync(filename, Buffer.from(buffer));
console.log(`✓ Captured: ${filename}`);
// Find previous screenshot
const files = fs.readdirSync(previousDir)
.filter(f => f.startsWith(siteName))
.sort()
.reverse();
if (files.length < 2) {
console.log('First capture. No comparison baseline yet.');
return null;
}
const previousFile = `${previousDir}/${files[1]}`;
// Compare with ImageMagick
return new Promise((resolve) => {
const compare = spawn('compare', [
'-metric', 'mae',
previousFile,
filename,
'null:'
]);
let diffPercentage = '';
compare.stderr.on('data', (data) => {
diffPercentage += data.toString();
});
compare.on('close', () => {
// Parse MAE (Mean Absolute Error) from ImageMagick output
const mae = parseFloat(diffPercentage);
const threshold = 1000; // Adjust based on sensitivity
if (mae > threshold) {
console.log(`🚨 CHANGE DETECTED: ${mae.toFixed(0)} MAE difference`);
console.log(` Previous: ${previousFile}`);
console.log(` Current: ${filename}`);
resolve({ changed: true, mae, previousFile, currentFile: filename });
} else {
console.log(`✓ No significant change (MAE: ${mae.toFixed(0)})`);
resolve({ changed: false, mae });
}
});
});
}
// Usage with cron
captureAndCompare('https://example.com', 'example-com')
.then(result => {
if (result?.changed) {
// Send alert (email, Slack, webhook)
console.log('Alert triggered. Send notification to team.');
}
})
.catch(err => console.error('Monitor error:', err.message));
Cron Setup
Monitor multiple sites every hour:
#!/bin/bash
# /usr/local/bin/monitor-sites.sh
export PAGEBOLT_API_KEY=your_api_key
export NODE_ENV=production
cd /home/monitor-app
# Monitor multiple sites
node monitor.js --url https://example.com --name example-com
node monitor.js --url https://github.com --name github-com
node monitor.js --url https://news.ycombinator.com --name hacker-news
Crontab entry (run every hour):
0 * * * * /usr/local/bin/monitor-sites.sh >> /var/log/monitor.log 2>&1
Advanced: Batch Monitoring
Monitor 10 sites in parallel:
const sites = [
{ url: 'https://example.com', name: 'example-com' },
{ url: 'https://github.com', name: 'github' },
{ url: 'https://stackoverflow.com', name: 'stackoverflow' },
// ... more sites
];
async function monitorAll() {
const results = await Promise.all(
sites.map(site => captureAndCompare(site.url, site.name))
);
const changed = results.filter(r => r?.changed);
if (changed.length > 0) {
console.log(`${changed.length} sites changed today`);
// Send summary alert
}
}
monitorAll().catch(console.error);
Real-World Use Cases
Competitor Tracking — Know when competitors ship features:
const competitors = [
'https://stripe.com',
'https://segment.com',
'https://amplitude.com'
];
competitors.forEach(url => {
captureAndCompare(url, url.replace(/\//g, '-'))
.then(result => {
if (result?.changed) {
// Log competitor changes to database
db.insert('competitor_changes', {
competitor: url,
timestamp: new Date(),
previous: result.previousFile,
current: result.currentFile
});
}
});
});
Uptime & Health Monitoring — Alert on visual errors:
async function healthCheck(url, siteName) {
const result = await captureAndCompare(url, siteName);
if (result?.mae > 5000) {
// Likely a major change (error page, maintenance, etc)
sendSlackAlert(`⚠️ ${siteName} may be down. MAE: ${result.mae}`);
}
}
// Check every 15 minutes
setInterval(() => healthCheck('https://myapp.com', 'myapp'), 15 * 60 * 1000);
Design Regression Testing — Catch unintended style changes:
async function regressionTest(url, branch) {
const screenshot = await capture(url);
// Compare with golden baseline stored in git
const baseline = fs.readFileSync(`baselines/${branch}.png`);
const diff = await compareImages(baseline, screenshot);
if (diff.pixels > 1000) {
console.log('❌ Design regression detected');
process.exit(1); // Fail CI
}
}
Pricing
| Plan | Requests/Month | Cost | Best For |
|---|---|---|---|
| Free | 100 | $0 | Testing & small sites |
| Starter | 5,000 | $29 | 5-10 sites monitored hourly |
| Growth | 25,000 | $79 | 20+ sites or high frequency |
| Scale | 100,000 | $199 | Enterprise monitoring networks |
Image Comparison Tips
Sensitivity tuning:
- MAE > 10,000 = major visual change
- MAE 2,000–5,000 = moderate change
- MAE < 500 = noise (anti-aliasing, sub-pixel rendering)
Reduce false positives:
# Apply threshold before compare to ignore minor changes
convert previous.png -threshold 50% previous-threshold.png
convert current.png -threshold 50% current-threshold.png
compare -metric mae previous-threshold.png current-threshold.png null:
Store diffs for review:
compare previous.png current.png difference.png
# Creates visual diff showing what changed
Summary
Website change monitoring:
- ✅ Screenshot API (no browser management)
- ✅ ImageMagick for comparison (installed on any Linux)
- ✅ Cron jobs for scheduling (built-in on servers)
- ✅ Timestamped history (easy debugging)
- ✅ Scalable to 100+ sites
Get started: Try PageBolt free — 100 requests/month, no credit card required →
Top comments (0)