ScreenshotNeo

BlogHow-to

How to Use Browserless for Visual Change Monitoring of Web Pages

Use Browserless to capture consistent page screenshots, then add scheduling, image history, comparison, and alerts around the capture step.

By the ScreenshotNeo team4 October 202611 min read

Short answer: Browserless can render a web page and return a screenshot. To monitor visual changes over time, run that capture on a schedule or trigger, store timestamped images, compare each capture with a baseline or prior image, and route meaningful differences to a review or alert system. Browserless provides the capture step; its screenshot documentation does not describe a complete built-in scheduler, image history, visual diff, and alerting pipeline. Browserless Screenshot API · Make.com integration guide

1. The monitoring workflow

A screenshot is only one observation. A useful monitor needs to make observations comparable and preserve enough context to decide whether a difference matters.

  1. Choose a target and capture scope. Decide whether to watch the visible viewport, the full page, a fixed clip, or one element.
  2. Set a stable capture condition. Use the same viewport, device scale factor, format, and page-ready condition on every run.
  3. Capture on a schedule or event. Invoke the REST API for a stateless capture, or connect through Puppeteer or Playwright when the page needs interaction or browser-side waits.
  4. Store every result. Keep the image, capture timestamp, URL, settings, and response status together. Preserve the baseline and enough recent history to investigate changes.
  5. Compare and triage. Compare each image to a baseline or the prior capture. Send meaningful differences to a review queue or alert route, and suppress known noise such as rotating banners if appropriate.
  6. Check failures before calling them changes. A blank result, CAPTCHA, access-denied page, or missing lazy-loaded content may indicate capture trouble rather than a site change.

Browserless offers managed headless browsers, REST and GraphQL APIs, Puppeteer or Playwright connections, and Docker self-hosting. The REST endpoint is the simplest route for one request per capture; a browser connection gives more control over interaction and readiness. Browserless overview

2. Capture a page with Browserless REST

Get a Browserless API token, then POST JSON to the /screenshot endpoint. Authentication uses the token query parameter. The response is raw image bytes, so save it as a file rather than trying to parse it as JSON. The endpoint supports PNG, JPEG, or WebP output and Puppeteer-style screenshot options. Screenshot API documentation

cURL

export BROWSERLESS_TOKEN='YOUR_API_TOKEN_HERE'
curl --fail-with-body -X POST \
  "https://production-sfo.browserless.io/screenshot?token=${BROWSERLESS_TOKEN}" \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://example.com/",
    "options": {
      "fullPage": true,
      "type": "png"
    }
  }' \
  --output screenshot.png

Python

Install the dependency with python -m pip install requests. Save as capture.py and run BROWSERLESS_TOKEN=YOUR_API_TOKEN_HERE python capture.py.

import os
import requests

TOKEN = os.environ["BROWSERLESS_TOKEN"]
endpoint = f"https://production-sfo.browserless.io/screenshot?token={TOKEN}"
payload = {
    "url": "https://example.com/",
    "options": {"fullPage": True, "type": "png"},
}
response = requests.post(
    endpoint,
    headers={"Cache-Control": "no-cache", "Content-Type": "application/json"},
    json=payload,
    timeout=90,
)
response.raise_for_status()
with open("screenshot.png", "wb") as image_file:
    image_file.write(response.content)
print("Saved screenshot.png")

Node.js

Save as capture.mjs and run BROWSERLESS_TOKEN=YOUR_API_TOKEN_HERE node capture.mjs. This uses the built-in fetch API and requires a Node version that provides it.

import { writeFile } from 'node:fs/promises';

const token = process.env.BROWSERLESS_TOKEN;
if (!token) throw new Error('Set BROWSERLESS_TOKEN first');
const response = await fetch(
  `https://production-sfo.browserless.io/screenshot?token=${encodeURIComponent(token)}`,
  {
    method: 'POST',
    headers: {
      'Cache-Control': 'no-cache',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      url: 'https://example.com/',
      options: { fullPage: true, type: 'png' },
    }),
  },
);
if (!response.ok) {
  throw new Error(`Browserless returned ${response.status}: ${await response.text()}`);
}
await writeFile('screenshot.png', Buffer.from(await response.arrayBuffer()));
console.log('Saved screenshot.png');

The documented quickstart uses the same endpoint and request structure. Its basic examples write the response bytes directly; the examples above also check for HTTP errors before storing a capture. Browserless screenshot examples

3. Choose screenshot settings for repeatable comparisons

Small changes to capture conditions can create large image differences. Pick a configuration for the thing you want to monitor and keep it fixed across runs.

Setting Use it for Monitoring guidance
options.fullPage Capturing the whole document instead of the current viewport Use the same mode each run. Full-page images may vary in height when content is added or removed.
options.type png, jpeg, or webp output Keep the format stable. PNG is a practical default for visual comparisons; lossy formats can introduce compression differences.
options.quality Image quality for formats that support quality settings Keep the value consistent when using it. The endpoint documents quality among its screenshot settings.
options.clip A fixed rectangular region using coordinates and dimensions Use the same clip values and viewport; a changed layout can move content out of the selected region.
selector Capture a particular element by CSS selector Pass it at the top level alongside url. Browserless waits for the element and captures its bounding box.
options.viewport and device scale factor Setting the browser’s visible dimensions and pixel density Use the same viewport and scale to avoid responsive breakpoints or pixel dimensions changing between captures.
scrollPage Triggering lazy-loaded page content Set it at the request top level and pair it with options.fullPage: true for long pages with lazy content.
Wait configuration Waiting for an event, function, selector, or timeout Wait for a meaningful page state rather than relying on an arbitrary delay wherever possible.
gotoOptions Controlling navigation behavior Keep navigation behavior consistent and account for pages whose resources continue loading.
rejectResourceTypes, rejectRequestPattern Rejecting selected resources or requests Blocking can reduce irrelevant work, but blocked fonts, images, or scripts may change the rendered result.
bestAttempt Continuing if asynchronous waits fail or time out Use deliberately: a returned capture after a failed wait may not represent the intended ready state.

For an element capture, a request body has this shape:

{
  "url": "https://example.com/",
  "selector": "main .pricing",
  "options": { "type": "png" }
}

For lazy content, add "scrollPage": true at the top level and set "fullPage": true inside options. For a fixed rectangular capture, use options.clip with x, y, width, and height. Browserless documents selector capture, full-page capture, clipping, viewport, device scale factor, and the lazy-load scrolling option. Screenshot settings and troubleshooting

4. Control when the screenshot is taken

A monitor should capture after the content you care about has rendered. Static pages may be ready after navigation; client-rendered pages often need a particular selector or application state. Browserless REST supports waiting for events, functions, selectors, and timeouts, along with navigation configuration. With Puppeteer or Playwright, use the browser client’s navigation and wait methods to express the page condition directly. Avoid changing the wait policy between baseline and later captures.

For pages that require interaction, connect to a managed browser through Puppeteer or Playwright. For example, Browserless documents using Puppeteer with a WebSocket endpoint and taking a full-page capture after networkidle2. Browser connection screenshot examples

npm install puppeteer-core
import puppeteer from 'puppeteer-core';

const token = process.env.BROWSERLESS_TOKEN;
if (!token) throw new Error('Set BROWSERLESS_TOKEN first');
const browser = await puppeteer.connect({
  browserWSEndpoint: `wss://production-sfo.browserless.io?token=${encodeURIComponent(token)}`,
});
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1365, height: 900, deviceScaleFactor: 1 });
  await page.goto('https://example.com/', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'screenshot.png', fullPage: true });
} finally {
  await browser.close();
}

Use a selector wait when you know the meaningful content marker, or a bounded timeout when the page has a known delayed render. A network-idle condition may never arrive on pages with persistent network activity; if that happens, use a more specific readiness condition supported by the route you chose. Always close the browser connection in a finally block so the session is released after errors too.

5. Add schedule, storage, comparison, and alerts

The capture endpoint returns an image; your surrounding system decides when to call it, where to retain it, how to compare it, and who should see a difference. Browserless’s Make.com guide demonstrates configuring an HTTP request for a Browserless endpoint and describes automating browser tasks. Treat it as an orchestration starting point: the screenshot request alone does not establish that a full diff-and-alert workflow is configured. Browserless with Make.com

Implementation checklist

  • Choose the trigger interval based on how quickly you need to detect a change and how expensive or noisy repeated captures would be in your surrounding system.
  • Use a stable target URL, viewport, scope, format, and readiness condition.
  • Save each image with a timestamp and a record of the configuration used. Keep the baseline distinct from the newest capture.
  • Record capture failures separately from image differences; do not compare an error page with a valid baseline as though it were an ordinary site update.
  • Compare with the baseline for regression monitoring, or with the previous capture for change detection. Decide how to handle expected dynamic regions before escalating.
  • Notify a person or system with the target, capture time, comparison result, and links to both images so the change can be reviewed.
  • Retry transient capture failures with a limit and spacing between attempts. Avoid unlimited retries or overlapping scheduled runs for the same target.

Image-diff thresholds and noise filtering depend on the comparison tool and page. A raw pixel difference can flag antialiasing, timestamps, rotating content, or other dynamic regions. Keep the unmodified images so a reviewer can distinguish a real layout change from capture noise.

6. Or skip the browser setup

If you need a screenshot API without managing a browser connection, ScreenshotNeo captures a page with one GET request. It is a website screenshot API and MCP server for developers. Cookie banners and consent overlays are accepted and removed before capture; 60+ known consent platforms, newsletter popups, and chat widgets can be removed, with each step switchable. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents.

ScreenshotNeo API documentation

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Use the same capture settings for each observation when you add ScreenshotNeo to a visual-monitoring pipeline. Free includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free account and get 1,000 screenshots a month with no card.

7. Troubleshooting

Symptom Likely cause What to do
HTTP error or no image file Invalid token, malformed request, unreachable target, or endpoint error Check the HTTP status and response body before saving bytes as an image. Confirm the token is present, the JSON is valid, and the target URL can be reached.
Blank or white screenshot The site may block automated browsing, or the page may not have rendered before capture Inspect the screenshot and response, compare with a normal browser, and adjust the readiness condition. Browserless lists blank images among signs of automation blocking.
CAPTCHA or access-denied / 403 page The target is presenting a bot challenge or denying the automated browser Treat the result as a capture failure, not a visual site change. Browserless documents these as bot-detection symptoms; check whether the target permits your monitoring method.
Images or sections are missing Content may load lazily only after scrolling, or the screenshot happened too early Use top-level scrollPage: true with options.fullPage: true for lazy-loaded full-page content; wait for a page-specific selector when content is dynamic.
Screenshot differs on every run Viewport, scale, wait condition, dynamic content, or capture scope differs Fix viewport, device scale factor, format, scope, and waits. Identify volatile regions and avoid treating timestamps or rotating modules as regressions.
Full-page capture cuts off content Lazy content did not load, or page dimensions changed while rendering Enable scrolling for lazy content, wait for the relevant content, and review whether the page changes height during capture.
Element selector capture fails The selector is incorrect or the element is absent at capture time Verify the selector in the rendered DOM, use a wait for the element, and pass selector at the request’s top level.
Navigation wait hangs or times out A persistent request prevents the chosen network-idle condition, or a page event never occurs Use a bounded wait tied to the content you need. With Puppeteer or Playwright, choose the appropriate page-ready condition for the target and keep it stable between runs.
Unexpected difference after changing resource blocking A blocked font, image, stylesheet, or script changed the rendering Compare with blocking disabled, then block only resources that do not affect the monitored visual area.

Browserless specifically notes blank or white images, CAPTCHA screens, access-denied or 403 pages, and missing or broken elements as signs a site may be blocking automation. Bot detection troubleshooting

8. Performance, reliability, and cost considerations

  • Capture cost grows with frequency and target count. Estimate how many scheduled requests your workflow creates, including retries. Browserless’s reviewed screenshot references do not state a price, so check its current plan and usage terms before setting a production schedule.
  • Reduce unnecessary work carefully. Capturing one selector or a clip can narrow the image to the monitored area. Rejecting unneeded resource types or request patterns may help the capture workload, but can also change what appears in the screenshot.
  • Set timeouts and bounded retries. Slow sites and blocked pages should produce recorded failures rather than silently becoming image changes. Do not allow retries to overlap without a policy.
  • Keep credentials out of source control. Put the API token in an environment variable or secret store. The token is part of the query string, so avoid logging full endpoint URLs where they would reveal it.
  • Preserve evidence. Keep the capture and the settings used alongside the comparison result. This makes it possible to investigate whether a difference came from the site, the browser conditions, or a failed load.
  • Use one capture path consistently. Mixing REST, different browser clients, or different self-hosted and managed environments can change browser conditions. If you must switch, create a new baseline under the new configuration.

Browserless offers both managed browser services and self-hosting with Docker, so deployment choice affects who operates the browser environment. Its docs describe the available deployment approaches but do not establish a universal performance or reliability figure. Browserless deployment overview

9. Frequently asked questions

Does Browserless include visual diff alerts?

The documented screenshot endpoint returns an image. The reviewed docs do not describe the full schedule, retention, image comparison, and alert chain as a built-in feature; add those components in your workflow.

Should I compare every screenshot to the previous one or to a baseline?

Use a baseline to detect drift from an approved state. Compare to the previous capture when you want to know what changed since the last observation. A workflow may use both for different purposes.

Can I monitor just one component of a page?

Yes. Browserless documents a top-level CSS selector field that captures the element’s bounding box, or you can define a fixed clip rectangle in screenshot options.

Can I render HTML without navigating to a website?

Yes. The REST screenshot API accepts an html field instead of url; do not include both in the same request. This is useful for capturing controlled markup, but it does not monitor a live page URL.

Can I run the capture without using Browserless’s managed service?

Browserless documents cloud and Docker self-hosted deployments. The operational trade-off is who manages the browser deployment and its supporting environment.