ScreenshotNeo

BlogComparisons

Chrome Headless vs Puppeteer for Full-Page Screenshots

Puppeteer offers an explicit full-page screenshot option; Chrome Headless CLI is simpler for viewport captures. Compare both workflows and choose the right setup.

By the ScreenshotNeo team4 October 20269 min read

Short answer: Use Puppeteer when you need a repeatable full-page screenshot workflow. Its page.screenshot() method has an explicit fullPage: true option. Use Chrome Headless’s command-line --screenshot when a simple command-line viewport capture is enough. The CLI reference documents screenshot and window-size flags, but not a matching full-page option. This recommendation follows the documented controls; it is not based on a performance benchmark. Puppeteer screenshot options, Chrome Headless CLI reference.

Both approaches run Chrome without a visible browser window. The practical difference is how much control you need over navigation, readiness, page interaction, and capture scope. This guide shows a runnable command-line capture and a Puppeteer full-page capture, then covers viewport, output, readiness, failure cases, and operational tradeoffs.

1. Choose the capture workflow

Need Better starting point Reason
Capture a page from a shell or script with few moving parts Chrome Headless CLI --screenshot and --window-size cover a straightforward viewport screenshot.
Capture the entire document Puppeteer fullPage: true explicitly requests a full-page screenshot.
Wait for a selector, click controls, or capture a specific element Puppeteer The page API supports navigation and interaction before capture, and element handles have a screenshot method.
Configure virtual display properties for headless Chrome Chrome Headless screen configuration Virtual screen settings control display properties; they do not substitute for Puppeteer’s document-capture setting.
Use a browser mode optimized for automation without needing all regular Chrome features Evaluate chrome-headless-shell Puppeteer documents that shell differs from regular Chrome and may be more performant for some automation. Validate your own pages and fidelity needs.

The main distinction is capture scope versus display setup. Puppeteer’s fullPage controls whether the screenshot covers the document. A viewport or virtual-screen dimension controls the browser’s display area. Chrome’s virtual screen guide.

2. Capture a full page with Puppeteer

Install Puppeteer in a Node.js project. The package downloads a compatible Chrome for Testing browser by default. If your environment manages Chrome separately, consult the Puppeteer installation guide and configure the executable path for your setup.

npm install puppeteer

Save this as screenshot.mjs. It accepts a URL and output path, waits for navigation to reach domcontentloaded, and captures the full document as PNG:

import puppeteer from 'puppeteer';

const url = process.argv[2];
const output = process.argv[3] ?? 'page.png';

if (!url) {
  console.error('Usage: node screenshot.mjs <url> [output.png]');
  process.exit(1);
}

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.goto(url, {
    waitUntil: 'domcontentloaded',
    timeout: 60_000,
  });
  await page.screenshot({ path: output, fullPage: true });
  console.log(`Saved ${output}`);
} finally {
  await browser.close();
}

Run it with:

node screenshot.mjs https://example.com page.png

fullPage is optional and defaults to false. Set it to true to capture beyond the current viewport. Puppeteer also supports output encoding and type, clipping, transparent backgrounds, and capture beyond the viewport. The default for captureBeyondViewport depends on whether a clip is supplied; check the API reference when combining those options. ScreenshotOptions interface.

Wait for the page state you actually need

domcontentloaded means the initial document has been parsed; it does not guarantee that client-rendered content, images, or API-driven widgets have finished. Puppeteer’s screenshot guide demonstrates waiting on navigation with a waitUntil condition. Choose a condition based on the target site, then wait for an app-specific selector if that is a better signal of readiness. Puppeteer screenshots guide.

await page.goto(url, { waitUntil: 'networkidle0', timeout: 60_000 });
await page.waitForSelector('main article', { timeout: 15_000 });
await page.screenshot({ path: 'article.png', fullPage: true });

Network-idle conditions can be a poor fit for pages with continuous polling, analytics, or long-lived requests. In that case, use a page-specific selector or a deliberate short delay after the relevant content appears. Avoid treating a timeout alone as proof that a dynamic page is ready.

Capture only one element

If the goal is a chart, card, or article rather than the whole document, Puppeteer can capture an element handle:

const article = await page.waitForSelector('main article', { timeout: 15_000 });
if (!article) throw new Error('Article element was not found');
await article.screenshot({ path: 'article.png' });

This is a different capture scope from fullPage. See the official screenshot guide for the documented page and element screenshot examples.

3. Capture a viewport with Chrome Headless CLI

Chrome Headless’s CLI is useful when the task is a single command and a viewport image is sufficient. The documented flags include --screenshot, --window-size, and --timeout. The timeout sets a maximum wait before capture even if the page is still loading; it does not establish that a specific dynamic component is ready. Chrome Headless command-line reference.

chrome --headless --no-sandbox --window-size=1440,1000 --timeout=10000 --screenshot=page.png https://example.com

Replace chrome with the executable name or full path installed in your environment. The --no-sandbox flag is commonly needed in constrained container environments, but it changes Chrome’s security isolation; use an appropriately isolated environment and follow your deployment’s security policy. Where Chrome’s sandbox is available and configured, omit that flag.

The CLI example sets the viewport dimensions and a maximum wait, then writes a screenshot. It is a viewport capture workflow. For a document-length capture with explicit page readiness and interaction controls, use Puppeteer or another documented browser automation API. The CLI reference does not describe a fullPage flag equivalent to Puppeteer’s.

4. Configure output, viewport, and fidelity

Puppeteer screenshot options

  • fullPage: boolean, defaults to false; set to true to capture the full page.
  • path: output file path in Node.js examples; omit it when you want screenshot bytes returned to the caller.
  • type and format-specific options: select an output encoding such as PNG or JPEG and use supported quality settings where applicable. Consult the API reference for the exact supported combinations.
  • clip: restrict capture to a specified rectangle. Be mindful that clipping and captureBeyondViewport interact.
  • omitBackground: capture with a transparent background when supported by the chosen format.
  • captureBeyondViewport: controls capture outside the viewport in relevant configurations; its documented default depends on whether a clip is provided.

Use only the options your output needs. A clip is not the same as full-page capture, and a wider viewport does not automatically mean the entire document is captured.

Chrome Headless display settings

--window-size=WIDTH,HEIGHT sets the window dimensions for the CLI example. Chrome also documents virtual screen configuration for headless mode. These settings affect the display environment; they do not give the CLI a Puppeteer-style fullPage option. See Configure virtual screens in Headless mode.

Rendering differences to record

For repeatable comparisons, record Chrome and Puppeteer versions, headless mode, viewport dimensions, virtual display settings, and the readiness condition. Puppeteer distinguishes regular Headless from chrome-headless-shell. The shell does not fully match regular Chrome, though it may be more performant for automation that does not require the complete Chrome feature set. This is not a universal screenshot speed result; verify visual output in the mode you will use. Puppeteer headless modes.

5. Common errors and fixes

Symptom Likely cause What to do
The screenshot stops at the visible viewport Puppeteer’s fullPage option was omitted, so it uses the default false. Set fullPage: true. A larger window size changes viewport dimensions, not this option.
Content or images are missing The page had not rendered that content when capture began, or content is loaded lazily. Wait for a meaningful selector, an appropriate navigation condition, or a site-specific readiness signal before capture.
The CLI captures while the page is still changing --timeout is a maximum wait, not a check for application readiness. Use a Puppeteer workflow when readiness must depend on a selector or an interaction; otherwise choose a suitable CLI timeout and inspect results.
Puppeteer navigation times out The page did not satisfy the selected navigation condition before the configured timeout, often because it remains active. Choose a condition suited to the site, raise the timeout only when justified, and follow navigation with an app-specific selector if needed.
Chrome does not launch in a container The executable may be missing or inaccessible, or the environment’s sandbox configuration may not work. Install/configure Chrome as required by Puppeteer or your CLI environment. Review container isolation and sandbox policy before changing sandbox flags.
Screenshot differs between environments Browser version, headless mode, display dimensions, fonts, or readiness timing differs. Pin or record the browser and automation versions, display settings, and wait condition; compare using the same headless mode.
Element screenshot fails or the element is absent The selector did not match or the element was not ready. Wait for the selector with a bounded timeout, verify the selector against the page, and handle the missing-element case.

6. Performance, reliability, and cost

The official references describe controls and browser modes, not comparative benchmark results. Do not assume a fixed speed advantage for either workflow. Chrome Headless CLI has less orchestration for a basic one-off command; Puppeteer adds a browser automation layer and gives you more control over readiness and interaction. chrome-headless-shell may be more performant for automation where the full Chrome feature set is not needed, but validate both fidelity and resource use for your workload. Puppeteer headless modes.

For reliability, use a bounded timeout, close the browser in a finally block, and wait for the page condition that matches the content you need. Reuse a browser process for batches when appropriate, while keeping each page’s state isolated and ensuring failed captures do not leave processes running. For visual regression or archival work, keep browser mode and display settings consistent. These are operational practices; the cited references do not prescribe specific timing or resource limits.

Local capture has no per-screenshot API fee, but it uses your compute, browser installation, maintenance time, and infrastructure. If you need a managed endpoint, ScreenshotNeo pricing is Free for 1,000 shots per month with no card; Starter is $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan.

7. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Its capture can accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. AI agents can use its MCP server tools: take_screenshot, get_page_info, and capture_pdf.

For a full-page shot, add full_page=true to the request. See the ScreenshotNeo API documentation for the supported parameters and options.

cURL

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

Python

import requests

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

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com',
  full_page: 'true',
});
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 code uses full_page for full-page capture. ScreenshotNeo also supports element capture by CSS selector, dark mode, device presets and custom viewports, retina scale, PDF settings, custom CSS and JavaScript, clicks, hiding selectors, selector/delay/network-idle waits, request and resource blocking, custom headers, cookies, user agent and Authorization, timezone, geolocation, transparency, resizing, configurable cache TTL, signed public image links, asynchronous jobs with signed webhooks, bulk capture of 100 URLs per call, a usage API, and an OpenAPI spec. It accepts parameter names used by other screenshot APIs to make migration easier. Consult the docs for exact parameter names and combinations.

Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots; 1,000 screenshots a month are free with no card and paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

8. Frequently asked questions

Do I need Puppeteer to take a screenshot with Chrome Headless?

No. Chrome’s CLI supports screenshot capture. Puppeteer is the direct fit when your workflow needs its explicit full-page option and browser automation controls.

Does Puppeteer’s full-page option wait for every image?

The option controls capture scope; readiness is a separate concern. Wait for the page state or content your use case requires before taking the screenshot.

Is chrome-headless-shell always faster?

No such universal result is established by the cited documentation. Puppeteer says it may be more performant for automation that does not require the full Chrome feature set; measure and validate your own workload.

Can I use Chrome’s virtual screen settings instead of fullPage?

No. Display configuration and document capture scope solve different problems. Use the capture API’s full-page setting when you need the whole document.

Sources