ScreenshotNeo

BlogHow-to

Browserless Screenshot API Example with Python Requests

Capture a webpage with Browserless’s screenshot API using Python requests. Learn how to authenticate, save image bytes, choose capture options, and troubleshoot common issues.

By the ScreenshotNeo team4 October 20267 min read

To capture a webpage with Browserless in Python, send a JSON POST request to its /screenshot endpoint, pass your API token as the token query parameter, check the HTTP status, then write the binary response to a file. Set options.fullPage to True when you need the whole page instead of just the current viewport.

The example below uses Browserless’s documented production host and Python requests pattern. See the Browserless screenshot API documentation for its current request options. Keep your token private: do not commit it to source control or expose it in logs.

1. Install requests and set up your token

Install the HTTP client if needed:

python -m pip install requests

Get an API token from your Browserless account dashboard. For local use, set it as an environment variable rather than putting the secret in a file that might be committed:

export BROWSERLESS_TOKEN="YOUR_API_TOKEN_HERE"

On Windows PowerShell:

$env:BROWSERLESS_TOKEN = "YOUR_API_TOKEN_HERE"

2. Capture a full-page screenshot with Python

This complete example sends the token in the query string, the page and screenshot options in a JSON body, and saves the response bytes as a PNG:

import os
from pathlib import Path

import requests

TOKEN = os.environ.get("BROWSERLESS_TOKEN")
if not TOKEN:
    raise RuntimeError("Set the BROWSERLESS_TOKEN environment variable first")

endpoint = "https://production-sfo.browserless.io/screenshot"
params = {"token": TOKEN}
headers = {
    "Cache-Control": "no-cache",
    "Content-Type": "application/json",
}
payload = {
    "url": "https://example.com/",
    "options": {
        "fullPage": True,
        "type": "png",
    },
}

response = requests.post(
    endpoint,
    params=params,
    headers=headers,
    json=payload,
    timeout=90,
)
response.raise_for_status()

output_path = Path("screenshot.png")
output_path.write_bytes(response.content)
print(f"Saved {output_path} ({len(response.content)} bytes)")

The successful response is raw image data, not a JSON object. Open the file in binary mode, as Path.write_bytes does here. Calling raise_for_status() first prevents an HTTP error response from being mistakenly saved with a .png extension. The 90-second client timeout is an example limit for waiting on the request; adjust it to fit your workload and your own request deadline.

3. Choose the right capture mode and options

Need Request setting Notes
Visible viewport Omit fullPage or set it to false Captures the browser viewport rather than extending down the page.
Entire document options.fullPage: true Useful for long pages. Some sites load content only as the page is scrolled.
One element Top-level selector Targets an element such as main or #report. The selector must match content present in the rendered page.
Fixed rectangle options.clip Specify the capture rectangle with coordinates and dimensions when you need a precise region.
Lazy-loaded content scrollPage: true The docs recommend scrolling, optionally with full-page mode, so content triggered by scrolling has a chance to load.
Image format options.type Documented formats include PNG, JPEG, and WebP. Match the output filename extension to the format requested.
JPEG or WebP compression options.quality Use where supported by the selected format. Tune for your image quality and file-size needs.
Viewport or higher-density output Viewport size and device scale factor options Set these when you need consistent dimensions or higher pixel density across captures.

For example, to request WebP instead, change the format and output filename together:

payload["options"]["type"] = "webp"
# After the request succeeds:
Path("screenshot.webp").write_bytes(response.content)

To capture inline HTML, send html instead of url; Browserless documentation says not to include both in the same request. To capture a particular element, use the top-level selector field, for example:

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

For a rectangle, put the desired clip coordinates and dimensions under options.clip. Consult the official API reference for the exact shape and supported fields for your account and endpoint version. Browserless also documents wait behavior, navigation configuration, request or resource rejection, and bestAttempt for proceeding when asynchronous events fail or time out. Launch parameters can be supplied in query parameters or a JSON launch payload; when both specify the same launch setting, an individual query parameter takes precedence.

4. cURL and Node.js equivalents

These examples use the same endpoint, token query parameter, and JSON payload. Protect the token as a secret in each environment.

cURL

curl --fail-with-body \
  --request POST \
  "https://production-sfo.browserless.io/screenshot?token=${BROWSERLESS_TOKEN}" \
  --header "Cache-Control: no-cache" \
  --header "Content-Type: application/json" \
  --data '{"url":"https://example.com/","options":{"fullPage":true,"type":"png"}}' \
  --output screenshot.png

Node.js

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: {
    "Cache-Control": "no-cache",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/",
    options: { fullPage: true, type: "png" },
  }),
  signal: AbortSignal.timeout(90_000),
});

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

const image = Buffer.from(await response.arrayBuffer());
await import("node:fs/promises").then(({ writeFile }) => writeFile("screenshot.png", image));
console.log(`Saved screenshot.png (${image.length} bytes)`);

5. Troubleshooting common problems

Symptom Likely cause What to check
401 or authentication error Missing, invalid, or incorrectly passed token Confirm the dashboard token is current and that the request includes ?token=.... Do not confuse this REST endpoint pattern with older Browserless BaaS v1 authentication patterns.
Non-image file or unreadable image An HTTP error body was written as if it were an image, or the extension does not match the requested format Check the status before writing; inspect the error response text when it is not successful; align type and filename.
Only the top of a long page appears Viewport capture is the default behavior Set options.fullPage to true.
Images or lower-page sections are missing Lazy loading depends on scrolling, or the page has not finished rendering Try scrollPage: true, a suitable wait behavior, and full-page capture if appropriate.
Screenshot shows CAPTCHA, blank page, or access denied The target site may block automated browser access or return that page to the browser Check whether the URL is accessible in a normal browser and whether the site permits automated access. Browserless documents a separate /unblock API for some bot-detection cases, but it is not guaranteed to work for every site or authorize access to restricted content.
Request times out Slow navigation, long-running resources, or client deadline too short Set an intentional client timeout, review the endpoint’s wait and navigation options, and handle timeout exceptions. Avoid unbounded retries.
Selector capture fails or is empty Selector is invalid, absent on that page, or the content has not rendered yet Verify the selector in the page, wait for relevant content, and try a viewport capture to distinguish selector issues from navigation issues.

6. Reliability, performance, and cost considerations

  • Check both transport and content. An HTTP success means the request completed; it does not guarantee that the target rendered the page you intended. Bot checks and access-denied pages can be captured as page content.
  • Choose the smallest useful capture. A viewport, selector, or clip may produce less data than a full-page image. PNG preserves detail but can be larger; JPEG and WebP can reduce output size where their compression fits the use case.
  • Set bounded timeouts. Use an explicit client timeout and handle network and timeout exceptions. If retrying transient failures, use a small bounded retry policy with backoff; repeated requests can consume service capacity and should not be automatic for every error.
  • Expect page variation. Dynamic content, cookie state, geography, authentication, and site-side changes can alter the rendered image. Use documented navigation, wait, cookie, header, or launch settings when they address a specific requirement.
  • Review account pricing and limits. The supplied Browserless documentation establishes the API request pattern but does not provide pricing or quota details for this example. Check your current account terms before estimating capture costs.
  • Protect secrets and captured data. The token appears in the URL query string, so avoid logging full request URLs. Treat screenshots as potentially sensitive if the page contains private or authenticated information.

Or skip the browser setup

ScreenshotNeo offers a website screenshot API, so a single GET request can return an image. See the ScreenshotNeo API documentation for request options.

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response headers indicate the page verdict and billing status. Its MCP server lets AI agents use screenshot, page-info, and PDF-capture tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does the screenshot endpoint return JSON?

No. A successful screenshot request returns raw image bytes. Save the response body directly as a binary file.

Can I send both a URL and inline HTML?

No. For inline HTML, send html instead of url, as described in the Browserless documentation.

Does full-page mode guarantee every lazy image will load?

No. Full-page mode requests a taller capture, while lazy-loaded elements may require scrolling or additional wait behavior before capture.

Is a CAPTCHA screenshot a Python error?

Usually it means the target site served a bot check or denied access to the automated browser. The HTTP request and file-writing code can still be working correctly.

Sources