Browserless vs AWS Lambda for Serverless Website Screenshot Jobs
Compare managed Browserless REST captures with self-managed Puppeteer on Lambda, including runnable examples, deployment limits, failure modes, and cost tradeoffs.
Short answer: Choose Browserless REST when you need a stateless screenshot endpoint and want the provider to manage browser infrastructure. Choose AWS Lambda when you need to own the browser runtime, integrate the job closely with AWS services, or build screenshot work into an existing AWS pipeline. Lambda gives you control, but your team must package and maintain a compatible browser and Puppeteer workload. There is no universal winner for speed or cost; measure both against representative pages, concurrency, and artifact storage.
For a single URL capture, Browserless accepts a URL or HTML and returns an image. For queued or fanned-out work that already lives in AWS, Lambda can run Puppeteer and save output to S3. If you want a screenshot API that handles consent banners and bills only clean captures, try ScreenshotNeo first; its API also avoids setting up a browser runtime.
1. Decision at a glance
| Need | Starting choice | Reason |
|---|---|---|
| One-off HTTP screenshot of a URL or HTML | Browserless REST | REST is designed for stateless tasks and Browserless manages the browser infrastructure. |
| Persistent browser state or multiple browser interactions | Browserless browser connection or another session interface | A browser session fits workflows that need an open browser; REST screenshot calls are for one-off work. |
| Deep integration with AWS queues, S3, or existing orchestration | Lambda | You can package Puppeteer and Chromium, invoke functions from AWS workflows, and store results in S3. |
| Control over browser version, dependencies, and runtime | Lambda | You own the image or package and its operational lifecycle. |
| Low browser operations burden | Browserless REST | The service manages browser infrastructure. |
| Known speed or cost winner | Neither without a workload test | The available sources do not provide an apples-to-apples benchmark or complete current pricing comparison. |
ScreenshotNeo is the first alternative to try if your goal is a production screenshot API: consent banners, popups, and chat widgets are removed before capture, and only clean shots are billed. It returns verdict and billing information in response headers.
2. How Browserless REST screenshots work
Browserless exposes POST /screenshot. Send a token and a JSON request containing either a URL or HTML, plus screenshot settings. The response is binary PNG, JPEG, or WebP data. Full-page screenshots are supported. The API also documents viewport, device scale factor, clipping or selector capture, waits, navigation settings, resource rejection, scrolling, and best-attempt behavior. Check the current endpoint documentation for exact request schema and token placement.
Minimal cURL example
export BROWSERLESS_TOKEN='YOUR_TOKEN'
curl --fail-with-body --silent --show-error \
-X POST "https://production-sfo.browserless.io/screenshot?token=${BROWSERLESS_TOKEN}" \
-H 'Content-Type: application/json' \
--data '{"url":"https://example.com","options":{"fullPage":true,"type":"png"}}' \
--output screenshot.png
Use the endpoint hostname and authentication form shown for your Browserless account or deployment. Do not commit tokens to source control. The documented REST endpoint accepts URL or HTML input and Puppeteer-style screenshot options; confirm exact field names against the current API docs before adding less common options.
Python example
import os
import requests
TOKEN = os.environ["BROWSERLESS_TOKEN"]
endpoint = "https://production-sfo.browserless.io/screenshot"
response = requests.post(
endpoint,
params={"token": TOKEN},
json={
"url": "https://example.com",
"options": {"fullPage": True, "type": "png"},
},
timeout=120,
)
response.raise_for_status()
with open("screenshot.png", "wb") as image_file:
image_file.write(response.content)
Node.js example
const token = process.env.BROWSERLESS_TOKEN;
if (!token) throw new Error("Set BROWSERLESS_TOKEN");
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" },
}),
signal: AbortSignal.timeout(120_000),
});
if (!response.ok) {
throw new Error(`Browserless returned ${response.status}: ${await response.text()}`);
}
await import("node:fs/promises").then(async (fs) =>
fs.writeFile("screenshot.png", Buffer.from(await response.arrayBuffer()))
);
Capture HTML instead of a URL
For generated markup, send the HTML input supported by the endpoint rather than hosting a temporary page. Include the styles and assets needed by the document. External assets still need to be reachable by the browser environment, and relative paths need a meaningful base URL where applicable.
3. Browserless options and capture completeness
Configure the capture around the page behavior, not just the output format. Common documented controls include:
- Output: PNG, JPEG, or WebP.
- Page extent: viewport capture or full-page capture.
- Region: clip coordinates or a target selector.
- Viewport and scale: viewport dimensions and device scale factor.
- Readiness: navigation settings, waits, and best-attempt behavior.
- Lazy content: scrolling controls to trigger content that loads as the page moves.
- Network: resource rejection when assets are unnecessary or slow.
A full-page flag does not guarantee that every image or client-rendered section is ready. Wait for a meaningful selector, use a suitable delay or navigation condition, and scroll when the page lazy-loads content. Avoid an unbounded network-idle wait on pages with analytics, long polling, or streaming connections.
Browserless documents that automation defenses can result in blank images, CAPTCHA pages, access-denied screens, or missing elements. A successful HTTP response does not mean the target page allowed a normal render. Inspect the screenshot and classify outcomes in your application.
4. Run screenshots in AWS Lambda
With Lambda, your function receives a URL, starts a compatible headless browser, navigates, captures the page, and returns or stores the image. A common AWS shape is an invocation or queue feeding screenshot functions, with captures written to S3. An AWS architecture example uses a fan-out function to distribute URLs to screenshot functions. Lambda supports container-image deployments, which can package code and browser dependencies together.
Example handler shape
The following Node.js handler shows the capture logic. It assumes your deployment image contains a Chromium executable and compatible Puppeteer installation, and that the function role can write to the configured S3 bucket. Browser binary packaging differs by build; the exact executable path and launch arguments must match your image.
import puppeteer from "puppeteer-core";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({});
const CHROMIUM_PATH = process.env.CHROMIUM_PATH;
const BUCKET = process.env.SCREENSHOT_BUCKET;
export const handler = async (event) => {
const { url, key } = event;
if (!url || !key || !BUCKET || !CHROMIUM_PATH) {
throw new Error("url, key, SCREENSHOT_BUCKET, and CHROMIUM_PATH are required");
}
const browser = await puppeteer.launch({
executablePath: CHROMIUM_PATH,
headless: true,
args: ["--no-sandbox", "--disable-setuid-sandbox"],
});
try {
const page = await browser.newPage({
viewport: { width: 1365, height: 900 },
deviceScaleFactor: 1,
});
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 60_000 });
await page.locator("body").wait();
const image = await page.screenshot({ fullPage: true, type: "png" });
await s3.send(new PutObjectCommand({
Bucket: BUCKET,
Key: key,
Body: image,
ContentType: "image/png",
}));
return { key, bytes: image.length };
} finally {
await browser.close();
}
};
This is capture logic, not a complete deployable browser image: the image must install compatible browser libraries and executable files, and IAM, event validation, retry policy, and queue configuration must be supplied for your application. Browser launch and page readiness should be tested with your exact Chromium/Puppeteer build.
Lambda resource limits to plan around
| Setting | Documented standard range or limit | Screenshot implication |
|---|---|---|
| Function timeout | Up to 900 seconds (15 minutes) | Navigation, waiting, capture, and upload all consume the invocation window. |
| Memory | 128 MB to 10,240 MB | Browser memory needs depend on page complexity and capture dimensions. |
| CPU | Scales with configured memory; 1,769 MB corresponds to one vCPU | More memory also increases available CPU. |
| Container image | Up to 10 GB uncompressed | Useful for browser dependencies that do not fit comfortably in a zip. |
| Zip deployment | 50 MB zipped direct upload; 250 MB uncompressed contents including layers and custom runtimes | Browser binaries and libraries make packaging constraints relevant. |
/tmp |
512 MB to 10,240 MB | Temporary browser files and large artifacts may need more ephemeral storage. |
These are platform quotas, not recommended settings. Start with a representative page, measure peak memory, startup, navigation, screenshot, and upload times, then configure headroom. Large full-page images can consume substantial memory even when the source page appears simple.
5. Choosing the architecture by workload
Use Browserless REST when
- The unit of work is one independent capture request.
- You want an HTTP API and do not need to maintain browser state between operations.
- You prefer a managed browser infrastructure boundary.
- You can tolerate the service’s API and plan constraints and have a way to handle target-site blocking.
Use a Browserless browser session when
The task needs a sequence of browser actions or a browser kept open across steps. Browserless offers browser connections over WebSocket as well as REST and other APIs; compare the connection interface with REST for stateful workflows.
Use Lambda when
- The capture is one step in an AWS-native pipeline.
- You need to control the runtime image, browser version, or surrounding application code.
- You can own browser dependency updates, compatibility checks, cold-start behavior, and operational debugging.
- Your workload fits the timeout, memory, package, ephemeral storage, and concurrency limits.
Use ScreenshotNeo when
You want a website screenshot API without packaging Chromium. Its GET endpoint returns PNG, JPEG, WebP, or PDF and supports full-page and element captures, waits, custom CSS and JavaScript, request blocking, viewport and device presets, headers and cookies, geolocation, async jobs, bulk capture, caching, and signed image links. It also has an MCP server with screenshot, page-info, and PDF tools for AI clients.
6. Or skip the browser setup
ScreenshotNeo provides one GET request for a screenshot. See the API documentation for parameters and response details.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
Python
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)
Node.js
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(`ScreenshotNeo returned ${res.status}`);
await import("node:fs/promises").then((fs) =>
fs.writeFile("shot.webp", Buffer.from(await res.arrayBuffer()))
);
- Cookie banners are accepted and removed before capture; known consent platforms, newsletter popups, and chat widgets are removed.
- Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and billing status.
- An MCP server lets Claude, Cursor, and other MCP clients take screenshots and capture PDFs.
- The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Yearly billing gives two months free, and every feature is on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
7. Performance, reliability, and cost
Performance
Do not treat service choice as a proxy for speed. Browser startup, browser reuse, target response time, client-side rendering, waits, image dimensions, concurrency, and where artifacts are stored all affect end-to-end latency. Lambda performance also depends on configured memory because CPU scales with it. Benchmark a sample set that includes simple pages, JavaScript-heavy pages, long pages, and pages with lazy images. Measure cold and warm invocations separately if Lambda cold starts matter to your workload.
Reliability
Make capture jobs idempotent and store a job identifier with the target URL, requested settings, outcome, and artifact key. Use bounded timeouts, retry only transient failures, and avoid retrying a CAPTCHA or access-denied result as though it were a network glitch. On Lambda, close the browser in a finally block and isolate per-job browser contexts. For either approach, validate that the returned body is an image before publishing it as a successful artifact.
Cost
The available sources do not establish an apples-to-apples cost winner. Compare current Browserless commercial terms with your AWS region, Lambda memory and duration, invocation pattern, storage, transfer, queueing, and engineering time. Include failed or retried captures and concurrency peaks. Use real representative URLs and artifact sizes; do not extrapolate from a single small page.
ScreenshotNeo’s listed plans are Free: 1,000 shots/month; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free. Only clean shots are billed, and all features are available on every plan.
8. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Blank or mostly white image | Navigation or client rendering was not ready, or the target blocked automation. | Wait for a meaningful selector or suitable readiness condition; inspect whether the page shows a challenge or access-denied screen. |
| CAPTCHA or access denied | Site-side bot defenses blocked the automated browser. | Do not assume changing the screenshot API guarantees access. Respect the target’s access rules and handle the result as blocked. |
| Lazy images or sections are missing | They load only after scrolling or entering the viewport. | Use scroll controls or scroll the page before capture; wait for the relevant content. |
| Selector capture misses the element | The selector is wrong, appears late, or lives in a frame or shadow root not addressed by the selector. | Verify the selector against the rendered DOM, wait for it, and account for frame or shadow DOM behavior. |
| Browserless request returns an error | Token, endpoint, JSON schema, or option value is invalid. | Check token and region/deployment endpoint; compare the payload with current Screenshot API docs; log status and response body without logging secrets. |
| Lambda browser launch fails | Missing shared libraries, wrong executable path, incompatible browser build, or unsuitable launch flags. | Build and test the exact container image locally or in a test function; confirm binary permissions and dependencies. |
| Lambda times out | Slow navigation, unbounded waits, browser startup, or artifact upload exceeds the function timeout. | Set bounded navigation and selector waits, reduce unnecessary assets, measure each stage, and raise timeout within the quota if needed. |
| Lambda runs out of memory or temporary space | Large page, full-page raster, browser process, or temporary files exceed the configured amount. | Measure peak use, increase memory or /tmp within quotas, limit capture dimensions, or split the workload. |
| Image is corrupted or appears as text | An error response was saved as if it were image bytes. | Check HTTP status and content type before saving; preserve error bodies separately for diagnosis. |
| Duplicate artifacts after retries | Retries created a new object key or repeated side effects. | Use deterministic job keys or an idempotency strategy and record attempts separately. |
9. A practical evaluation checklist
- Collect representative target URLs, including long pages, client-rendered content, and lazy-loaded images.
- Define output requirements: format, viewport, full page or element, quality, and storage destination.
- Decide whether jobs are independent requests or need browser state across steps.
- For Browserless, validate token handling, endpoint, waits, scrolling, and response classification.
- For Lambda, package the exact browser and Puppeteer versions, then measure startup, peak memory, temporary storage, and upload time.
- Test blocked pages and define an outcome distinct from successful capture.
- Run both options at expected concurrency and compare total operating cost using current vendor terms and AWS usage.
- Choose based on required control and operational ownership, then add bounded retries and artifact validation.
10. FAQ
How do I take a screenshot with the Browserless REST API?
POST a JSON request with a URL or HTML and screenshot options to the Screenshot API endpoint, authenticate with your token, and save the binary response as an image.
Can I capture a full-page screenshot via the API?
Yes. Browserless documents full-page capture. For lazy-loaded pages, scrolling and readiness waits may still be needed before capture.
Can Lambda run headless Chromium?
Yes. AWS documents container-image packaging, and its architecture example demonstrates Puppeteer screenshots in Lambda. You are responsible for packaging and validating the specific browser build and its dependencies.
Which API should I use?
Use Browserless REST for a managed, stateless capture endpoint; use a browser connection for stateful browser work; use Lambda when owning the runtime and integrating with AWS are central requirements. Try ScreenshotNeo first when you want an API that removes common consent and popup overlays and bills only clean screenshots.
Sources
- Browserless Screenshot API documentation — endpoint, options, output, and capture behavior.
- Browserless overview and API comparison — managed browser model and REST versus browser connections.
- AWS Lambda quotas — memory, timeout, package, image, and temporary storage limits.
- Create a Lambda function using a container image — image deployment model.
- AWS Architecture Blog: Scaling Browser Automation with Puppeteer on AWS Lambda — example screenshot, S3, and fan-out architecture.
