ScreenshotNeo

BlogHow-to

How to Generate Website Screenshots with Browserless’s REST API

Capture website screenshots with Browserless’s REST API. Learn the request format, runnable examples, capture options, troubleshooting, and a simpler alternative.

By the ScreenshotNeo team4 October 20268 min read

To capture a website with Browserless, send an HTTPS POST request to its /screenshot endpoint. Pass your API token in the token query parameter and a JSON body containing the page URL and any capture options. The response is image data, so save it as a file rather than trying to parse it as JSON.

Use the production host and region assigned to your Browserless account. The examples below use the documented production-sfo.browserless.io host. Get a real API token from the Browserless dashboard and keep it in an environment variable or secret store; do not commit it to source code. See the official Screenshot API documentation.

1. Make a basic screenshot request

This cURL request captures a full-page PNG and writes the binary response to screenshot.png:

curl -X POST \
  'https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://example.com/",
    "options": {
      "fullPage": true,
      "type": "png"
    }
  }' \
  --output screenshot.png

Replace the example host if your account uses a different endpoint or region, and replace the placeholder token. The request body contains the target URL and optional screenshot settings. The response contains the image bytes.

2. Use the API from Python

Install the HTTP client with python -m pip install requests. Then set BROWSERLESS_TOKEN in your environment and run this script:

import os
import requests

endpoint = "https://production-sfo.browserless.io/screenshot"
token = os.environ["BROWSERLESS_TOKEN"]

response = requests.post(
    endpoint,
    params={"token": token},
    json={
        "url": "https://example.com/",
        "options": {
            "fullPage": True,
            "type": "png",
        },
    },
    timeout=90,
)
response.raise_for_status()

with open("screenshot.png", "wb") as image_file:
    image_file.write(response.content)

Use binary mode (wb) when writing the response. The endpoint returns image data, not a JSON object. The raise_for_status() call makes HTTP errors visible instead of saving an error response as though it were an image.

3. Use the API from Node.js

This example uses the built-in fetch and file APIs in current Node.js releases. Set BROWSERLESS_TOKEN in the environment before running it:

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

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

const endpoint = new URL("https://production-sfo.browserless.io/screenshot");
endpoint.searchParams.set("token", token);

const response = await fetch(endpoint, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    url: "https://example.com/",
    options: { fullPage: true, type: "png" },
  }),
});

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

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

Use arrayBuffer() and write bytes to disk. Calling response.json() would be the wrong handling for a successful image response.

4. Choose what to capture

Goal Request setting Notes
Capture the whole page options.fullPage: true Captures beyond the visible viewport.
Capture one element Top-level selector Place it beside url, not inside options. The API waits for the element and crops to its bounding box.
Capture a fixed rectangle options.clip with x, y, width, and height Use explicit coordinates and dimensions for a rectangular region.
Choose the image format options.type PNG is the default; JPEG and WebP are also documented options. Match the output filename to the selected format.
Wait for content Request configuration such as waitForSelector or waitForTimeout Wait for a meaningful page condition when content loads asynchronously.
Trigger lazy-loaded content Top-level scrollPage: true Pair with options.fullPage: true when you need a long page.
Render supplied HTML html in the body Use this in place of url; do not send both in the same request.
Inject a script or stylesheet addScriptTag or addStyleTag Optional additions for pages that need a script or style before capture.

Capture a specific element

{
  "url": "https://example.com/",
  "selector": "main article",
  "options": { "type": "png" }
}

Keep selector at the top level. If it does not match an element on the rendered page, the request cannot produce the intended element crop; wait for the content or correct the selector.

Capture a rectangle

{
  "url": "https://example.com/",
  "options": {
    "clip": { "x": 0, "y": 0, "width": 1200, "height": 800 },
    "type": "png"
  }
}

Coordinates and dimensions describe the capture region. Make sure the chosen rectangle fits the page and viewport conditions you intend to capture.

Wait and scroll for dynamic pages

{
  "url": "https://example.com/",
  "scrollPage": true,
  "options": { "fullPage": true, "type": "png" },
  "waitForSelector": "main article"
}

Use a selector or page event when possible so capture timing follows the content you need. A fixed delay can be useful when there is no stable selector, but it can waste time on fast pages and still be too short on slow ones. Browserless documents request configuration for waits, selectors, events, functions, and timeouts; check the endpoint’s current documentation for supported field shapes.

5. Supply HTML instead of a URL

For a page you construct yourself, send HTML in the request body. The screenshot guide cautions against including url in the same request:

{
  "html": "<!doctype html><html><body><h1>Rendered from HTML</h1></body></html>",
  "options": { "type": "png" }
}

For supplied markup that depends on styles or scripts, the docs describe addStyleTag and addScriptTag as ways to inject them before the screenshot. Confirm the current schema in the official guide when using those optional fields.

6. Configure browser launch parameters when needed

Browser launch settings can be passed as individual query parameters. More complex launch configuration can be sent in a JSON launch parameter, which must be URL-encoded or base64-encoded. If both an individual query parameter and the launch payload set the same value, the individual parameter takes precedence.

Only use settings supported by the screenshot endpoint and your account’s Browserless host. The Launch Parameters reference documents the available launch configuration. Do not put secrets in logs when constructing or recording a request URL, since the token is in its query string.

7. Troubleshoot common failures

Symptom Likely cause What to try
Saved file is not a valid image An HTTP error or JSON error body was written as an image. Check the HTTP status before saving, inspect the error response, and verify the endpoint, token, and request body.
Blank or white screenshot The page may not have finished rendering, or the target may block browser automation. Wait for a meaningful selector or event; check whether the page is blank in a normal browser too. Browserless documents bot blocking as one possible cause.
Content is missing Content may be asynchronous or lazy-loaded. Wait for the relevant selector and use scrollPage: true for lazy content; use full-page capture when the desired content extends below the viewport.
CAPTCHA, access denied, or HTTP 403 page The target site may be restricting automated traffic. Check the target’s access rules and Browserless guidance. The docs point to the /unblock endpoint for some bot-detection cases and mention residential proxies, but neither guarantees access to every site.
Element capture fails or crops the wrong area The selector may not match, may match an unintended element, or may appear after the capture starts. Inspect the selector on the rendered page, wait for the intended element, and keep selector at the request’s top level.
Request times out Navigation or rendering exceeded the configured wait. Wait for a specific condition rather than an unnecessarily long generic delay, and review the endpoint’s timeout and account limits.
Token appears in logs The token is part of the query string. Keep it in a secret store or environment variable and redact request URLs from application logs.

The launch-parameter reference lists a default timeout of 60,000 milliseconds. The maximum depends on the plan, so check your account’s current limit rather than treating that default as a universal maximum.

8. Reliability, performance, and cost considerations

Browserless describes its REST APIs as stateless, single-action endpoints: a request launches a browser, performs one task, and closes the session. Cookies and state are discarded after the response. This suits independent captures. If your workflow needs persistent cookies, several interactions, or real-time branching, the Browserless REST overview points to session or BrowserQL approaches instead.

  • Choose waits deliberately. Waiting for a needed selector or event avoids capturing too early without imposing the same fixed delay on every page.
  • Use the smallest capture that meets the need. A viewport or element capture avoids producing a long full-page image when only one component is needed. Full-page plus scrolling is useful when the page’s lower content matters.
  • Handle failures explicitly. Check HTTP status codes, preserve useful error details, and retry only failures that are plausibly transient. A CAPTCHA or access-denied response is not fixed by blindly repeating the same request.
  • Plan for independent sessions. Since each REST call is stateless, do not assume a prior request’s cookies or browser state will carry over.
  • Check account-specific limits and pricing. The reviewed API documentation establishes a default timeout and plan-dependent maximum, but does not provide a pricing figure here. Consult your Browserless account for current limits and costs.

For endpoint behavior and account-specific host details, use the Browserless REST API overview and your dashboard documentation.

9. Or skip the browser setup

If you want a screenshot from one HTTP request without managing a browser session, ScreenshotNeo is a website screenshot API and MCP server for developers. It returns PNG, JPEG, WebP, or PDF output. See the ScreenshotNeo API documentation.

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,
)
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}`);
  • Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers identify the page verdict and billing status.
  • An MCP server lets AI agents using Claude, Cursor, or another MCP client call take_screenshot, get_page_info, and capture_pdf.
  • The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan.

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

10. FAQ

Does a Browserless screenshot request return JSON?

No. A successful screenshot request returns image data. Save the response as bytes; check the HTTP status separately to detect errors.

Can I keep browser state between REST screenshot requests?

No. Browserless describes REST calls as stateless, with browser state discarded after each response. Use a session-oriented approach for workflows that need persistent state.

Can Browserless always capture a site that shows a CAPTCHA?

No such guarantee is documented. The target may be blocking automation. Browserless describes options for some bot-detection cases, but site protections can still prevent a successful capture.

Can I capture only one page element?

Yes. Supply a top-level selector alongside the URL; Browserless documents waiting for that element and cropping to its bounding box.