ScreenshotNeo

BlogHow-to

How to Capture a Full-Page Screenshot with Puppeteer

Capture an entire webpage with Puppeteer using fullPage: true. Learn how to wait for content, choose output options, troubleshoot failures, and automate reliably.

By the ScreenshotNeo team4 October 20267 min read

To capture an entire page with Puppeteer, navigate to it and call page.screenshot({ path: 'screenshot.png', fullPage: true }). The fullPage option defaults to false, so set it explicitly. Puppeteer saves the file when you provide path; without a path, the screenshot is returned as bytes.

1. Install Puppeteer and capture a full page

In a new project, install Puppeteer:

npm install puppeteer

Save this as screenshot.mjs and run it with node screenshot.mjs. Puppeteer downloads a compatible browser as part of its usual installation.

import puppeteer from 'puppeteer';

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

The Puppeteer screenshot guide demonstrates this launch, navigation, and capture flow. networkidle2 is a useful starting point, not a guarantee that every site has finished rendering: pages with ongoing requests, lazy images, or client-side content may need a more specific readiness check.

2. Wait for the page content you need

Choose the navigation wait condition based on the site and the content in the image:

Wait condition When to use it Consideration
load Ordinary pages where the load event is a sufficient starting point. Client-side rendering or later network requests can still change the page.
domcontentloaded You want the parsed document quickly and will wait for a specific element or state afterward. Images and other resources may still be loading.
networkidle2 Pages that settle after a small number of network connections remain active. Long-lived connections or background requests can make it unsuitable.
networkidle0 Pages where you want all network connections to become idle before proceeding. Analytics, polling, or streaming requests can prevent this condition from arriving.

For a page that renders a known component after navigation, wait for that component rather than relying only on a global network condition:

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

For a known application state, wait for a predicate that reflects it:

await page.waitForFunction(() => {
  return document.querySelector('[data-status]')?.textContent?.trim() === 'Ready';
});

These are site-specific checks. Puppeteer cannot infer that an arbitrary page’s custom widget or lazy-loaded content is complete. Scroll through the page or trigger the site’s own load behavior if content appears only as the visitor approaches it, then capture after the desired content is present.

3. Choose screenshot options

The general ScreenshotOptions reference documents the main capture and output controls.

Option What it does Practical note
fullPage: true Captures the full page rather than just the viewport. Set it explicitly; the default is false.
path Saves the screenshot to a file. Relative paths resolve from the process working directory. Omit it to receive the image data instead.
type Selects a supported image format. PNG is the default. A recognized path extension can determine the format.
quality Sets image quality for formats that support it. It does not apply to PNG. Lower quality can reduce output size when using a lossy format.
omitBackground: true Omits the default white background. Useful when the page has transparent regions and you need transparency in the output.
clip Captures a specified rectangular region. Use this for a region rather than a whole-page image; read the documented interaction with captureBeyondViewport.
encoding: 'base64' Returns base64-encoded screenshot data. The default return value is a Uint8Array.

For example, write a JPEG with an explicit quality:

await page.screenshot({
  path: 'page.jpg',
  type: 'jpeg',
  quality: 80,
  fullPage: true
});

Or capture to memory and write the returned bytes yourself:

import { writeFile } from 'node:fs/promises';

const bytes = await page.screenshot({ fullPage: true });
await writeFile('page.png', bytes);

Use the exact option types and format support documented for your installed Puppeteer version. The screenshot API can differ by browser protocol: the current Puppeteer WebDriver BiDi documentation lists clip, encoding, and fullPage as supported Page.screenshot parameters. Do not assume every option in the general reference is available through BiDi; check the BiDi documentation for that path.

4. Capture one element instead of the whole page

If the desired image is a chart, card, or other single component, locate it and use the element handle’s screenshot method:

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

An element screenshot scrolls the element into view if needed and follows the page screenshot behavior. It throws if the element has been detached from the DOM, so query it after the page has rendered and reacquire it if the application replaces that element. See the ElementHandle screenshot reference.

5. cURL, Python, and Node.js alternatives

Puppeteer is a Node.js library, so its browser automation code runs in Node.js rather than cURL or Python. If your application is written in another language, you can still invoke a Node script as a process, or use that language’s own browser automation library. The following service call is an alternative when you do not want to install and operate a browser yourself.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. The request below captures the full page; see the ScreenshotNeo API docs for the available parameters and response details.

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}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.

6. Troubleshooting

Symptom Likely cause Fix
The image contains only the visible viewport. fullPage was omitted or set to false. Set fullPage: true in the screenshot options.
The image is blank or shows a loading state. The capture ran before the application rendered its content, or navigation failed. Check the navigation result and wait for a site-specific selector or ready state before capture.
Images or lower-page content are missing. They load lazily or only after scrolling. Trigger the page’s expected scroll or interaction behavior, wait for the images or content to appear, then capture.
Navigation never reaches networkidle0. The page keeps polling, streaming, or otherwise maintaining network activity. Use an appropriate navigation condition and wait for the specific content your screenshot needs.
Element is not attached to the DOM occurs during an element capture. The application replaced or removed the selected node before its screenshot. Wait for rendering to finish, query the element again, and capture the fresh handle.
No file appears where expected. The path is relative to a different working directory, or no path was supplied. Use an absolute path or check process.cwd(). Without path, write the returned bytes yourself.
The screenshot is unexpectedly large or memory use rises. A full-page image of a very tall page has many pixels to encode and hold. Capture only the required region or element, reduce the page dimensions where suitable, or process pages in separate jobs.
An option is rejected in BiDi mode. The general screenshot option may not be supported by the BiDi implementation. Use only the parameters documented for the protocol in use, or run through the supported protocol path for the option needed.

7. Performance, reliability, and cost

A full-page capture produces an image whose height depends on the document, so very long pages take more memory and time to render and encode than a viewport capture. The research dossier provides no benchmark or universal size limit; actual behavior depends on the page, browser, and environment. When you need only a section, an element screenshot or clipped capture avoids generating an unnecessarily tall image.

For repeatable captures, control the conditions that affect rendering: use a stable URL, wait for the content that matters, set viewport and device scale deliberately when they matter to the output, and handle navigation or selector timeouts. Close the browser in a finally block so it is released after success or failure. If capturing many pages, reuse a browser process where appropriate while isolating pages and handling failures per capture.

Self-hosted Puppeteer has no per-screenshot service charge, but you operate the browser runtime and pay for the compute, storage, and engineering time it requires. ScreenshotNeo pricing is Free for 1,000 shots per month, Starter $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. Every feature is on every plan. Only clean shots are billed, according to the product’s billing behavior described above.

Frequently asked questions

Does fullPage: true include content that has not loaded yet?

It requests a full-page screenshot of the page as rendered at capture time. Wait for the content your target site needs, and trigger lazy loading when applicable.

Does Puppeteer save the screenshot automatically?

Only when you supply a path. Without it, the call returns image data that you can save or process yourself.

Can I make a transparent screenshot?

Use omitBackground: true where supported, and ensure the page itself leaves the relevant regions transparent. Check protocol-specific support if using BiDi.

Can I take a full-page screenshot with cURL?

cURL does not control a local Puppeteer browser. It can call a screenshot service such as ScreenshotNeo’s API, while Puppeteer’s own capture requires Node.js code.

References