Websites change constantly — your own and your competitors'. Prices get updated, layouts get redesigned, CTAs move, features launch quietly. Detecting these changes by checking manually doesn't scale.
Visual change detection solves this by taking periodic screenshots, comparing them to a baseline, and alerting you when the pixel diff crosses a threshold. This guide covers how to implement it via API — no infrastructure required.
How visual change detection works
The core loop:
- Take a baseline screenshot of a page
- On a schedule (every 5 minutes, hourly, daily), take a new screenshot
- Run a pixel-level comparison between the new screenshot and the baseline
- If the diff percentage exceeds your threshold, fire an alert
- Include a diff overlay image in the alert showing exactly what changed
This approach catches:
- Layout regressions from bad deploys
- Competitor pricing changes
- Content updates (new blog posts, changed CTAs, removed features)
- A/B test side effects on pages you didn't expect to change
- Third-party widget failures (chat widgets, banners, cookie notices)
Quick start with SnapSharp Monitors
The /v1/monitors API handles the scheduling, comparison, and alerting for you:
curl -X POST https://api.snapsharp.dev/v1/monitors \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/pricing",
"interval_minutes": 30,
"diff_threshold": 2.0,
"notify_email": "[email protected]",
"notify_webhook": "https://yourapi.com/alerts/visual"
}'Parameters:
interval_minutes: how often to check (5, 15, 30, 60, 360, 1440)diff_threshold: percentage of pixels that must change to trigger an alert (0.5–100)notify_email: email address to alertnotify_webhook: URL to POST a signed webhook payload
The monitor fires a webhook with:
diff_percent: exact percentage of changed pixelsscreenshot_url: URL of the new screenshotbaseline_url: URL of the baseline screenshotdiff_image_url: URL of the diff overlay image (red pixels = changed)load_time_ms: page load time (alert if too slow)
Code example: Node.js
import SnapSharp from '@snapsharp/sdk';
const client = new SnapSharp(process.env.SNAPSHARP_API_KEY!);
// Create a monitor for your pricing page
async function createPricingMonitor() {
const monitor = await client.createMonitor({
url: 'https://yourapp.com/pricing',
intervalMinutes: 60,
diffThreshold: 1.5,
notifyEmail: '[email protected]',
notifyWebhook: 'https://yourapi.com/webhooks/visual-alerts',
// Screenshot options
width: 1280,
height: 900,
fullPage: true,
darkMode: false,
});
console.log('Monitor created:', monitor.id);
return monitor;
}
// List all active monitors
async function listMonitors() {
const monitors = await client.listMonitors();
for (const m of monitors) {
console.log(`${m.id}: ${m.url} — every ${m.intervalMinutes}min, threshold ${m.diffThreshold}%`);
}
}
// Handle webhook alert
// In your Express/Next.js/Hono route:
async function handleVisualAlert(payload: {
monitorId: string;
url: string;
diffPercent: number;
diffImageUrl: string;
screenshotUrl: string;
loadTimeMs: number;
}) {
if (payload.diffPercent > 5) {
// Significant change — create incident
await createIncident({
title: `Visual regression detected on ${payload.url}`,
diffPercent: payload.diffPercent,
diffImageUrl: payload.diffImageUrl,
});
} else {
// Minor change — log for review
console.log(`Minor change (${payload.diffPercent}%) on ${payload.url}`);
}
}Code example: Python
from snapsharp import SnapSharp
import os
client = SnapSharp(os.environ['SNAPSHARP_API_KEY'])
# Create monitors for a set of pages
pages_to_monitor = [
{'url': 'https://yourapp.com/', 'threshold': 2.0},
{'url': 'https://yourapp.com/pricing', 'threshold': 1.0},
{'url': 'https://yourapp.com/checkout', 'threshold': 0.5}, # checkout: very sensitive
{'url': 'https://yourapp.com/blog', 'threshold': 5.0}, # blog: allow content changes
]
for page in pages_to_monitor:
monitor = client.create_monitor(
url=page['url'],
interval_minutes=60,
diff_threshold=page['threshold'],
notify_email='[email protected]',
width=1280,
full_page=True,
)
print(f"Created monitor {monitor['id']} for {page['url']}")Manual diff: compare any two screenshots
For one-off comparisons (before/after a deploy, staging vs production), use the /v1/diff endpoint directly:
# Compare staging to production
curl -X POST https://api.snapsharp.dev/v1/diff \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url1": "https://staging.yourapp.com",
"url2": "https://yourapp.com",
"width": 1280,
"full_page": true
}' \
--output diff-result.pngOr via SDK:
const diff = await client.diff({
url1: 'https://staging.yourapp.com',
url2: 'https://yourapp.com',
width: 1280,
fullPage: true,
});
console.log(`Changed pixels: ${diff.diffPercent}%`);
// diff.diffImageUrl → overlay image with red highlighting on changed areasUse case: competitor monitoring
Monitor competitor pricing pages to catch updates:
from snapsharp import SnapSharp
import os
client = SnapSharp(os.environ['SNAPSHARP_API_KEY'])
competitors = [
'https://competitor1.com/pricing',
'https://competitor2.com/pricing',
'https://competitor3.com/pricing',
]
for url in competitors:
client.create_monitor(
url=url,
interval_minutes=360, # Check every 6 hours
diff_threshold=1.0, # Alert on any meaningful change
notify_webhook='https://yourapi.com/competitor-alerts',
# Block cookie banners for clean baseline
block_cookie_banners=True,
block_ads=True,
)The webhook payload includes diffPercent and diffImageUrl. Your handler can post the diff image to Slack:
import requests
def handle_competitor_alert(payload: dict):
message = (
f"*Competitor page changed!*\n"
f"URL: {payload['url']}\n"
f"Changed pixels: {payload['diff_percent']:.1f}%\n"
f"Diff image: {payload['diff_image_url']}"
)
requests.post(os.environ['SLACK_WEBHOOK_URL'], json={'text': message})Setting the right threshold
diff_threshold controls sensitivity. Too low → noise from dynamic content. Too high → misses real changes.
| Page type | Recommended threshold | Reasoning |
|---|---|---|
| Checkout / payment flow | 0.5% | Any change is significant |
| Pricing page | 1.0% | Catch price/plan changes |
| Homepage | 2.0–3.0% | Allow hero image rotations |
| Blog / news | 5.0%+ | Content updates are expected |
| Competitor pages | 1.0% | You want to know everything |
Dynamic content (timestamps, user counts, stock prices) will trigger false positives if you set the threshold too low. Use hide_selectors to mask dynamic elements:
curl -X POST https://api.snapsharp.dev/v1/monitors \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/dashboard",
"interval_minutes": 60,
"diff_threshold": 1.5,
"hide_selectors": [".live-count", ".timestamp", "#cookie-banner"],
"notify_email": "[email protected]"
}'Maintenance windows
Skip alerts during planned deploys or scheduled maintenance:
curl -X PATCH https://api.snapsharp.dev/v1/monitors/mon_abc123 \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"maintenance_windows": [
{
"day": "tuesday",
"start_hour": 2,
"end_hour": 4,
"timezone": "UTC"
}
]
}'Managing monitors via dashboard
The SnapSharp dashboard shows:
- All active monitors with their last screenshot and diff
- Alert history with diff overlay images
- Baseline management (reset baseline after intentional redesign)
- Public status pages (share a monitor's history with clients)
# Update baseline after intentional change
curl -X POST https://api.snapsharp.dev/v1/monitors/mon_abc123/baseline \
-H "Authorization: Bearer YOUR_KEY"
# Pause a monitor temporarily
curl -X PATCH https://api.snapsharp.dev/v1/monitors/mon_abc123 \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"paused": true}'
# Delete a monitor
curl -X DELETE https://api.snapsharp.dev/v1/monitors/mon_abc123 \
-H "Authorization: Bearer YOUR_KEY"Integrating with your existing stack
The webhook payload is a standard POST with HMAC-SHA256 signature:
// Next.js route handler
import { NextRequest, NextResponse } from 'next/server';
import crypto from 'crypto';
export async function POST(req: NextRequest) {
const body = await req.text();
const signature = req.headers.get('x-snapsharp-signature') ?? '';
// Verify webhook authenticity
const expected = crypto
.createHmac('sha256', process.env.SNAPSHARP_WEBHOOK_SECRET!)
.update(body)
.digest('hex');
if (signature !== `sha256=${expected}`) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const payload = JSON.parse(body);
// Route by event type
if (payload.event === 'monitor.alert') {
await handleVisualAlert(payload);
}
return NextResponse.json({ received: true });
}Pricing
Visual monitoring is included in all SnapSharp plans. The Growth plan ($49/mo) supports up to 100 active monitors. Free plan includes 5 monitors.
No extra per-alert fees. Monitors run on SnapSharp's infrastructure — you don't pay for screenshots used by monitors separately from your monthly quota.
Sign up at snapsharp.dev/sign-up — free tier includes 5 monitors with hourly checks.