ScreenshotNeo

BlogHow-to

Why Does a Screenshot API Capture the Wrong Viewport Size?

Diagnose screenshot size mismatches by separating CSS viewport dimensions, device-pixel scaling, and capture region—and see how to fix each.

By the ScreenshotNeo team4 October 20268 min read

A screenshot API can return an image with unexpected dimensions for three distinct reasons: the page’s effective CSS viewport differs from the requested width and height, device-pixel scaling changes the output image dimensions, or the capture covers a clip or the full scrollable page instead of the visible viewport. Set and inspect the viewport before navigation, then check pixel scale and capture region separately.

Keep these measurements separate while debugging:

  • CSS viewport: the width and height the page uses for layout and media queries.
  • Output pixels: the saved image dimensions, which can be multiplied by device scale or screenshot scale.
  • Capture region: the visible viewport, a specified rectangle, or the full page.

1. Check the effective viewport before capture

Do not rely only on the dimensions you pass to an API wrapper. Record the page’s actual inner width and height immediately before taking the screenshot. A wrapper, browser context, or hosted service may have additional configuration or defaults; inspect its request schema and effective browser settings rather than assuming another tool’s behavior applies.

For Playwright, configure the viewport on the browser context when creating it. This ensures the page starts with the intended size before the site loads:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1280, height: 800 },
  deviceScaleFactor: 1,
});
const page = await context.newPage();

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
console.log(await page.evaluate(() => ({
  innerWidth: window.innerWidth,
  innerHeight: window.innerHeight,
  devicePixelRatio: window.devicePixelRatio,
})));

await page.screenshot({ path: 'viewport.png', fullPage: false, scale: 'css' });
await browser.close();

Replace https://example.com with the target page. The printed values help distinguish a viewport mismatch from a larger output image. Set the size before navigation: many sites do not expect a phone or browser window to change size after loading, and they may respond to a late resize in ways that complicate diagnosis.

Playwright viewport and screen settings

A page can have its own viewport. Playwright’s page-level setViewportSize changes the page viewport and resets the screen size. If you need deliberate control of both screen and viewport, set them when creating the browser context:

const context = await browser.newContext({
  viewport: { width: 390, height: 844 },
  screen: { width: 390, height: 844 },
  deviceScaleFactor: 1,
});

Use the mobile dimensions that match your test case. The effective viewport matters for responsive CSS and media queries; setting only an API parameter without confirming what the browser page reports can hide where a wrapper or context is overriding the request.

2. Separate CSS pixels from device pixels

The viewport is measured in CSS pixels, while an image file is measured in output pixels. On a high-DPI emulated device, one CSS pixel can map to multiple device pixels. Playwright’s screenshot scale option offers two choices:

Screenshot scale Output pixel behavior Use when
css One output pixel per CSS pixel. You want image dimensions to follow the CSS viewport dimensions for a viewport capture.
device One output pixel per device pixel. You want the captured image to reflect the emulated device’s pixel density.

For example, a CSS viewport of 390 × 844 can produce an image with more pixels when device scaling is used on a device with a scale factor greater than one. That does not necessarily mean the page layout used the wrong viewport. Compare window.innerWidth and window.innerHeight with the saved image’s pixel dimensions, and record window.devicePixelRatio as well.

const dimensions = await page.evaluate(() => ({
  cssWidth: window.innerWidth,
  cssHeight: window.innerHeight,
  devicePixelRatio: window.devicePixelRatio,
}));
console.log(dimensions);

await page.screenshot({ path: 'css-pixels.png', scale: 'css' });
await page.screenshot({ path: 'device-pixels.png', scale: 'device' });

Keep the context’s deviceScaleFactor and the screenshot’s scale choice explicit when comparing results. Changing either can affect output pixels without changing the CSS layout dimensions you are trying to test.

3. Verify the capture region

A screenshot can have a different width or height because it captures a different region, even when viewport emulation is correct.

  • Visible viewport: captures the area currently visible in the browser viewport.
  • Full page: captures the entire scrollable document, so its height can exceed the viewport height.
  • Clip: captures a specified rectangle, which can be smaller than or offset from the viewport.

Playwright’s Page API documents that fullPage: true “takes a screenshot of the full scrollable page, instead of the currently visible viewport.” Turn it off when you expect viewport-sized output.

// Visible viewport
await page.screenshot({ path: 'visible.png', fullPage: false });

// Full scrollable document
await page.screenshot({ path: 'full-page.png', fullPage: true });

// A rectangle within the page
await page.screenshot({
  path: 'clipped.png',
  fullPage: false,
  clip: { x: 20, y: 30, width: 500, height: 300 },
});

These examples use Playwright. For another library or a hosted screenshot API, check its own options for full-page capture and clipping; names and defaults can differ.

4. Diagnose a mismatch step by step

  1. Write down the requested width and height. Include which wrapper or service receives them and whether they represent CSS pixels, device pixels, or a capture rectangle.
  2. Set viewport and screen settings before navigation. Use the browser context when both need deliberate configuration.
  3. Read the live page dimensions just before capture. Record window.innerWidth, window.innerHeight, and window.devicePixelRatio.
  4. Check device scale factor and screenshot output scale. Decide whether the saved image should use CSS pixels or device pixels.
  5. Inspect capture-region settings. Confirm whether full-page or clip capture is enabled.
  6. Inspect the tool’s effective settings. For a wrapper or service, verify its request schema and browser configuration instead of assuming its defaults.
  7. Compare like with like. Compare requested CSS viewport dimensions to the page’s CSS dimensions, and compare the expected scaled capture region to the image’s pixel dimensions.

A useful diagnostic record is: requested viewport, effective inner width and height, device scale factor, screenshot scale, full-page flag, clip rectangle, and saved image dimensions.

5. Chrome DevTools Protocol settings

If you use Chrome DevTools Protocol (CDP) directly, viewport emulation and screenshot capture are separate operations. Page.setDeviceMetricsOverride controls device metrics such as width, height, and device scale factor. These settings affect reported screen and inner-window dimensions and device-width/device-height media-query results. Page.captureScreenshot handles the image capture, including capture bounds and options for capturing beyond the viewport.

Check the parameters you send to both commands. A correct metrics override does not guarantee viewport-sized output if capture parameters request a clip or content beyond the visible viewport. Consult the primary references for the precise protocol fields and supported values: CDP device metrics override and CDP screenshot capture.

6. Common errors and fixes

Symptom Likely cause Fix
Image is larger than the requested viewport. Device-pixel scaling or scale: 'device'. Compare CSS viewport values with output pixels; use CSS scale if one output pixel per CSS pixel is desired.
Image is much taller than expected. Full-page capture includes the document’s scrollable height. Disable full-page mode for a visible-viewport capture.
Image is smaller or shifted. A clip rectangle limits or offsets the captured region. Remove the clip or inspect its x, y, width, and height.
Responsive layout uses the wrong breakpoint. The effective viewport or device metrics differ from the requested values. Set the context viewport and, if needed, screen dimensions before navigation; inspect inner dimensions and media-query behavior.
Viewport values look correct after resizing, but layout differs from a fresh capture. The page reacted to a resize after loading. Create the page at the intended viewport before navigation, then compare a fresh load.
One screenshot client works while another does not. The clients may have different wrappers, defaults, units, or capture options. Check each tool’s request schema and effective browser settings; do not transfer another library’s defaults.

7. Performance, reliability, and cost

Viewport size alone does not explain capture time or resource use. Full-page capture may cover a much larger document than a visible-viewport shot, while changing device scale can increase the number of output pixels. If capture latency or image size matters, use the smallest viewport and capture region that meet the task, and avoid device-pixel output unless the extra resolution is needed.

For reliable comparisons, keep viewport, screen, device scale, screenshot scale, and capture region fixed across runs. Capture after the page has reached the state your task needs, and record the effective dimensions at capture time. A hosted service may expose different controls and billing rules; verify its own documentation rather than extrapolating from Playwright or CDP.

8. Use ScreenshotNeo when you do not want to manage browser setup

ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. Its API accepts a URL and can return a screenshot or PDF. Use the documented viewport and capture parameters for your target dimensions; the exact defaults and option names are in the ScreenshotNeo API documentation.

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,
)
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}`);
await Bun.write('shot.webp', res);

The cURL and Python examples save the returned response body. The Node.js example uses Bun’s file-writing helper; in a Node.js project, save the response body with your chosen file-writing method. Replace the example URL with the page you need and consult the documentation for viewport and output options.

Or skip the browser setup

Make a single request to capture a page:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
  • Cookie and consent banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
  • Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and whether the request was billed.
  • An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for ScreenshotNeo and start with 1,000 screenshots a month, no card required.

FAQ

Should the screenshot file always match the viewport dimensions?

Only when you capture the visible viewport at one output pixel per CSS pixel. Device-pixel scale, clipping, and full-page capture can change the file dimensions.

Does a full-page screenshot mean viewport emulation failed?

No. Full-page mode captures the scrollable document, so its height can exceed the visible viewport while the page layout still uses the intended viewport.

Do all screenshot APIs use Playwright’s defaults?

No. The behavior described here is specific to the cited Playwright and CDP controls. Check the documentation and effective settings for the API or wrapper you use.

What should I log to debug this later?

Log requested and effective CSS dimensions, device scale factor, output scale, capture mode, clip bounds if any, and final image dimensions.

References