NewIntroducing semantic snapshotsPair every capture with structured DOM data →

For teams that review UI changes

Screenshots stable enough to compare on every build

Capture a preview and production the same way on every build, with motion and moving parts removed, and compare the pixels yourself.

The problem

A CSS change meant for one page moves a button on twelve others. Unit tests do not see it, and screenshot tests that run a browser inside CI are slow and flaky, and they differ between a laptop and the runner because fonts, animations and timing are never quite the same.

How domscout handles it

Every capture runs in the same Chromium at the viewport and pixel density you set, so two captures differ only where the page does. render.disableAnimations and render.reducedMotion stop a transition caught mid-frame from showing up as a change, hideSelectors hides the regions that change on every load (a chat bubble, a timestamp, a rotating banner), and fullPage covers the whole document. domscout returns the images; comparing them is a few lines with any image-diff library, and the threshold for what counts as a change stays in your code.

Where it shows up

  • A pull-request check that captures the preview deployment and compares it with production.
  • Before-and-after images attached to a design-system change.
  • A nightly comparison of the pages a release must not break.

The request

POST /screenshot. fullPage and render start at Pro, and this request costs 2 credits. A full page can be up to 12,000 pixels tall; a taller one is refused rather than cut off. A viewport capture without either option works on every plan for 1 credit.

cURL
curl -X POST https://api.domscout.io/screenshot \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://preview.example.com/pricing",
    "width": 1280,
    "height": 800,
    "deviceScaleFactor": 1,
    "fullPage": true,
    "hideSelectors": ["[data-testid='\''live-chat'\'']"],
    "render": {"disableAnimations": true, "reducedMotion": true},
    "format": "png",
    "responseType": "binary"
  }' \
  -o pricing.png
Node.js
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.domscout.io/screenshot", {
  method: "POST",
  headers: { "Content-Type": "application/json", "x-api-key": "YOUR_API_KEY" },
  body: JSON.stringify({
    url: "https://preview.example.com/pricing",
    width: 1280,
    height: 800,
    deviceScaleFactor: 1,
    fullPage: true,
    hideSelectors: ["[data-testid='live-chat']"],
    render: { disableAnimations: true, reducedMotion: true },
    format: "png",
    responseType: "binary"
  }),
});
await writeFile("pricing.png", Buffer.from(await response.arrayBuffer()));
Python
import requests

response = requests.post(
    "https://api.domscout.io/screenshot",
    headers={"x-api-key": "YOUR_API_KEY"},
    json={
        "url": "https://preview.example.com/pricing",
        "width": 1280,
        "height": 800,
        "deviceScaleFactor": 1,
        "fullPage": True,
        "hideSelectors": ["[data-testid='live-chat']"],
        "render": {"disableAnimations": True, "reducedMotion": True},
        "format": "png",
        "responseType": "binary"
    },
)
with open("pricing.png", "wb") as f:
    f.write(response.content)
Go
package main

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"os"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"url": "https://preview.example.com/pricing",
		"width": 1280,
		"height": 800,
		"deviceScaleFactor": 1,
		"fullPage": true,
		"hideSelectors": []any{"[data-testid='live-chat']"},
		"render": map[string]any{"disableAnimations": true, "reducedMotion": true},
		"format": "png",
		"responseType": "binary",
	})
	req, _ := http.NewRequest("POST", "https://api.domscout.io/screenshot", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-api-key", "YOUR_API_KEY")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := os.Create("pricing.png")
	defer out.Close()
	io.Copy(out, res.Body)
}
PHP
<?php
$ch = curl_init('https://api.domscout.io/screenshot');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'x-api-key: YOUR_API_KEY'],
    CURLOPT_POSTFIELDS => json_encode([
        'url' => 'https://preview.example.com/pricing',
        'width' => 1280,
        'height' => 800,
        'deviceScaleFactor' => 1,
        'fullPage' => true,
        'hideSelectors' => ['[data-testid=\'live-chat\']'],
        'render' => ['disableAnimations' => true, 'reducedMotion' => true],
        'format' => 'png',
        'responseType' => 'binary'
    ]),
]);
$response = curl_exec($ch);
if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}
file_put_contents('pricing.png', $response);
Ruby
require "json"
require "net/http"

response = Net::HTTP.post(
  URI("https://api.domscout.io/screenshot"),
  {
    url: "https://preview.example.com/pricing",
    width: 1280,
    height: 800,
    deviceScaleFactor: 1,
    fullPage: true,
    hideSelectors: ["[data-testid='live-chat']"],
    render: { disableAnimations: true, reducedMotion: true },
    format: "png",
    responseType: "binary"
  }.to_json,
  "Content-Type" => "application/json",
  "x-api-key" => "YOUR_API_KEY"
)
File.binwrite("pricing.png", response.body)
Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;

public class Capture {
    public static void main(String[] args) throws Exception {
        String body = """
            {
                "url": "https://preview.example.com/pricing",
                "width": 1280,
                "height": 800,
                "deviceScaleFactor": 1,
                "fullPage": true,
                "hideSelectors": ["[data-testid='live-chat']"],
                "render": {"disableAnimations": true, "reducedMotion": true},
                "format": "png",
                "responseType": "binary"
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.domscout.io/screenshot"))
            .header("Content-Type", "application/json")
            .header("x-api-key", "YOUR_API_KEY")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();
        HttpResponse<Path> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofFile(Path.of("pricing.png")));
        System.out.println("Saved " + response.body());
    }
}
C#
using System.Net.Http.Json;

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");

var response = await client.PostAsJsonAsync("https://api.domscout.io/screenshot", new {
    url = "https://preview.example.com/pricing",
    width = 1280,
    height = 800,
    deviceScaleFactor = 1,
    fullPage = true,
    hideSelectors = new[] { "[data-testid='live-chat']" },
    render = new { disableAnimations = true, reducedMotion = true },
    format = "png",
    responseType = "binary"
});
await File.WriteAllBytesAsync("pricing.png", await response.Content.ReadAsByteArrayAsync());

Go further

More in Testing and QA: Responsive layout checks · JavaScript error checks · Signup and checkout flow checks · Hover, focus and dark mode screenshots · Localization screenshots · Unlabelled control checks · A screenshot of every step · Embedded widget checks · Screenshots for support tickets · Ad landing page checks · Before and after hydration · Selectors for test automation · Print layout checks · All use cases

Give your product a browser.

Get clean web content and visual proof into your workflow in minutes.