Browserless Screenshot API Review: Features, Limits, and Trade-offs
Learn how Browserless’s screenshot endpoint works, which capture controls it offers, where its stateless model falls short, and what to consider before adopting it.
Browserless’s current REST /screenshot endpoint turns a URL or supplied HTML into PNG, JPEG, or WebP image bytes with one authenticated POST request. It supports viewport and full-page captures, CSS selector and clip-region captures, format and viewport controls, and several ways to wait for page content. Its main operational limit is that each REST call is a single stateless browser task: cookies and browser state do not persist between requests. Browserless documents these behaviors, but the available evidence does not establish comparative speed, visual fidelity, or success rates. Browserless Screenshot API documentation · REST API overview.
How the endpoint works
Send a JSON body to the regional Browserless /screenshot endpoint, with your token in the query string. Use either url to navigate to a webpage or html to render supplied markup; do not send both fields in the same request. The response is the image itself, so save the response bytes to a file rather than trying to parse it as JSON. The documented current endpoint accepts PNG, JPEG, and WebP output through screenshot options. Endpoint and output format details.
Prerequisites
- A Browserless API token from your account.
- The regional endpoint for your account, such as
https://production-sfo.browserless.io/screenshot. Browserless documents multiple endpoints; choose the applicable region from your account and current connection documentation. - An HTTP client able to send JSON and write binary response data.
Basic request examples
Replace YOUR_API_TOKEN with your token and change the endpoint region if your account uses another one. These examples request a full-page PNG and write the binary response to a file.
cURL
curl --fail-with-body -X POST \
'https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN' \
-H 'Content-Type: application/json' \
-H 'Cache-Control: no-cache' \
--data '{"url":"https://example.com/","options":{"fullPage":true,"type":"png"}}' \
--output screenshot.png
Python
import requests
endpoint = "https://production-sfo.browserless.io/screenshot"
response = requests.post(
endpoint,
params={"token": "YOUR_API_TOKEN"},
headers={"Cache-Control": "no-cache"},
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)
Node.js
import { writeFile } from "node:fs/promises";
const endpoint = new URL("https://production-sfo.browserless.io/screenshot");
endpoint.searchParams.set("token", "YOUR_API_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" },
}),
signal: AbortSignal.timeout(90_000),
});
if (!response.ok) {
throw new Error(`Browserless returned ${response.status}: ${await response.text()}`);
}
await writeFile("screenshot.png", Buffer.from(await response.arrayBuffer()));
JavaScript with explicit binary handling
In Node.js, fetch returns a response body, not a file. Convert its array buffer to a Buffer before writing. In browser JavaScript, use a Blob and object URL to offer the binary response as a download; avoid exposing a secret token in client-side code.
const endpoint = "https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN";
const response = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
url: "https://example.com/",
options: { fullPage: false, type: "jpeg", quality: 80 },
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const imageBlob = await response.blob();
// Use imageBlob in a browser UI, or convert its arrayBuffer to a Node Buffer.
Capture options and configuration
Browserless passes screenshot settings in an options object using Puppeteer-style screenshot options, and adds endpoint-level controls for selectors, waits, scrolling, navigation, and rejected requests. Check the current API reference for the complete accepted schema; the examples below show documented option families rather than every possible browser setting. Screenshot options.
| Need | Setting or approach | Notes |
|---|---|---|
| Visible viewport | Leave fullPage false |
Capture corresponds to the configured viewport. Set the viewport deliberately for responsive layouts. |
| Entire document | options.fullPage: true |
For lazy content, pair with top-level scrollPage: true so content is brought into view first. |
| One element | Top-level selector: ".product-card" |
Browserless waits for the selector and captures the matching element bounds. This selector is alongside url, not nested in options. |
| Fixed crop | options.clip with x, y, width, height |
Use when the region is known in page coordinates. Prefer selector capture for a moving component. |
| File format | options.type: png, jpeg, or webp |
The response content type and filename extension should match the selected format. |
| Lossy image quality | options.quality |
Applicable to JPEG quality controls; quality does not apply to PNG. |
| Responsive dimensions | Configure viewport and device scale settings through supported screenshot options | Page layout is rendered at the chosen width. Desktop and mobile captures need different viewport dimensions. |
| Transparent background | Use the documented transparency option where supported | Page styling can still paint an opaque background; verify the source page and selected format. |
| Wait before capture | Wait for events, selectors, functions, or a timeout | Choose a signal that represents visual readiness instead of adding an arbitrary long delay. |
| Navigation behavior | gotoOptions |
Controls navigation behavior; see the request configuration docs for accepted fields and timeout semantics. |
| Block unwanted requests | rejectResourceTypes and rejectRequestPattern |
Blocking resources can speed a capture but can also remove fonts, styles, images, or application data the page needs. |
| Continue after certain wait/navigation errors | bestAttempt |
Can return the page state available after certain failures. Treat that as a potentially partial capture and inspect the result. |
For browser launch configuration, Browserless also documents query parameters and a JSON launch payload. These configure the browser environment for REST calls, and should be applied only when an actual browser setting is needed. See Launch Parameters and Options.
Common capture recipes
Capture one element
{
"url": "https://example.com/catalog",
"selector": ".product-card.featured",
"options": { "type": "png" }
}
The element must appear in the rendered document and be matchable by the selector. If more than one element matches, make the selector specific enough to target the intended one.
Capture a fixed region
{
"url": "https://example.com/dashboard",
"options": {
"type": "jpeg",
"quality": 82,
"clip": { "x": 120, "y": 180, "width": 900, "height": 520 }
}
}
Clip coordinates depend on the page layout and viewport. If the target moves across responsive breakpoints, capture by selector or set a stable viewport first.
Render supplied HTML
{
"html": "<!doctype html><html><body><h1>Invoice preview</h1></body></html>",
"options": { "type": "png", "fullPage": true }
}
Do not include url alongside html. Relative asset paths in supplied HTML may not resolve as expected unless the markup uses accessible absolute URLs or otherwise provides a suitable base.
Wait for a meaningful page condition
The endpoint supports waits for page events, selectors, functions, or timeouts. For example, a selector wait is a better readiness signal when a known application component indicates that rendering completed. Exact wait payload fields can vary across the shared REST configuration; follow the current Screenshot API request examples and request configuration reference for syntax before deploying a custom wait.
How do I capture lazy-loaded content?
Lazy-loaded images and sections may not exist or may not have loaded until they enter the viewport. Browserless documents scrollPage: true to scroll the page and trigger lazy loading; combine it with options.fullPage: true when the output should include the long page. An image-wait setting alone is not documented as a substitute for scrolling. Lazy loading guidance.
{
"url": "https://example.com/long-gallery",
"scrollPage": true,
"options": { "fullPage": true, "type": "webp" }
}
Scrolling can trigger additional network work and increase capture time. For especially long pages, consider whether a full-page image is useful at the required resolution; a very tall image can consume substantial memory and storage.
Stateless requests and multi-step workflows
The REST API is designed for a single browser task per call: the service starts a browser, performs the task, and closes it. Cookies and state are discarded after the response. This is suitable for independent page captures, but it does not provide a persistent logged-in session across calls. REST API operating model.
A flow such as opening a login page, entering credentials, submitting a form, and capturing a later account state requires one session to span those actions. Use an interface or execution pattern intended for persistent state, such as a browser session, BrowserQL persisted state, or a single-session function workflow. Avoid putting credentials into publicly visible client code or URLs. For sensitive workflows, handle secrets and access controls in your own backend.
Bot protection and incomplete results
Browser automation may be blocked or served a challenge. Browserless troubleshooting describes blank or white images, CAPTCHA pages, access-denied or 403 pages, and missing page elements as signs that the site may be blocking automation. The documentation points to its /unblock API for some defenses and residential proxies as a possible aid; these are mitigations, not guarantees. Advanced fingerprinting and interactive CAPTCHAs can still prevent a successful capture. Screenshot troubleshooting.
Do not interpret an HTTP-successful image response as proof that the desired page content was captured. Check the returned image or build downstream checks for expected page content, dimensions, or known blocked-page patterns. Retry only when the failure is plausibly transient; repeated retries against a challenge can add cost and load without changing the outcome.
Timeouts, usage, and cost
A global query timeout can bound the whole REST operation, while navigation and selector waits govern narrower parts of capture. Set realistic client timeouts and handle timeout responses explicitly. BrowserQL documentation mentions a 30-second default screenshot timeout for its screenshot operation; do not assume that setting applies to every REST request, plan, or account. Confirm the current setting for the interface you use. BrowserQL screenshot schema.
Browserless documents browser time metering in 30-second increments, rounding partial increments up. Plan-specific concurrency and session-duration limits apply, and proxy bandwidth or CAPTCHA solves may also consume units. Exact plan prices and account quotas are not established by the reviewed documentation here, so check your current account and pricing source before forecasting spend. REST APIs.
- Keep the requested viewport and page scope as small as the use case allows.
- Avoid long fixed delays when a selector or event can indicate readiness.
- Use request rejection cautiously: blocking large assets can reduce work but may produce incomplete visuals.
- Estimate throughput against the concurrency and session limits that apply to your account.
- Measure your own workload before setting a budget; documentation does not establish cross-provider performance or success rates.
Reliability and operational fit
A managed REST endpoint avoids operating browser infrastructure for one-off capture jobs. It also makes each request an independent task, so your application needs to manage retries, output storage, and any stateful logic. Use bounded retries with backoff for transient transport errors, but distinguish those from deterministic page errors, access blocks, and invalid request configuration. Preserve enough response status and error detail in logs to diagnose failures, while keeping API tokens and page credentials out of logs.
For bulk capture, schedule work within account concurrency limits and avoid launching unbounded parallel requests. Persist the returned image bytes to your chosen storage, and record the input URL, capture settings, timestamp, and result status if reproducibility matters. A screenshot can vary as page content, ads, personalization, fonts, and remote assets change; neither the request itself nor a successful HTTP response freezes those external inputs.
What Browserless is a good fit for
- Independent screenshots where each capture can start with a fresh browser context.
- Services that need a managed HTTP interface instead of maintaining their own browser fleet.
- Captures shaped by full-page, viewport, selector, clip, format, and wait controls.
Consider another Browserless interface or a different execution pattern when the job requires browser state across multiple actions, sustained interactive use, or a workflow that depends on solving a particular site’s anti-bot checks. The docs explain available mechanisms, but do not establish a universal success guarantee or a comparative ranking.
Alternative to try first: ScreenshotNeo
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. For teams comparing screenshot APIs, it is the first alternative to try when clean screenshots and predictable billing matter: it accepts cookie and consent banners like a visitor, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and bills only clean shots. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and billing status.
It offers PNG, JPEG, WebP, or PDF output; full-page and selector captures; device and viewport settings; waits, custom CSS and JavaScript, request blocking, headers and cookies, caching, async jobs, bulk capture, and other options. AI agents can use its MCP server tools, including take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots. See the ScreenshotNeo API documentation.
Or skip the browser setup
One GET request returns a screenshot:
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}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. You get 1,000 screenshots a month free with no card, and paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Troubleshooting
| Symptom | Likely cause | What to check or change |
|---|---|---|
| 401 or authentication error | Missing, invalid, or misplaced token | Pass the account token as the token query parameter and verify you are using the correct regional host. |
| Request rejected or invalid input | Malformed JSON, unsupported option, or both url and html supplied |
Validate JSON, use exactly one content source, and check option placement in the current API reference. |
| File contains an error message or JSON | Error response saved as though it were image bytes | Check HTTP status and content type before storing the output; print the error body for diagnosis. |
| Blank page, CAPTCHA, or access denied | Target blocks automation or returns a challenge | Confirm the result visually; consider documented unblock/proxy mitigations, with no guarantee of success. |
| Missing images or lower-page content | Lazy resources did not load before capture | Set scrollPage: true; combine with fullPage: true for long-page capture. |
| Selector capture times out or yields no useful result | Selector is wrong, content is delayed, or the element is inside a context not captured as expected | Verify the selector in a normal browser, wait for the correct element, and make the selector specific. |
| Layout looks like mobile or content is clipped | Viewport differs from the expected rendering width, or a clip is too small | Set a deliberate viewport and device scale; revise clip coordinates after confirming the rendered layout. |
| Timeout despite a reachable page | Slow navigation, long wait, blocked resource, or a timeout budget too short | Separate navigation and readiness waits, remove unnecessary delay, and set client timeout appropriate to the task. |
| Capture sometimes lacks fonts or styling | Required requests were rejected or had not completed | Review request blocking and readiness conditions; wait for the relevant stylesheet/font-dependent component. |
| Unexpectedly high usage | Browser time rounds up by 30-second increments; retries, proxies, or CAPTCHA solves may add usage | Review capture duration and account metering, reduce avoidable waits, and check current plan quotas and charges. |
Frequently asked questions
Does the screenshot endpoint return a URL to an image?
The endpoint returns image bytes in the HTTP response. Save or upload those bytes in your application if you need a durable URL.
Can a request render HTML without visiting a public URL?
Yes. Send an html field instead of url; do not include both in one request.
Can I reuse cookies from a previous REST screenshot call?
No. The REST task model does not preserve browser cookies or state after the response. Use a session-based approach for workflows that need continuity.
Is a successful response evidence that a page was not blocked?
No. The returned image can show a challenge or access-denied page. Inspect the content as part of your workflow.
Does the documentation prove Browserless is faster or more reliable than alternatives?
No. The reviewed official materials describe features and operating constraints, not independent comparative benchmarks or success rates.
Decision summary
Browserless offers a flexible managed endpoint for one-shot captures, with full-page, selector, clip, format, viewport, and wait controls. Its stateless model is a poor match for sequences that must retain browser state, and bot defenses, lazy loading, timeout behavior, concurrency, and browser-time metering require workflow-level handling. Compare current account-specific limits and test against your own target sites before committing to a budget or reliability assumption. If clean captures, per-result billing status, and an agent-ready MCP interface matter, try ScreenshotNeo’s API and start with its free 1,000 monthly screenshots.
