ScreenshotNeo

BlogGuides

Why Is My Screenshot API Capture Different from Chrome?

A screenshot API can differ from Chrome because its browser, viewport, page state, or capture settings differ. Use this checklist to isolate the cause.

By the ScreenshotNeo team4 October 20266 min read

A screenshot API capture can differ from an open Chrome window because the browser build or host environment, viewport and device settings, page state, or screenshot options may differ. Matching the URL alone does not make the captures equivalent. Start by matching viewport, device scale, readiness, and capture mode; then investigate browser, operating system, fonts, and rendering environment.

This guide shows how to compare captures, reproduce the page locally with Puppeteer, and identify common causes. A particular API’s browser version, operating system, and installed fonts are provider-specific; ask the provider when its documentation does not say.

1. Compare the conditions first

Record both capture setups before changing anything. Change one variable at a time so the result points to a cause.

What to match Why it matters
Browser build and host, if known Different browser versions, operating systems, fonts, and graphics stacks can affect rendering. Hosted API details are not established unless the provider documents them.
Viewport width and height These are CSS pixel dimensions and can change responsive breakpoints, line wraps, and layout.
Device scale factor and emulation Pixel density changes output dimensions and rasterization. Mobile mode, touch, and user agent can also affect page behavior.
Page readiness and application state Data, images, animations, and client-side rendering may complete after navigation.
Capture mode and output Viewport, full-page, clipping, background, format, and JPEG quality affect what is captured and how it looks.
URL and browser state Authentication, cookies, locale, color scheme, cache, and application data can change visible content.

Chrome supports headless capture without a visible window, and browser automation exposes viewport and screenshot controls. These controls make it possible to compare conditions explicitly, but a hosted API may not expose every setting. See Chrome Headless CLI, Puppeteer viewport, and the provider’s own parameter documentation.

2. Diagnose differences in a useful order

  1. Compare output dimensions. If width or height differs, check viewport size, device scale factor, and whether one image is full-page while the other is viewport-only.
  2. Match the viewport before navigation. Use the same CSS width and height, then match device scale, mobile mode, touch, and user agent where available. Puppeteer notes that many sites do not expect a phone viewport to change after the page loads, so set it before navigating.
  3. Match page readiness. Wait for the same meaningful element or app-specific ready signal. A fixed delay is useful to test whether late content is the issue, but a selector or readiness condition is generally more precise.
  4. Match screenshot options. Compare viewport versus full-page capture, clip rectangle, background transparency, scale, image type, and JPEG quality. Use PNG while diagnosing visual differences to remove lossy compression as a variable.
  5. Inspect page and browser state. Confirm URL, authentication, cookies, locale, color scheme, cache state, and data are equivalent.
  6. Investigate rendering environment. If geometry and timing match but text shapes or line breaks do not, compare browser version, operating system, and available fonts. Ask the API provider for these details or reproduce locally in its documented browser image if available.

Chrome’s headless CLI documents that a screenshot can be taken after a timeout even if loading has not completed. Prefer a meaningful readiness condition for dynamic pages. Puppeteer documents screenshot capture, waiting for a selector, and screenshot options.

3. Reproduce the capture locally with Puppeteer

This runnable example sets the viewport before navigation, waits for a page-specific selector, and saves a PNG. Install Puppeteer in a Node.js project with npm install puppeteer. Replace the URL and selector with the page and readiness element you need.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({
      width: 1440,
      height: 900,
      deviceScaleFactor: 1
    });
    await page.goto('https://example.com', {
      waitUntil: 'domcontentloaded',
      timeout: 60000
    });
    await page.waitForSelector('main', { timeout: 30000 });
    await page.screenshot({ path: 'capture.png', fullPage: false });
  } finally {
    await browser.close();
  }
})();

Use the same viewport and readiness strategy in the API request if its controls permit them. For a full-page comparison, set fullPage: true; for a transparent PNG, set omitBackground: true. These change the capture and should be matched against the API’s corresponding options.

Optional: compare two local modes

If you suspect headless versus visible Chrome behavior, capture once in each mode with the same Puppeteer version, viewport, URL, readiness condition, and screenshot options. A visible browser window alone does not guarantee identical host, profile, fonts, or page state, so record those too.

4. Common symptoms and fixes

Symptom Likely variable What to try
Different overall layout or breakpoint Viewport width or height Set identical CSS viewport dimensions before navigation.
Everything is sharper, blurrier, or dimensions are scaled Device scale factor or output scaling Match device scale and compare image pixel dimensions.
Missing charts, images, or fetched content Capture happened too early Wait for a page-specific selector or ready signal; use a controlled delay only to diagnose.
Text wraps differently with matching geometry Font availability, browser build, or OS Check loaded fonts in the page and ask the provider about its font set and browser image.
Colors differ but structure matches Color scheme, page state, transparency, or image format Match dark mode, background, and format; compare PNG files.
One image includes more content vertically Full-page versus viewport capture or clipping Use the same capture mode and clip rectangle.
Different logged-in or personalized page Cookies, authentication, locale, or app data Use equivalent session state and request headers where supported.
Intermittent mismatch Asynchronous updates, animation, network variability, or changing data Wait on a stable app condition, disable or finish animations if possible, and compare repeated captures.

5. Reliability, performance, and cost considerations

Repeatable captures depend on controlling the environment and the page state. Pin the browser and automation versions for local workflows, set viewport and scale explicitly, wait for a meaningful readiness condition, and use a lossless format while comparing. A generic network-idle condition can be unsuitable for pages with persistent connections; a page-specific selector is often a better signal.

Full-page capture and high device scale can produce larger images and take more time to render and transfer. Use viewport capture when the task needs only the visible fold, and reserve full-page output for cases that need it. A longer timeout can reduce premature captures but increases the time spent waiting on genuinely slow or stuck pages. For hosted APIs, check the provider’s timeout, billing, caching, and failure policies rather than assuming they match local Puppeteer.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its one-call capture can return an image or PDF, with the API options documented here:

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot, and each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed; response headers report the page verdict and billing status. An MCP server lets AI agents use screenshot, page-info, and PDF-capture tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account.

FAQ

Does headless Chrome always render differently from visible Chrome?

The capture mode is only one possible variable. Compare browser build, host, viewport, device settings, page state, and screenshot options before attributing a difference to headless mode.

Is a device preset the same as emulating a phone?

Not necessarily. Check which viewport, pixel density, user agent, mobile behavior, and touch settings the specific API changes.

Which format should I use to compare captures?

Use PNG while diagnosing pixels because it avoids JPEG’s lossy compression. Once the rendering matches, choose an output format based on the use case.

What if the API does not disclose its browser or fonts?

Ask the provider for the browser version, operating system, and font environment. Without those details, the capture conditions cannot be confirmed from the URL alone.