Menu
monitoringapitutorialvisual-diff

Website Change Detection API: Monitor Visual Changes Automatically

SnapSharp Team·April 16, 2026·8 min read

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:

  1. Take a baseline screenshot of a page
  2. On a schedule (every 5 minutes, hourly, daily), take a new screenshot
  3. Run a pixel-level comparison between the new screenshot and the baseline
  4. If the diff percentage exceeds your threshold, fire an alert
  5. 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 alert
  • notify_webhook: URL to POST a signed webhook payload

The monitor fires a webhook with:

  • diff_percent: exact percentage of changed pixels
  • screenshot_url: URL of the new screenshot
  • baseline_url: URL of the baseline screenshot
  • diff_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.png

Or 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 areas

Use 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 typeRecommended thresholdReasoning
Checkout / payment flow0.5%Any change is significant
Pricing page1.0%Catch price/plan changes
Homepage2.0–3.0%Allow hero image rotations
Blog / news5.0%+Content updates are expected
Competitor pages1.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.

Website Change Detection API: Monitor Visual Changes Automatically — SnapSharp Blog