Automate Website Monitoring with Screenshots
Updated March 2026 -- Build a complete visual monitoring system that detects changes, downtime, and defacements automatically.
Why Visual Monitoring?
Traditional uptime monitoring checks if a server responds with HTTP 200. But your site can return 200 while showing a broken layout, missing images, or even a defacement. Visual monitoring catches what status codes miss.
By taking periodic screenshots and comparing them, you can detect:
Layout breakage from CSS or JavaScript errors
Missing images or broken media
Unauthorized content changes or defacements
Broken deployments that pass health checks
Third-party widget failures (ads, chat widgets, analytics)
Architecture Overview
Cron Job (every 15 min)
|
v
Screenshot API --> Save to S3/R2
|
v
Compare with previous screenshot (pixel diff)
|
v
If diff > threshold --> Send alert (Slack/Email/PagerDuty)Step 1: Take Periodic Screenshots
Set up a cron job that captures your website every 15 minutes.
const SCREENSHOT_API = 'https://screenshotapi-api-production.up.railway.app';
const API_KEY = process.env.SCREENSHOT_API_KEY;
async function captureScreenshot(url) {
const params = new URLSearchParams({
url,
width: '1280',
height: '800',
format: 'png',
fullpage: 'false',
});
const response = await fetch(
`${SCREENSHOT_API}/v1/screenshot?${params}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
if (!response.ok) throw new Error(`Screenshot failed: ${response.status}`);
return Buffer.from(await response.arrayBuffer());
}
// Run every 15 minutes via cron
async function monitor() {
const sites = [
'https://your-app.com',
'https://your-app.com/pricing',
'https://your-app.com/docs',
];
for (const url of sites) {
const screenshot = await captureScreenshot(url);
await saveToStorage(url, screenshot);
await compareWithPrevious(url, screenshot);
}
}Step 2: Compare Screenshots
Use a pixel comparison library like pixelmatch to detect visual changes.
const pixelmatch = require('pixelmatch');
const { PNG } = require('pngjs');
async function compareWithPrevious(url, currentBuffer) {
const previousBuffer = await getPreviousScreenshot(url);
if (!previousBuffer) return; // first run
const current = PNG.sync.read(currentBuffer);
const previous = PNG.sync.read(previousBuffer);
const { width, height } = current;
const diff = new PNG({ width, height });
const mismatchedPixels = pixelmatch(
previous.data, current.data, diff.data,
width, height, { threshold: 0.1 }
);
const totalPixels = width * height;
const diffPercentage = (mismatchedPixels / totalPixels) * 100;
if (diffPercentage > 5) { // 5% threshold
await sendAlert(url, diffPercentage, diff);
}
}Step 3: Send Alerts
async function sendAlert(url, diffPercentage, diffImage) {
// Slack webhook
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `Visual change detected on ${url}\n` +
`Change: ${diffPercentage.toFixed(1)}% of pixels differ\n` +
`Time: ${new Date().toISOString()}`,
}),
});
}Advanced: Multi-Device Monitoring
Monitor how your site looks on different devices by using our device presets endpoint.
const devices = [
{ name: 'desktop', width: 1280, height: 800 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'mobile', width: 375, height: 812 },
];
for (const device of devices) {
const params = new URLSearchParams({
url: 'https://your-app.com',
width: device.width.toString(),
height: device.height.toString(),
format: 'png',
});
// ... capture and compare for each device
}Cost Analysis
Monitoring 5 pages every 15 minutes = 5 x 96 daily captures = 480 screenshots/day = ~14,400/month.
With our Pro plan ($29/month for 10,000 screenshots), you can monitor up to 3 pages every 15 minutes. The Business plan ($99/month for 100,000) covers even the most comprehensive monitoring setup.
Compare this to dedicated visual monitoring services like Percy ($399/mo) or Chromatic ($149/mo) -- building your own with a Screenshot API is significantly more cost-effective.
Best Practices
1.Set your diff threshold carefully -- 1-2% catches typos, 5-10% catches layout changes while ignoring dynamic content
2.Exclude areas with dynamic content (timestamps, ads) by cropping before comparing
3.Store screenshot history for at least 30 days for debugging
4.Use our fullpage=false parameter for consistent viewport captures
5.Add a wait parameter (wait=2000) to ensure all content loads before capture
Start Monitoring in 5 Minutes
Get your free API key and start capturing screenshots. 100 free screenshots/month.