ScreenshotNeo

BlogHow-to

How to Take Full-Page Website Screenshots with Browserless in Node.js

Capture full web pages with Browserless in Node.js. Compare its REST and browser-session options, load lazy content, and troubleshoot missing sections.

By the ScreenshotNeo team4 October 20268 min read

To take a full-page screenshot with Browserless in Node.js, send a POST request to its /screenshot REST endpoint with the target url and options.fullPage: true. If the page loads images or sections only when scrolled into view, also set the top-level scrollPage: true. For pages that need custom waits or interaction, connect to a Browserless browser session with Puppeteer or Playwright and call the library’s screenshot method with fullPage: true. Browserless Screenshot API documentation describes the REST request and lazy-content behavior.

1. Choose the capture route

Use the REST API for a straightforward URL-to-image request. Use a connected browser session when your script must inspect or interact with the page before capture, such as waiting for a particular element or navigating through a flow. Both routes can capture the full document, but their request shapes and wait options differ. Browserless’s Node.js examples show the session patterns.

Route Best for Full-page setting Output
REST /screenshot Render a URL and receive an image in one request options.fullPage: true Image response; documented formats include PNG, JPEG, and WebP
Puppeteer session Navigation control and page interaction page.screenshot({ fullPage: true }) Buffer or file, depending on options
Playwright session Navigation control using Playwright’s page API page.screenshot({ fullPage: true }) Buffer or file, depending on options

Use the Browserless endpoint host assigned or documented for your account. The production-region hostname in the examples below is illustrative; do not assume it applies to every deployment. Keep your token in an environment variable and out of source control and logs.

2. Take a full-page screenshot with the REST API

The REST request is a POST with JSON. Put fullPage inside the options object. Browserless returns image data, so write the response bytes directly to a file.

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

const token = process.env.BROWSERLESS_TOKEN;
if (!token) throw new Error("Set BROWSERLESS_TOKEN first");

const endpoint = `https://production-sfo.browserless.io/screenshot?token=${encodeURIComponent(token)}`;
const response = await fetch(endpoint, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Cache-Control": "no-cache",
  },
  body: JSON.stringify({
    url: "https://example.com/",
    options: {
      fullPage: true,
      type: "png",
    },
  }),
});

if (!response.ok) {
  const details = await response.text();
  throw new Error(`Browserless returned HTTP ${response.status}: ${details}`);
}

await writeFile("screenshot.png", Buffer.from(await response.arrayBuffer()));

This uses Node.js’s built-in fetch and filesystem APIs. It requires a Node.js version with global fetch; on older Node.js releases, use an HTTP client or upgrade. The request structure follows the Browserless REST documentation; the wrapper is an example, not a claim of having been executed.

Include below-the-fold lazy content

A full-page image can include the entire document dimensions while still missing content that the page has not loaded yet. Lazy-loaded content often appears only after its area enters the viewport. Browserless recommends setting top-level scrollPage: true together with options.fullPage: true for this case:

body: JSON.stringify({
  url: "https://example.com/long-page",
  scrollPage: true,
  options: {
    fullPage: true,
    type: "png",
  },
})

scrollPage is at the request’s top level; fullPage and the screenshot format are inside options. Scrolling gives the page a chance to trigger lazy loading, but it cannot guarantee that every site-specific script, image, or asynchronous section will finish loading.

Render HTML instead of a URL

For generated markup, Browserless documents sending html instead of url. Do not send both in the same request. For an ordinary website capture, use url.

3. Use Puppeteer when you need page control

Connect with puppeteer-core when your script needs a page object to navigate, wait, or perform actions before capturing. Install the package in your project with npm install puppeteer-core, then run this as an ES module:

import puppeteer from "puppeteer-core";

const token = process.env.BROWSERLESS_TOKEN;
if (!token) throw new Error("Set BROWSERLESS_TOKEN first");

const browser = await puppeteer.connect({
  browserWSEndpoint: `wss://production-sfo.browserless.io?token=${encodeURIComponent(token)}`,
});

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 finally block closes the browser session even if navigation or capture throws. The networkidle2 value is Puppeteer’s navigation wait used in Browserless’s example; it is not interchangeable with Playwright’s wait strings. A quiet network also does not necessarily mean a page-specific animation or application state is ready.

4. Use Playwright when your project uses Playwright

Browserless also documents a playwright-core connection route. Install it with npm install playwright-core. Use the Chromium Playwright WebSocket endpoint/path supplied in Browserless’s current example for your account. The route and navigation wait differ from Puppeteer, so copy the current endpoint from the Browserless Playwright example.

import { chromium } from "playwright-core";

const token = process.env.BROWSERLESS_TOKEN;
if (!token) throw new Error("Set BROWSERLESS_TOKEN first");

// Use the Chromium Playwright WebSocket endpoint documented for your Browserless account.
const browser = await chromium.connectOverCDP(
  `wss://production-sfo.browserless.io/chromium/playwright?token=${encodeURIComponent(token)}`
);

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

Confirm the exact WebSocket path and connection method against your Browserless account’s current documentation before deploying. Browserless’s example uses Playwright-specific connection details and networkidle; do not substitute Puppeteer’s endpoint or networkidle2 without checking compatibility.

5. Choose capture scope, format, and readiness settings

For a whole document, use fullPage. For just one component, Browserless’s REST API supports a selector at the request’s top level, alongside the URL. For a fixed rectangular region, use the REST endpoint’s documented clip option in the location specified by its current schema. Verify option placement for the endpoint you are calling; the separate Browserless Query Language (BQL) interface has its own schema and defaults.

Need Setting or approach What to check
Entire document options.fullPage: true for REST; fullPage: true for a page screenshot Ensure the flag belongs to the correct API’s options object
Lazy-loaded sections Top-level scrollPage: true plus full-page capture on REST Some sites need their own waits or interaction
Single element REST selector field, or the library’s element screenshot workflow Wait for the element and confirm it is visible
Fixed region Documented clip rectangle Use the endpoint-specific clip schema
Different image format REST documents PNG, JPEG, and WebP Use the format option supported by the route
Smaller JPEG/WebP output Set a supported quality value for the chosen format Puppeteer documents quality from 0–100; quality does not affect PNG
Consistent viewport Configure viewport and device scale factor where supported These affect layout and output dimensions; check REST versus library syntax

The Browserless REST docs cover viewport, clip, device scale factor, quality, and selector capture. Puppeteer’s ScreenshotOptions reference describes its own defaults and notes that quality is not applicable to PNG. Do not transfer BQL arguments or defaults into a REST request: BQL is a separate interface, where the documented screenshot timeout default is 30 seconds and fullPage defaults to false.

6. Common problems and fixes

Symptom Likely cause Fix
Only the visible viewport appears fullPage is absent or in the wrong options object For REST, set options.fullPage: true; for Puppeteer or Playwright, set it in page.screenshot().
Images or sections below the fold are missing Lazy content did not load before capture For REST, add top-level scrollPage: true with full-page capture. For a browser session, scroll or wait for the relevant page content before the screenshot.
The wrong area is captured A selector, clip, or capture scope does not match the intended region Check whether you need the full document, a selector, or a clip, and use the syntax for that route.
Image format or quality is unexpected The requested format or quality option is unsupported for that route; PNG does not use Puppeteer’s quality setting Confirm the format and quality behavior in the endpoint or library reference. Use JPEG or WebP if you need lossy quality control.
The script fails to connect or authenticate Token missing, incorrect, expired, or attached to the wrong endpoint host Check BROWSERLESS_TOKEN, the account’s assigned endpoint, and the exact WebSocket path for your library. Keep tokens out of logs.
The browser session remains allocated after an error Cleanup was skipped when an operation threw Close the browser in a finally block, as in the examples.
The image is blank, shows a CAPTCHA, or differs from a normal browser The site may be blocking automation or serving different content Check the returned image and page behavior. Do not assume a screenshot service will bypass the site’s controls; use only access you are authorized to automate.
The REST response is an error instead of an image Request schema, URL, token, or endpoint may be wrong Check HTTP status and response text, confirm the documented host and JSON shape, and ensure you did not send both html and url.

7. Performance, reliability, and cost considerations

  • Full-page images can be large. Long pages and high device scale factors increase output dimensions and bytes. Use JPEG or WebP when a smaller lossy image is acceptable; use PNG when lossless output matters.
  • Readiness is a tradeoff. Waiting for network idle can make captures more complete, but pages with persistent requests may never become idle. Choose waits that match the page and set a bounded timeout using the configuration supported by your route.
  • Lazy loading adds work. Scrolling can trigger more requests and rendering. The document’s full height alone does not prove that all content was fetched.
  • Release browser sessions. Always close connected sessions in a finally block so errors do not skip cleanup. This helps resource management but does not guarantee capture success.
  • Handle failures explicitly. Check REST HTTP status before writing the response as an image. For browser sessions, catch and report navigation and screenshot errors, and avoid logging credentials.
  • Plan service cost from current account terms. The cited Browserless documentation establishes request patterns and options, not a price or a universal quota. Check your account’s current pricing and limits before estimating production spend.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF, and its parameter names also work with those used by other screenshot APIs, which can make switching straightforward. See the ScreenshotNeo API documentation.

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

Cookie banners are accepted like a visitor and removed before capture, along with known newsletter popups and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers say which result occurred. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

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

9. FAQ

Can the Browserless REST API capture a full page?

Yes. Set fullPage to true inside the REST request’s options object.

Why can a full-page image still omit content?

Full-page controls capture dimensions; they do not guarantee every lazy-loaded element has been fetched. Scroll the page or use Browserless’s REST scrollPage option, then wait for any remaining page-specific content.

Should I use the REST API or Puppeteer?

Use REST for a direct URL-to-image request. Use Puppeteer or Playwright when you need a connected page to navigate, wait, or interact before capture.

Does the BQL screenshot schema use the same options?

BQL is a separate Browserless interface with its own documented arguments and defaults. Follow the schema for the interface you actually call rather than copying BQL fields into REST JSON.