Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsTo capture a website from a Node.js server with Puppeteer, launch a browser, open a page, navigate to the URL, and call page.screenshot(). The example below returns PNG bytes; add a path option to save a file, or choose full-page, clipped-region, or element capture depending on what the endpoint needs.
Install Puppeteer and capture a page
The following ES module example captures a page and returns its image bytes. Install Puppeteer with npm install puppeteer; the package manages a compatible browser for its standard installation. If your deployment supplies its own browser, configure the launch options for that environment and verify the browser executable is available.
As an Amazon Associate I earn from qualifying purchases.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
const image = await page.screenshot({ type: 'png' });
// `image` is binary image data; return it or persist it as needed.
} finally {
await browser.close();
}
This follows Puppeteer’s documented lifecycle: launch, create a page, navigate, capture, and close the browser. The finally block matters in a server handler because it also runs if navigation or capture throws. Puppeteer’s current API documentation identifies version 25.12.0. See the official Page API example.
Choose what the screenshot should include
| Capture target | How to request it | Useful when |
|---|---|---|
| Current viewport | Default behavior; no full-page or clip option | You want only what is visible at the configured viewport size. |
| Full document | await page.screenshot({ fullPage: true }) |
You need a tall image containing the whole page. |
| Rectangular region | Pass a clip rectangle with x, y, width, and height. |
You need a specific bounded area rather than the whole viewport. |
| One element | Wait for its selector, get its element handle, then call element.screenshot(). |
You need a component, card, chart, or other rendered element by itself. |
Full-page capture
const image = await page.screenshot({ fullPage: true });
Capture a region
const image = await page.screenshot({
clip: { x: 0, y: 0, width: 900, height: 600 }
});
Use coordinates and dimensions appropriate to the rendered page and viewport. Check the result for the intended crop, particularly when layout changes across viewport sizes.
#1 Best Overall
Capture one element
await page.waitForSelector('.report-card');
const card = await page.$('.report-card');
if (!card) throw new Error('Report card was not found');
const image = await card.screenshot();
The screenshot guide notes that an element screenshot scrolls the element into view by default when it is hidden. Waiting for a selector establishes that it exists; it does not necessarily prove that a client-side component has finished populating its content. See the Puppeteer screenshots guide.
Save a file or return image data
Without a path, page.screenshot() returns image data rather than writing a file. By default, the data is a Uint8Array; you can request base64 output with encoding: 'base64'. Set a path when the server should persist the capture locally.
// Save to disk; Puppeteer infers the output type from the extension.
await page.screenshot({ path: 'capture.png' });
// Return bytes to the caller.
const bytes = await page.screenshot({ type: 'png' });
// Return base64 text, for example when an API contract requires JSON.
const base64 = await page.screenshot({ type: 'png', encoding: 'base64' });
For an HTTP endpoint that returns an image directly, send the bytes with a matching content type, such as image/png. If a JSON response requires base64, encode the image deliberately and account for the larger representation; binary output is usually the more natural form for an image response. The API options and return types are documented in Page.screenshot.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Set format, quality, and transparency
- PNG: the default format; it is a sensible choice when you need lossless output or transparency.
- JPEG: set
type: 'jpeg'. Thequalityoption ranges from 0 to 100 and applies only to formats where quality is supported, not PNG. - Transparent background: use
omitBackground: truewhere transparency is needed. - File extension: when using
path, Puppeteer infers the image type from that extension.
const transparentPng = await page.screenshot({
type: 'png',
omitBackground: true
});
const compactJpeg = await page.screenshot({
type: 'jpeg',
quality: 80
});
Consult the complete ScreenshotOptions reference for supported options and constraints.
Wait for the right page state
The official screenshot guide uses waitUntil: 'networkidle2' in its navigation example. It is a useful starting condition, not a guarantee that every site is ready to capture: some pages continue rendering, poll services, or load content only after application-specific events.
await page.goto(url, { waitUntil: 'networkidle2' });
await page.waitForSelector('.report-ready');
const image = await page.screenshot({ fullPage: true });
If the capture depends on known content, wait for its selector or a specific application-ready condition before taking the screenshot. Avoid assuming that a successful navigation alone means a dynamic page has finished displaying the data you need.
Rank #4
Use the capture in a Node.js HTTP endpoint
This framework-neutral handler shape shows the key decisions: validate the requested URL in your application, ensure cleanup runs on errors, and return the binary response with an image content type. URL validation and network access controls are important in a public screenshot service; do not let an untrusted caller use your server to reach internal services.
import puppeteer from 'puppeteer';
export async function capturePng(req, res) {
const url = req.query.url;
if (typeof url !== 'string' || !/^https?:///i.test(url)) {
res.statusCode = 400;
res.end('A valid HTTP or HTTPS URL is required');
return;
}
let browser;
try {
browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
const image = await page.screenshot({ type: 'png' });
res.statusCode = 200;
res.setHeader('Content-Type', 'image/png');
res.end(Buffer.from(image));
} catch (error) {
res.statusCode = 502;
res.end('Screenshot capture failed');
} finally {
if (browser) await browser.close();
}
}
This is a starting shape, not a complete hardened service. Apply request timeouts, URL allowlisting or destination filtering, authentication and rate limits according to your threat model. Do not return raw internal exception details to callers.
Best Value
- Used Book in Good Condition
Plan server lifecycle and concurrency around your workload
Close browser resources on both success and failure paths. The simplest example launches and closes a browser per job. Whether to reuse browser processes, isolate jobs with separate contexts, or impose a queue depends on workload and deployment constraints; the Puppeteer documentation cited here does not establish a universal safe throughput, memory budget, platform choice, or browser-pool configuration.
For shared BrowserContexts, Puppeteer documents that opening a new page or closing a page waits while a screenshot is in progress; bringToFront() does not wait. Avoid treating page focus as a synchronization barrier. See the Page.screenshot API notes.
Troubleshoot common capture failures
| Symptom | Likely cause | What to check |
|---|---|---|
| Navigation or screenshot throws | The browser, navigation, or capture operation failed. | Log the server-side error, confirm the target URL is reachable from the server, and keep browser cleanup in a finally block. |
| Screenshot is blank or incomplete | The page may still be rendering or a required component is not ready. | Wait for a relevant selector or application-ready condition instead of relying only on navigation completion. |
| Only the visible area appears | Default screenshot behavior captures the viewport. | Set fullPage: true for the whole page or use a clip or element handle for a narrower target. |
| No image file appears | No output path was supplied. | Set path, or handle the returned bytes in memory. |
| Transparency or JPEG quality has no effect | The selected option may not apply to the chosen format or page background. | Use omitBackground for transparent output; use quality with JPEG rather than PNG. |
| Capture jobs interfere with page operations | Concurrent page operations can interact with an in-progress screenshot. | Sequence work on the page; in shared contexts, page creation or closure waits for screenshots, while bringToFront() does not. |
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. A single GET request can return an image or PDF, with options for full-page capture, CSS selectors, viewport and device presets, and more. Its clean-shot handling accepts consent banners and removes known consent platforms, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, with verdict and billing information in response headers. AI agents can use its MCP server tools, including take_screenshot, get_page_info, and capture_pdf.
Here is a Node.js call that saves the response bytes:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
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', new Uint8Array(await res.arrayBuffer()));
For a Node.js environment without Bun, write the returned bytes with Node’s file system API. See the ScreenshotNeo documentation for request parameters and response behavior. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month with no card.
Quick Recap
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.




