ScreenshotNeo

BlogHow-to

Beyond Basic Screenshots: Automating Web Page Capture

Automate viewport, full-page, and element screenshots with Playwright or Puppeteer. Learn how to prepare pages, handle failures, and choose a capture method.

By the ScreenshotNeo team4 October 20268 min read

To automate a web page screenshot, launch a browser with Playwright or Puppeteer, navigate to the page, wait for the content you need to appear, and save a viewport, full-page, or element capture. Use Playwright for a broad browser automation API, Puppeteer when it fits your existing project, and Chrome DevTools Protocol (CDP) when you need lower-level Chrome control. A screenshot records pixels; it does not preserve the page’s semantic structure or make the result interactive.

1. Choose the capture method

Method Use it when Trade-off
ScreenshotNeo You want a hosted screenshot API or MCP tools for an AI agent, without managing a browser process. Requires an API key for API use; its free plan includes 1,000 shots per month.
Playwright You need browser automation plus viewport, full-page, element, and screenshot styling controls. You run and maintain the browser in your environment.
Puppeteer Your application already uses Puppeteer or you want its high-level Chrome automation API. You run and maintain the browser in your environment.
CDP You need direct control over Chrome’s DevTools protocol, including screenshot format and clipping options. It is lower-level; you manage the browser session and protocol messages.

There is no universal winner between Playwright and Puppeteer. Choose based on your existing stack, browser needs, and desired controls. The examples below use JavaScript and Node.js.

2. Install and run Playwright

In a new Node.js project, install Playwright and its Chromium browser:

npm init -y
npm install playwright
npx playwright install chromium

Save this as capture.mjs and run node capture.mjs. It writes a full-page PNG. Replace the readiness condition with a signal that matches the page you capture.

import { chromium } from 'playwright';

const browser = await chromium.launch({ headless: true });
try {
  const page = await browser.newPage({
    viewport: { width: 1440, height: 900 },
    deviceScaleFactor: 1
  });
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
  // Prefer a page-specific signal when the site renders content asynchronously.
  await page.locator('h1').waitFor({ state: 'visible', timeout: 15000 });
  await page.screenshot({ path: 'page.png', fullPage: true, animations: 'disabled' });
} finally {
  await browser.close();
}

The heading wait is only an example. Some sites need a different selector, an application-ready marker, or a carefully chosen short delay. Navigation completing does not guarantee that all application content or images have finished rendering.

Viewport, full-page, and element screenshots

Omit fullPage to capture the current viewport. To capture one component, use a locator screenshot instead:

await page.locator('.product-card').screenshot({ path: 'product-card.png' });

A full-page capture includes the scrollable document; an element capture targets a single node. Playwright does not combine its full-page option with a target element. For lazy-loaded sections, consider scrolling the page in steps and waiting for relevant content before the full-page capture; whether that is needed depends on the page.

Set dimensions and image output

Set the viewport explicitly for repeatable captures. Playwright supports PNG, JPEG, and WebP screenshots, and options such as JPEG quality and image scale. CSS scale uses CSS pixels; device scale uses device pixels and can make the image dimensions and file size larger. Choose output dimensions and format based on the consumer of the image.

await page.screenshot({
  path: 'page.webp',
  type: 'webp',
  quality: 80,
  scale: 'css',
  fullPage: false
});

For full-page output, set fullPage: true. For a higher device-pixel resolution, use scale: 'device' and account for the larger output. See the Playwright Page API for the available options.

Prepare dynamic pages and overlays

For repeatable results, make page state deliberate:

  • Wait for a meaningful selector or application-ready signal instead of assuming navigation means rendering is complete.
  • Disable or wait for animations when a transition could be captured mid-frame.
  • Decide whether cookie dialogs, newsletter overlays, chat widgets, and sticky controls belong in the image. Dismiss or preserve them intentionally.
  • Use Playwright’s screenshot stylesheet option to hide or adjust known dynamic elements when appropriate. A broad hiding rule can also remove content you meant to capture.
  • For lazy-loaded images, scroll through the relevant regions and wait for them to load if the capture needs those images.

Playwright supports handlers for unexpected overlays. Such handlers can change page state, including focus and mouse position, so use them only when that behavior is acceptable. See the Page API documentation.

3. Puppeteer alternative

Install Puppeteer, which downloads a compatible Chrome for Testing browser as part of its standard installation:

npm init -y
npm install puppeteer

Save as capture.cjs and run node capture.cjs:

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: 'networkidle2',
      timeout: 30000
    });
    await page.waitForSelector('h1', { visible: true, timeout: 15000 });
    await page.screenshot({ path: 'page.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Puppeteer’s screenshot guide demonstrates waiting for network activity before capture. That can be useful, but it is not proof that every page is visually settled: some sites keep connections open or render content later. Prefer a page-specific signal when one is available. For a single element, use await page.$eval('.product-card', el => el.screenshot({ path: 'card.png' })) only if your Puppeteer version exposes the element handle API in that form; the documented pattern is to obtain the element handle and call its screenshot method:

const card = await page.$('.product-card');
if (!card) throw new Error('Product card not found');
await card.screenshot({ path: 'product-card.png' });

Consult Puppeteer’s screenshot guide and Page.screenshot documentation for current options and behavior.

4. Use Chrome DevTools Protocol directly

CDP exposes Chrome’s Page.captureScreenshot command. It supports image format selection, JPEG quality, clipping, and capture beyond the viewport. This is useful when a system already speaks CDP or needs protocol-level control; for ordinary scripts, Playwright or Puppeteer usually provides a more convenient page and element API.

// After connecting to a Chrome CDP session and enabling the Page domain:
const result = await client.send('Page.captureScreenshot', {
  format: 'png',
  captureBeyondViewport: true
});
// result.data is base64-encoded image data.

The snippet assumes an established CDP client and session; connection setup depends on how Chrome was launched and how your client library exposes protocol commands. Read the CDP Page domain reference for command parameters. CDP is Chrome-specific; use a higher-level library if you need its browser abstraction or locator workflow.

5. Or skip the browser setup

ScreenshotNeo provides a one-request screenshot API. This cURL example saves a WebP capture of Stripe:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -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"}, 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' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

See the ScreenshotNeo API documentation for options and parameter details. 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 use screenshot, page-info, and PDF capture tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for free and get 1,000 screenshots a month with no card.

6. Troubleshooting

Symptom Likely cause What to do
Screenshot is blank or missing content The capture ran before client-side rendering finished, or the page returned a challenge or error state. Wait for an application-specific visible element, inspect the page state, and check the navigation response. Do not treat a successful navigation event as proof that expected content loaded.
Navigation times out The page keeps network connections open, is slow, or does not reach the chosen lifecycle event. Use a bounded timeout and a less restrictive navigation condition such as domcontentloaded, then wait for a meaningful page-specific signal. Avoid waiting indefinitely for network idle.
Lazy images are absent in full-page output Images may load only when scrolled into view. Scroll through the page before capture and wait for the images or sections you need.
Image dimensions are unexpectedly large Device pixel scale multiplies output dimensions relative to CSS pixels. Use CSS scale or reduce viewport/device scale factor if the consumer does not need the extra resolution.
Capture contains an unwanted popup or sticky bar The overlay appeared after the initial readiness check. Wait for it, dismiss it, or apply a targeted screenshot stylesheet rule. Confirm that removing it does not hide desired content.
Element screenshot fails The selector matched nothing, the element is hidden, or it is outside the expected rendered state. Wait for the locator to be visible, verify the selector, and ensure the element is not detached before capture.
Browser launch fails in a container The browser binary, shared libraries, or required launch configuration may be missing. Install the browser supported by your automation package and its system dependencies for the container. Check the package’s official installation instructions for that environment.

7. Reliability, performance, and cost

For local automation, browser startup and page rendering usually dominate the work. Reuse a browser process for a batch of captures where isolation requirements allow it, create a fresh page per job, and always close pages and browsers in cleanup code. Limit concurrency according to available memory and the target site’s capacity. Set explicit navigation and readiness timeouts so one difficult page cannot stall a batch.

Repeatability depends on controlling inputs: viewport, device scale, locale or other page settings, readiness condition, animations, and overlays. A page can still vary because its content changes, third-party resources fail, or the site renders differently. Save enough context with the image—such as target URL, capture time, and chosen dimensions—to diagnose unexpected output.

Self-hosted Playwright, Puppeteer, and CDP have no per-screenshot API fee, but they require compute, browser installation, maintenance, and operational handling. A hosted API trades that setup for service usage and plan limits. ScreenshotNeo’s listed plans are Free: 1,000 shots/month with no card; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. Only clean shots are billed; cache hits and the stated failed or challenge outcomes cost nothing, with verdict and billing status in response headers. Check the documentation for request behavior and available parameters.

8. Frequently asked questions

Does a screenshot let me extract page text or click controls later?

No. It is an image of rendered pixels. Use the browser DOM, accessibility information, or another structured representation when you need text, page structure, or interaction.

Should I use network idle as my only readiness check?

No. It can be a useful signal, but application-specific content may appear later, and some pages do not become network-idle. Wait for the condition that represents the content you actually need.

Can I capture a single component instead of a whole page?

Yes. Playwright locators and Puppeteer element handles can capture a target element. Use a full-page capture when you need the scrollable document.

Can an AI agent request screenshots?

ScreenshotNeo’s MCP server provides screenshot, page-info, and PDF capture tools for Claude, Cursor, and MCP clients.