ScreenshotNeo

BlogHow-to

Fix Screenshot API Captures with Incorrect Viewport Dimensions

Diagnose screenshot dimensions by separating CSS viewport size, device-pixel scaling, and capture region. Includes runnable Playwright and Puppeteer fixes.

By the ScreenshotNeo team4 October 20267 min read

If a screenshot’s pixel dimensions do not match the viewport you configured, first check whether you are comparing CSS pixels with device pixels. Then check whether the capture is full-page, clipped, or targeted at an element, and confirm the effective viewport on the page or context that produced the image. Those are separate controls; a dimension mismatch alone does not establish an API defect.

1. Identify what dimensions you expected

Write down the expected width and height in CSS pixels, then inspect the actual image file’s width and height in bitmap pixels. Also record which region you intended to capture: the visible viewport, the full scrollable page, a clip rectangle, or one element.

Observation First setting to inspect
Image is larger than the CSS viewport in both dimensions Screenshot scale and device scale factor
Image has the expected width but is much taller Full-page capture
Image is unexpectedly cropped or has a custom size Clip rectangle or element screenshot
Page layout itself uses a different breakpoint Effective viewport, screen emulation, or configuration order

Do not assume a universal pixel ratio. Measure the actual output and check the effective device scale factor and screenshot scale.

2. Check Playwright viewport and device settings

Playwright can set a viewport on a browser context or resize a page. Its documentation recommends setting the viewport before navigation because many sites are not designed to have their viewport changed after load. If both screen and viewport are relevant, configure both on the context. Device emulation can also set user agent, screen, viewport, touch behavior, and device scale factor. A preset carries its own values, so put intentional overrides after the preset.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1280, height: 800 },
  screen: { width: 1280, height: 800 },
  deviceScaleFactor: 1,
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'viewport.png', fullPage: false, scale: 'css' });
await browser.close();

This example explicitly asks for a 1280 × 800 CSS viewport and one output pixel per CSS pixel. If you need device-pixel output, use scale: 'device'; at a device scale factor above 1, the bitmap can then be larger than the CSS viewport.

When using Playwright Test, keep overrides after any device preset:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [{
    name: 'chromium-custom-viewport',
    use: {
      ...devices['Pixel 5'],
      viewport: { width: 1280, height: 800 },
      screen: { width: 1280, height: 800 },
      deviceScaleFactor: 1,
    },
  }],
});

The later properties override values from the preset. For a real device profile, retain its intended scale factor and inspect the resulting bitmap rather than assuming it will equal the CSS viewport.

3. Check the screenshot region and scale

Playwright’s fullPage: true captures the full scrollable page, not just the current viewport. A clip option changes the captured rectangle, and an element screenshot captures the target element. For a viewport-sized result, omit full-page and clip options and call screenshot on the page.

// Visible viewport only, one image pixel per CSS pixel
await page.screenshot({ path: 'viewport.png', fullPage: false, scale: 'css' });

// Full scrollable page: height may exceed the viewport
await page.screenshot({ path: 'full-page.png', fullPage: true });

// A deliberate crop, measured in page CSS pixels
await page.screenshot({
  path: 'clip.png',
  clip: { x: 100, y: 50, width: 600, height: 400 },
});

// A single element has its own bounds
await page.locator('#receipt').screenshot({ path: 'receipt.png' });

For a minimal diagnosis, capture a known page with an explicit viewport, device scale factor 1, CSS scale, and no full-page, clip, or element targeting. Record the configured viewport, screen, scale factor, scale option, and resulting file dimensions. Add the original options back one at a time to find which setting changes the output.

4. Puppeteer: set the viewport before navigation

Puppeteer also separates viewport configuration from screenshot region options. Set the viewport before navigating. Its screenshot options include fullPage, clip, and captureBeyondViewport; check these if the image is taller or otherwise different from the visible viewport.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setViewport({
  width: 1280,
  height: 800,
  deviceScaleFactor: 1,
});
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
await page.screenshot({
  path: 'viewport.png',
  fullPage: false,
  captureBeyondViewport: false,
});
await browser.close();

For a deliberate crop, supply a clip rectangle with the intended page coordinates and dimensions. Remove it when you expect the full viewport. For a full-page image, set fullPage: true intentionally and expect the output height to reflect page content.

5. Hosted screenshot APIs: verify the service’s own settings

A hosted API may use different parameter names, defaults, browser versions, or scaling behavior than Playwright and Puppeteer. Check its documentation and response metadata for viewport width and height, device or scale settings, full-page mode, clipping, and element targeting. Confirm that the request values reach the same capture that returned the image. If the API exposes a URL or request identifier in response headers, preserve it when reporting a reproducible mismatch.

To compare two outputs, use the same URL and compare five things: CSS viewport, screen size, device scale factor, screenshot scale/output dimensions, and captured region. A historical Playwright issue reported a 980 × 1812 pixel image against a 393 × 727 CSS viewport on an emulated Pixel 5 with device scale factor 2.75, using Playwright 1.30.0. That single 2023 report is an example of differing units and configuration, not evidence of a current general defect or a universal ratio.

6. Troubleshooting checklist

Symptom Likely cause Fix
Bitmap dimensions exceed the configured viewport Device-pixel screenshot scale or a device scale factor above 1 Use CSS scale when available, or calculate and verify output dimensions from the effective settings.
Height is much larger than expected Full-page capture Disable full-page mode to capture only the viewport.
Only part of the page appears Clip rectangle or element targeting Remove the clip or capture the page rather than a locator.
Layout uses an unexpected breakpoint Viewport applied after navigation or overridden by device emulation Set viewport before navigation and place explicit overrides after preset configuration.
Viewport and screen values do not match Only one was set, or a preset supplied the other Set both context values when both matter to the emulated page.
Changing viewport has no visible effect Wrong page/context, later configuration replacement, or page layout behavior Log the values on the actual capture page and reproduce on a minimal page before restoring application code.
Hosted API dimensions still differ Service defaults, parameter mismatch, or different browser behavior Check that service’s parameter names, defaults, version information, and response metadata.

7. Performance, reliability, and cost

Viewport capture generally avoids the extra work and larger output associated with capturing a long page. Full-page capture may require rendering more content and can produce much larger files, especially on pages with long feeds or lazy-loaded media. Device-pixel output also increases image dimensions as scale rises. Choose only the region and pixel scale the consumer needs; this reduces transfer and storage without changing the page’s CSS viewport.

For reliable reproduction, pin the browser and library versions in the project, set viewport and emulation explicitly, and capture only after the page is ready for the desired state. Sites can change layout after fonts, images, or client-side content load, so a screenshot’s dimensions and its page content are separate things to verify. Do not infer a platform-wide defect from one image; first isolate viewport, scale, and region with a minimal capture.

With self-hosted browsers, cost depends on your browser runtime and infrastructure. With a hosted API, check whether unsuccessful captures, retries, or cached results count toward usage and whether output formats affect pricing; these rules vary by provider.

8. Or skip the browser setup

For a managed capture, ScreenshotNeo accepts one GET request with a URL and returns a PNG, JPEG, WebP, or PDF. Its viewport and device options let you set the intended capture; see the ScreenshotNeo API documentation for request parameters.

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()));

ScreenshotNeo accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. An MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month, no card required.

FAQ

Should screenshot width equal viewport width?

Only when the output uses CSS-pixel scale and captures the viewport without clipping or element targeting. Device-pixel scale can produce a wider bitmap.

Does setting screen size set the viewport too?

They are distinct emulation values. Configure both when your scenario depends on both, and inspect any device preset values.

Can a screenshot have correct dimensions but the wrong layout?

Yes. A page can render at the requested dimensions while loading content or using a different emulation profile. Check effective settings and page readiness separately.