Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Puppeteer’s Page.screenshot() method: launch a browser, navigate to the URL, wait for the page state you need, and save or return the image. The smallest useful script is:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'hn.png' });
  await browser.close();
})();

This guide shows how to adapt that pattern for viewport, full-page, clipped, and element captures, choose an output format, wait for dynamically rendered content, and diagnose failures.

Set up Puppeteer

Puppeteer runs a Chromium-based browser that it controls through JavaScript. Create a project, install Puppeteer, and use a recent Node.js runtime supported by the Puppeteer version you install.

mkdir site-capture
cd site-capture
npm init -y
npm install puppeteer

The current official documentation surfaced version 25.12.0 on September 29, 2026. Puppeteer’s behavior and bundled browser can change, so check the API documentation matching your installed version when a version-specific detail matters.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture the visible viewport

page.screenshot() captures the current viewport. Supplying path writes the result to disk; the filename extension selects the image type when type is omitted. PNG is the documented default.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'networkidle2' });
    await page.screenshot({ path: 'example.png' });
  } finally {
    await browser.close();
  }
})();

Use an absolute path when a job runs from an unfamiliar working directory. If you omit path, the method returns screenshot bytes instead of creating a file:

const imageBytes = await page.screenshot();
// imageBytes is a binary Buffer by default

To receive a Base64 string, request it explicitly:

const base64Image = await page.screenshot({ encoding: 'base64' });

Choose the capture area

Full page

Set fullPage: true to capture the document’s full scrollable page rather than only what is visible.

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

This is useful for documentation, landing-page reviews, and regression images. It can produce a very tall file, so consider the image dimensions and downstream storage before using it in bulk.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A rectangular region

Pass a clip rectangle when you need coordinates within the page viewport. The rectangle uses x, y, width, and height.

await page.screenshot({
  path: 'hero-region.png',
  clip: { x: 0, y: 120, width: 1280, height: 640 }
});

captureBeyondViewport controls whether a clipped capture may include pixels outside the viewport. Its documented default is false without a clip and true with a clip. Set it deliberately when the rectangle extends beyond what is currently visible.

One element

For a component such as a card, chart, or article, obtain an element handle and call its screenshot() method. Puppeteer scrolls the element into view if necessary.

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

An element handle becomes invalid if the page replaces that node. In that case, acquire a fresh handle after the replacement and capture the new element.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Control when the page is ready

Navigation completion and visual readiness are different things. Puppeteer’s guide demonstrates waitUntil: 'networkidle2', which is a useful starting point, not a universal guarantee that fonts, animations, client-side data, or a particular widget have finished rendering.

await page.goto('https://example.com/dashboard', {
  waitUntil: 'networkidle2'
});
await page.waitForSelector('[data-report-ready]');
await page.screenshot({ path: 'dashboard.png' });

Prefer a condition that represents the page state you actually need:

  • Wait for a stable, page-specific selector after navigation.
  • Wait for the element you plan to capture before taking an element screenshot.
  • For content populated by JavaScript, wait for the populated state rather than assuming the first network idle event is sufficient.
  • Inspect a sample image; a successful HTTP navigation can still yield an empty, blocked, or partially rendered result.

If a site continuously opens connections, a network-idle condition may take longer than expected or never represent “finished.” Use a known DOM condition and set your own job timeout around the whole capture operation.

Configure the image output

Option Use Important behavior
path Save the image to a file The extension determines the type when type is omitted.
type Select PNG, JPEG, or WebP PNG is the documented default.
quality Control lossy image quality Accepts 0–100 and does not apply to PNG.
omitBackground Hide the default white background Allows transparent output where the page supports it.
encoding Choose the returned representation Defaults to binary; use base64 for a Base64 string.
fullPage Capture the complete document Use instead of a viewport-only image when the page scrolls.
clip Capture a rectangle Provide x, y, width, and height.
captureBeyondViewport Permit clipped pixels outside the viewport Documented default is false without a clip and true with a clip.

JPEG, WebP, and transparency examples

await page.screenshot({
  path: 'preview.webp',
  type: 'webp',
  quality: 82
});

await page.screenshot({
  path: 'transparent.png',
  omitBackground: true
});

Do not expect quality to reduce a PNG file; choose JPEG or WebP when lossy compression is appropriate.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A reusable capture script

This version accepts a URL and output path, waits for the navigation condition, and always closes the browser even if capture fails.

const puppeteer = require('puppeteer');

async function capture(url, outputPath) {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.goto(url, { waitUntil: 'networkidle2' });
    await page.screenshot({
      path: outputPath,
      fullPage: true
    });
  } finally {
    await browser.close();
  }
}

const [, , url, outputPath = 'screenshot.png'] = process.argv;
if (!url) {
  console.error('Usage: node capture.js <url> [output-path]');
  process.exit(1);
}

capture(url, outputPath).catch((error) => {
  console.error(error);
  process.exit(1);
});
node capture.js https://example.com page.png

For production jobs, validate the URL, constrain concurrency, give each navigation a finite timeout, and retain the error and output path in your job logs. Reuse a browser process for a controlled batch, but create an isolated page per target so one page’s DOM and cookies do not leak into another.

Troubleshoot common failures

The screenshot is blank or incomplete

  • Cause: the capture ran before client-side content appeared. Fix: wait for a selector or another page-specific ready state, then inspect the image.
  • Cause: the site returned a bot check, error page, or blocked response. Fix: log the final URL and page state and verify the target manually; a successful navigation call alone does not prove useful content was rendered.
  • Cause: a full-page capture is extremely tall. Fix: capture a defined element or clip, or process the resulting file according to your storage limits.

waitForSelector never resolves

The selector may be wrong, hidden behind a different rendering path, or absent for some users. Confirm it in the page’s DOM, make the selector specific to the target state, and apply an outer timeout so a failed page cannot occupy a worker indefinitely.

Element capture says the node was detached

The page replaced the element after you obtained its handle. Wait for the replacement to finish, call waitForSelector again, and capture the newly returned handle.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The output format is unexpected

Check both type and the file extension. If type is omitted, Puppeteer derives the format from the extension; with neither a usable extension nor an explicit type, PNG is the documented default.

The browser does not close after an error

Put capture code in a try/finally block and close the browser in finally. This prevents failed navigations from leaving Chromium processes behind.

Performance, reliability, and operating cost

  • Wait for the smallest sufficient state: a page-specific selector can finish sooner and be more predictable than waiting for every network connection to become idle.
  • Choose scope carefully: viewport and element shots use less memory than very tall full-page images.
  • Control parallelism: too many simultaneous Chromium pages can exhaust CPU or memory; use a bounded worker pool.
  • Make captures reproducible: use the same viewport, URL state, readiness selector, output type, and quality settings for each run.
  • Verify outcomes: retain the screenshot and navigation error details so a technically successful request cannot silently become a bad visual artifact.

Puppeteer itself supplies the screenshot API inside your browser-automation process; your costs are the compute, storage, and operational work required to run that process. There is no separate screenshot charge in the documented API.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a hosted website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF, so you do not need to install Chromium or maintain a browser worker.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

See the complete parameter reference in the ScreenshotNeo documentation.

cURL

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)
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}`);

Before capture, ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. It supports full-page shots with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper and page-range controls, HTML/CSS-to-image, custom JavaScript and CSS, pre-capture clicks, hidden selectors, waits for selectors, delays or network idle, request and resource blocking, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, user-selected cache TTLs, signed public-image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.

Only clean shots are billed. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and each response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Plan Price Included shots
Free $0 1,000 per month; no card
Starter $5 3,000
Growth $15 15,000
Pro $39 60,000
Scale $99 250,000
Business $249 1,000,000

Every feature is available on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to get 1,000 screenshots each month without a card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Which Puppeteer version does this guide refer to?

The official documentation surfaced version 25.12.0 on September 29, 2026. Confirm the version installed in your project and use the matching API reference when behavior differs.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.