Implementing a Website Monitoring System with Screenshot API
Website monitoring is essential for businesses that rely on their online presence. By capturing regular screenshots, you can visually track changes, detect issues, and ensure your site is functioning correctly. This tutorial will guide you through building an automated monitoring system using our Screenshot API.
Why Visual Monitoring Matters
Traditional monitoring focuses on uptime and performance metrics, but visual monitoring adds another dimension:
- Detect rendering issues that don't trigger errors
- Identify UI/UX regressions after deployments
- Spot unauthorized content changes
- Verify third-party components display correctly
- Document compliance with regulatory requirements
System Architecture
Our monitoring system will consist of:
- A scheduler to trigger screenshots at regular intervals
- The Screenshot API to capture website images
- Image storage for the screenshots
- A comparison engine to detect changes
- Notification system for alerts
Implementation Steps
1. Setting Up the Scheduler
We'll use Node.js with a cron job scheduler:
01const cron = require('node-cron');02const { captureScreenshot, compareScreenshots } = require('./screenshot-service');03const { sendAlert } = require('./notification-service');0405// Schedule screenshots every hour06cron.schedule('0 * * * *', async () => {07 console.log('Running scheduled screenshot capture...');0809 const websites = [10 { name: 'Main Website', url: 'https://example.com' },11 { name: 'Product Page', url: 'https://example.com/products' },12 { name: 'Checkout Page', url: 'https://example.com/checkout' }13 ];1415 for (const site of websites) {16 try {17 await processWebsite(site);18 } catch (error) {19 console.error(`Error processing ${site.name}:`, error);20 }21 }22});2324async function processWebsite(site) {25 // Capture new screenshot26 const newScreenshot = await captureScreenshot(site.url);2728 // Get previous screenshot from storage29 const previousScreenshot = await getPreviousScreenshot(site.name);3031 if (previousScreenshot) {32 // Compare screenshots33 const changes = await compareScreenshots(previousScreenshot, newScreenshot);3435 if (changes.percentageDifference > 5) {36 // Alert if significant changes detected37 await sendAlert({38 website: site.name,39 changePercentage: changes.percentageDifference,40 previousImage: previousScreenshot.url,41 newImage: newScreenshot.url,42 changesImage: changes.diffImageUrl43 });44 }45 }4647 // Save new screenshot as the latest48 await saveScreenshot(site.name, newScreenshot);49}
2. Screenshot Capture Service
01const fetch = require('node-fetch');02const AWS = require('aws-sdk');03const s3 = new AWS.S3();04const { v4: uuidv4 } = require('uuid');0506async function captureScreenshot(url) {07 // Call Screenshot API08 const response = await fetch('https://api.screenshotapi.com/capture', {09 method: 'POST',10 headers: {11 'Content-Type': 'application/json',12 'Authorization': `Bearer ${process.env.SCREENSHOT_API_KEY}`13 },14 body: JSON.stringify({15 url,16 width: 1280,17 height: 800,18 fullPage: true,19 format: 'png'20 })21 });2223 const data = await response.json();2425 // Download the screenshot26 const imageResponse = await fetch(data.screenshotUrl);27 const imageBuffer = await imageResponse.buffer();2829 // Generate unique filename30 const filename = `${uuidv4()}.png`;3132 // Upload to S333 await s3.putObject({34 Bucket: process.env.S3_BUCKET,35 Key: filename,36 Body: imageBuffer,37 ContentType: 'image/png'38 }).promise();3940 // Return screenshot info41 return {42 url: `https://${process.env.S3_BUCKET}.s3.amazonaws.com/${filename}`,43 timestamp: new Date().toISOString(),44 metadata: data.metadata45 };46}
3. Image Comparison Service
01const { PNG } = require('pngjs');02const pixelmatch = require('pixelmatch');03const fetch = require('node-fetch');0405async function compareScreenshots(previous, current) {06 // Download both images07 const [prevImg, currImg] = await Promise.all([08 downloadImage(previous.url),09 downloadImage(current.url)10 ]);1112 // Parse PNG images13 const prevPng = PNG.sync.read(prevImg);14 const currPng = PNG.sync.read(currImg);1516 // Create output image17 const { width, height } = prevPng;18 const diffPng = new PNG({ width, height });1920 // Compare pixels21 const numDiffPixels = pixelmatch(22 prevPng.data,23 currPng.data,24 diffPng.data,25 width,26 height,27 { threshold: 0.1 }28 );2930 // Calculate difference percentage31 const totalPixels = width * height;32 const percentageDifference = (numDiffPixels / totalPixels) * 100;3334 // Generate diff image35 const diffBuffer = PNG.sync.write(diffPng);36 const diffImageUrl = await uploadDiffImage(diffBuffer);3738 return {39 percentageDifference,40 diffPixels: numDiffPixels,41 totalPixels,42 diffImageUrl43 };44}4546async function downloadImage(url) {47 const response = await fetch(url);48 return await response.buffer();49}5051async function uploadDiffImage(buffer) {52 // Similar to the screenshot upload in captureScreenshot function53 // ...54}
4. Notification Service
01const nodemailer = require('nodemailer');02const slack = require('@slack/webhook');0304const slackWebhook = new slack.IncomingWebhook(process.env.SLACK_WEBHOOK_URL);05const transporter = nodemailer.createTransport({06 // Email configuration07 // ...08});0910async function sendAlert(alertData) {11 // Send Slack notification12 await slackWebhook.send({13 text: `🚨 Visual changes detected on ${alertData.website}`,14 blocks: [15 {16 type: 'section',17 text: {18 type: 'mrkdwn',19 text: `*Visual changes detected on ${alertData.website}*\n${alertData.changePercentage.toFixed(2)}% of the page has changed.`20 }21 },22 {23 type: 'image',24 title: {25 type: 'plain_text',26 text: 'Visual differences'27 },28 image_url: alertData.changesImage,29 alt_text: 'Visual differences highlighted'30 },31 {32 type: 'actions',33 elements: [34 {35 type: 'button',36 text: {37 type: 'plain_text',38 text: 'View Previous'39 },40 url: alertData.previousImage41 },42 {43 type: 'button',44 text: {45 type: 'plain_text',46 text: 'View Current'47 },48 url: alertData.newImage49 }50 ]51 }52 ]53 });5455 // Send email alert56 await transporter.sendMail({57 from: process.env.EMAIL_FROM,58 to: process.env.ALERT_EMAIL,59 subject: `Website Change Alert: ${alertData.website}`,60 html: `61 <h1>Visual changes detected on ${alertData.website}</h1>62 <p>${alertData.changePercentage.toFixed(2)}% of the page has changed.</p>63 <h2>Visual Difference:</h2>64 <img src="${alertData.changesImage}" alt="Visual differences" style="max-width: 100%;" />65 <div>66 <a href="${alertData.previousImage}">View Previous Version</a> |67 <a href="${alertData.newImage}">View Current Version</a>68 </div>69 `70 });71}
Deployment Options
This monitoring system can be deployed in several ways:
- AWS Lambda: For serverless operation with scheduled triggers
- Docker Container: For easy deployment to any container platform
- Traditional VPS: For complete control over the environment
Advanced Features
Once your basic monitoring system is working, consider these enhancements:
- Visual Regression Testing: Integrate with CI/CD pipelines to catch UI issues before deployment
- Historical Archiving: Keep a timeline of website changes for compliance or analysis
- Element-Specific Monitoring: Focus on critical UI components rather than the entire page
- Multi-Browser Testing: Capture screenshots across different browsers to ensure cross-browser compatibility
By implementing this monitoring system, you'll gain valuable insights into your website's visual stability and be alerted to unexpected changes before they impact your users.
Ready to Get Started?
Get your API key now and start capturing screenshots in minutes.