Screenshot API: Automated Website Screenshots with Browser Automation
Screenshot API with interactive browser automation. Capture websites, click buttons, fill forms, crop areas. 600 free screenshots/month with Make.com & n8n integration.
Visual regression testing is essential for maintaining consistent user experiences and catching visual bugs before they reach production. A reliable screenshot API for testing automates the process of capturing, comparing, and validating visual changes in your web applications.
According to BrowserStack's testing guide, visual bugs account for over 40% of production issues that escape traditional functional testing. Manual screenshot comparison is time-consuming and error-prone, making automated visual testing crucial for modern development workflows.
This comprehensive guide covers everything you need to know about using screenshot APIs for testing, including visual regression testing setup, screenshot cropping, CI/CD integration, and best practices for detecting visual differences.
Visual regression testing is an automated quality assurance process that compares screenshots of your application before and after code changes to detect unintended visual differences. Unlike functional testing that validates behavior, visual regression testing ensures your UI looks correct across all browsers and devices.
The process involves capturing baseline screenshots of your application, making code changes, capturing new screenshots, and comparing them pixel-by-pixel to identify differences. Any detected changes are flagged for review, allowing developers to catch CSS bugs, layout issues, and rendering inconsistencies before they reach production.
Common use cases include testing responsive designs, validating cross-browser compatibility, detecting CSS regression bugs, verifying component library changes, and ensuring consistent branding across pages.
While tools like Puppeteer and Playwright offer screenshot capabilities, using a dedicated screenshot API provides significant advantages for testing workflows:
Here's how to capture screenshots using the CustomJS API for visual regression testing. This example shows the basic setup that can be integrated into any testing framework.
const axios = require('axios');
const fs = require('fs');
async function captureScreenshot(url, filename) {
const response = await axios.post(
'https://e.customjs.io/screenshot',
{
input: {
url: url,
commands: [],
box: {
x: 0,
y: 0,
width: 1920,
height: 1080
}
}
},
{
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
}
);
fs.writeFileSync(filename, response.data);
console.log(`Screenshot saved: ${filename}`);
}
// Capture baseline screenshot
await captureScreenshot('https://example.com', 'baseline.png');import requests
import os
def capture_screenshot(url, filename):
api_url = 'https://e.customjs.io/screenshot'
headers = {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
}
payload = {
'url': url,
'viewport': {
'width': 1920,
'height': 1080
},
'fullPage': True,
'waitUntil': 'networkidle0'
}
response = requests.post(api_url, json=payload, headers=headers)
with open(filename, 'wb') as f:
f.write(response.content)
print(f'Screenshot saved: {filename}')
# Capture baseline
capture_screenshot('https://example.com', 'baseline.png')Setting up visual regression testing involves capturing baseline screenshots, implementing comparison logic, and integrating with your testing workflow. Here's a complete example using Node.js and the Pixelmatch library for image comparison.
npm install axios pixelmatch pngjsconst axios = require('axios');
const fs = require('fs');
const PNG = require('pngjs').PNG;
const pixelmatch = require('pixelmatch');
async function captureScreenshot(url, width = 1920, height = 1080) {
const response = await axios.post(
'https://e.customjs.io/screenshot',
{
input: {
url: url,
commands: [],
box: {
x: 0,
y: 0,
width: width,
height: height
}
}
},
{
headers: {
'x-api-key': process.env.CUSTOMJS_API_KEY,
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
}
);
return Buffer.from(response.data);
}
function compareScreenshots(baseline, current, diffOutput) {
const img1 = PNG.sync.read(baseline);
const img2 = PNG.sync.read(current);
const { width, height } = img1;
const diff = new PNG({ width, height });
const numDiffPixels = pixelmatch(
img1.data,
img2.data,
diff.data,
width,
height,
{ threshold: 0.1 }
);
fs.writeFileSync(diffOutput, PNG.sync.write(diff));
const diffPercentage = (numDiffPixels / (width * height)) * 100;
return {
numDiffPixels,
diffPercentage: diffPercentage.toFixed(2),
passed: diffPercentage < 0.5 // 0.5% threshold
};
}
async function runVisualRegressionTest(url) {
console.log('Starting visual regression test...');
// Capture current screenshot
const currentScreenshot = await captureScreenshot(url);
fs.writeFileSync('current.png', currentScreenshot);
// Load baseline
if (!fs.existsSync('baseline.png')) {
fs.writeFileSync('baseline.png', currentScreenshot);
console.log('Baseline created. Run test again to compare.');
return;
}
const baseline = fs.readFileSync('baseline.png');
// Compare screenshots
const result = compareScreenshots(
baseline,
currentScreenshot,
'diff.png'
);
console.log(`Diff pixels: ${result.numDiffPixels}`);
console.log(`Diff percentage: ${result.diffPercentage}%`);
console.log(`Test ${result.passed ? 'PASSED' : 'FAILED'}`);
if (!result.passed) {
throw new Error(`Visual regression detected: ${result.diffPercentage}% difference`);
}
}
// Run test
runVisualRegressionTest('https://example.com')
.catch(error => {
console.error(error);
process.exit(1);
});Integrating visual regression testing into your CI/CD pipeline ensures every code change is automatically tested for visual regressions. Here are examples for popular CI platforms.
name: Visual Regression Tests
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
visual-regression:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Download baseline screenshots
uses: actions/download-artifact@v3
with:
name: baseline-screenshots
path: ./screenshots/baseline
continue-on-error: true
- name: Run visual regression tests
env:
CUSTOMJS_API_KEY: ${{ secrets.CUSTOMJS_API_KEY }}
run: npm run test:visual
- name: Upload diff screenshots
if: failure()
uses: actions/upload-artifact@v3
with:
name: visual-diff-${{ github.sha }}
path: ./screenshots/diff
- name: Upload baseline screenshots
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v3
with:
name: baseline-screenshots
path: ./screenshots/baselinevisual_regression_test:
stage: test
image: node:18
variables:
CUSTOMJS_API_KEY: $CUSTOMJS_API_KEY
script:
- npm ci
- npm run test:visual
artifacts:
when: on_failure
paths:
- screenshots/diff/
expire_in: 7 days
only:
- merge_requests
- mainpipeline {
agent any
environment {
CUSTOMJS_API_KEY = credentials('customjs-api-key')
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Visual Regression Tests') {
steps {
sh 'npm run test:visual'
}
}
}
post {
failure {
archiveArtifacts artifacts: 'screenshots/diff/**/*.png'
}
}
}Instead of capturing full-page screenshots, you can capture specific elements to reduce noise and focus on critical UI components.
async function captureElementScreenshot(url, selector) {
const response = await axios.post(
'https://e.customjs.io/screenshot',
{
input: {
url: url,
commands: [
{ type: 'waitForSelector', value: selector },
{ type: 'screenshotElement', value: selector }
],
box: {
x: 0,
y: 0,
width: 1920,
height: 1080
}
}
},
{
headers: {
'x-api-key': process.env.CUSTOMJS_API_KEY,
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
}
);
return response.data;
}
// Capture only the header
await captureElementScreenshot('https://example.com', 'header.main-header'); Use the box parameter to crop screenshots to specific regions. This is useful for capturing specific areas of a page without the full viewport.
async function captureRegion(url, region) {
const response = await axios.post(
'https://e.customjs.io/screenshot',
{
input: {
url: url,
commands: [],
box: {
x: region.x,
y: region.y,
width: region.width,
height: region.height
}
}
},
{
headers: {
'x-api-key': process.env.CUSTOMJS_API_KEY,
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
}
);
return response.data;
}
// Capture header region only (top 200px)
await captureRegion('https://example.com', {
x: 0,
y: 0,
width: 1920,
height: 200
});
// Capture center content area
await captureRegion('https://example.com', {
x: 200,
y: 100,
width: 1200,
height: 800
});For pages with dynamic content, animations, or lazy loading, use appropriate wait strategies to ensure consistent screenshots.
async function captureWithWaitStrategy(url, strategy) {
const commands = [];
// Different wait strategies
switch(strategy) {
case 'networkidle':
commands.push({ type: 'waitForNetworkIdle' });
break;
case 'selector':
commands.push({ type: 'waitForSelector', value: '.content-loaded' });
break;
case 'timeout':
commands.push({ type: 'wait', value: 3000 }); // Wait 3 seconds
break;
case 'custom':
commands.push({ type: 'waitForFunction', value: 'window.dataLoaded === true' });
break;
}
const response = await axios.post(
'https://e.customjs.io/screenshot',
{
input: {
url: url,
commands: commands,
box: {
x: 0,
y: 0,
width: 1920,
height: 1080
}
}
},
{
headers: {
'x-api-key': process.env.CUSTOMJS_API_KEY,
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
}
);
return response.data;
}Different scenarios require different approaches to detecting and handling visual differences. Here are common strategies for various testing needs.
Best for static content where exact pixel matching is required.
const result = pixelmatch(
baseline.data,
current.data,
diff.data,
width,
height,
{
threshold: 0.0, // Exact match required
includeAA: false // Ignore anti-aliasing differences
}
);Allows minor differences, useful for dynamic content or font rendering variations.
const result = pixelmatch(
baseline.data,
current.data,
diff.data,
width,
height,
{
threshold: 0.1, // Allow 10% color difference
includeAA: true // Include anti-aliasing in comparison
}
);Mask dynamic areas like timestamps, ads, or user-specific content before comparison.
const sharp = require('sharp');
async function maskDynamicRegions(imagePath, regions) {
const image = sharp(imagePath);
const metadata = await image.metadata();
// Create mask overlay
const mask = Buffer.from(
``
);
return await image
.composite([{ input: mask, blend: 'over' }])
.toBuffer();
}
// Mask timestamp and ad regions
const maskedBaseline = await maskDynamicRegions('baseline.png', [
{ x: 10, y: 10, width: 200, height: 50 }, // Timestamp
{ x: 800, y: 100, width: 300, height: 250 } // Ad banner
]);// visual-regression.test.js
const { captureScreenshot, compareScreenshots } = require('./screenshot-utils');
describe('Visual Regression Tests', () => {
test('Homepage should match baseline', async () => {
const current = await captureScreenshot('https://example.com');
const baseline = fs.readFileSync('baselines/homepage.png');
const result = compareScreenshots(baseline, current, 'diff/homepage.png');
expect(result.diffPercentage).toBeLessThan(0.5);
});
test('Product page should match baseline', async () => {
const current = await captureScreenshot('https://example.com/product');
const baseline = fs.readFileSync('baselines/product.png');
const result = compareScreenshots(baseline, current, 'diff/product.png');
expect(result.diffPercentage).toBeLessThan(0.5);
});
});const { test, expect } = require('@playwright/test');
const axios = require('axios');
test('visual regression with CustomJS API', async ({ page }) => {
await page.goto('https://example.com');
// Trigger any interactions
await page.click('button.show-modal');
await page.waitForSelector('.modal');
// Capture screenshot via API
const response = await axios.post(
'https://e.customjs.io/screenshot',
{
input: {
url: page.url(),
commands: [],
box: {
x: 0,
y: 0,
width: 1920,
height: 1080
}
}
},
{
headers: {
'x-api-key': process.env.CUSTOMJS_API_KEY,
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
}
);
// Compare with baseline
const baseline = fs.readFileSync('baseline.png');
const result = compareScreenshots(baseline, response.data, 'diff.png');
expect(result.diffPercentage).toBeLessThan(0.5);
});// cypress/e2e/visual-regression.cy.js
describe('Visual Regression', () => {
it('should match homepage baseline', () => {
cy.visit('https://example.com');
// Use custom command to capture via API
cy.captureScreenshot('homepage').then((screenshot) => {
cy.readFile('cypress/baselines/homepage.png', 'base64').then((baseline) => {
cy.compareScreenshots(baseline, screenshot, 'homepage-diff.png')
.then((result) => {
expect(result.diffPercentage).to.be.lessThan(0.5);
});
});
});
});
});For no-code visual regression testing workflows, integrate the CustomJS Screenshot API with Make.com or n8n.
Learn more in our Make.com integration guide.
Check out our n8n integration documentation.
CustomJS offers transparent pricing for screenshot API usage:
Each screenshot request counts as one API call, regardless of viewport size or browser. Compare this to self-hosting Puppeteer clusters or using alternatives like Percy ($299/month) or Chromatic ($149/month).
Visual regression testing is an automated process that compares screenshots of your application before and after changes to detect unintended visual differences. It helps catch CSS bugs, layout issues, and rendering inconsistencies that functional tests might miss.
Screenshot comparison accuracy depends on your threshold settings and comparison algorithm. Pixel-perfect comparison (0% threshold) detects every pixel difference, while threshold-based comparison (0.1-1%) allows minor variations from font rendering or anti-aliasing. Most teams use 0.1-0.5% thresholds for production testing.
Yes! The CustomJS Screenshot API supports Chrome (Chromium), Firefox, and WebKit (Safari) engines. You can capture screenshots from all three browsers with a single API and compare them to detect cross-browser rendering differences.
Mask dynamic regions (timestamps, ads, user data) before comparison, use consistent test data, disable animations, and mock external APIs. You can also use the screenshot API's wait strategies to ensure content is fully loaded before capture.
Puppeteer requires managing browser instances, handling updates, and maintaining infrastructure. A screenshot API provides managed browsers, cross-browser support, consistent rendering, and easy scalability without infrastructure overhead. It's ideal for CI/CD integration and testing workflows.
Add a test step to your CI pipeline that captures screenshots via API, compares them with baselines, and fails the build if differences exceed your threshold. Store baselines as artifacts and upload diff images on failure for review. See our GitHub Actions, GitLab CI, and Jenkins examples above.
Use standard desktop dimensions like 1920x1080 or 1280x720 for full-page screenshots. For cropped regions, adjust the box parameters (x, y, width, height) to capture specific areas like headers, footers, or content sections. The box parameter crops the screenshot, it doesn't change the browser viewport.
Capture new screenshots after verifying the changes are correct, review the diffs to ensure they match expectations, replace old baselines with new screenshots, commit updated baselines to version control, and document the change in your commit message or pull request.
Visual regression testing with a screenshot API provides automated, reliable detection of visual bugs in your web applications. By integrating screenshot comparison into your CI/CD pipeline, you can catch CSS regressions, layout issues, and rendering inconsistencies before they reach production.
The CustomJS Screenshot API simplifies visual testing by providing managed Chromium browsers, screenshot cropping capabilities, and easy integration with existing testing frameworks. With 600 free screenshots per month and native Make.com and n8n integration, it's an accessible solution for teams of all sizes.
Whether you're testing a small website or a large-scale application, automated visual regression testing ensures consistent user experiences and reduces the risk of visual bugs escaping to production.
Get started with visual regression testing today
Continue reading on similar topics
Screenshot API with interactive browser automation. Capture websites, click buttons, fill forms, crop areas. 600 free screenshots/month with Make.com & n8n integration.
A deep dive into the top 5 screenshot APIs for 2025, comparing features, pricing, and performance to help you choose the best one.
Convert Markdown to PDF with tables, code blocks, and custom styling. Complete guide with examples for API documentation, reports, and automated workflows. 600 free conversions/month.