BrowserCat vs Playwright for Bulk Website Screenshots
Compare self-hosted Playwright with BrowserCat’s managed browsers, then build a reliable bulk screenshot workflow with bounded concurrency, retries, and cost tracking.
Short answer: Playwright is the browser automation framework that takes the screenshots. BrowserCat is a managed cloud-browser service that Playwright can connect to. For a modest batch or a workflow that needs tight control over the browser environment, run Playwright on infrastructure you operate. If you want hosted browser capacity and less browser infrastructure to manage, evaluate BrowserCat while keeping the same Playwright capture code. For a screenshot API that returns images directly without running browser workers, try ScreenshotNeo first: it removes cookie banners, popups, and chat widgets before capture, bills only clean shots, and starts at $5 for 3,000 shots.
This guide shows a runnable bulk capture pattern, explains what changes when you connect to BrowserCat, and covers concurrency, retries, reproducibility, troubleshooting, and cost.
1. What each tool does
| Question | Playwright on your infrastructure | Playwright connected to BrowserCat |
|---|---|---|
| Who takes the screenshot? | Playwright’s Page screenshot API. | Playwright’s Page screenshot API. |
| Who provides the browser runtime? | You provision and maintain it. | BrowserCat provides managed cloud browsers through its connection endpoint. |
| How do you scale? | Bound worker count; Playwright Test also supports sharding independent work across machines. | Use the service’s available capacity and account limits; confirm them for your workload. |
| What is the cost model? | Compute, storage, and operations costs depend on your infrastructure. | Credit-based pricing; successful Utility API requests and WebSocket session time have different credit rules. |
Playwright documents viewport and full-page screenshots in its Page API. BrowserCat’s documentation recommends Playwright and explains how to connect Playwright to its managed service. BrowserCat describes a single endpoint and backend routing on its homepage; treat that as a vendor description, not an independently measured performance result.
There is no independently established head-to-head benchmark here showing BrowserCat is faster than self-hosted Playwright for bulk screenshots. Measure a representative workload before choosing based on speed.
2. Take a batch of screenshots with Playwright
The following Node.js script captures a list of URLs with a fixed concurrency limit. It launches one Chromium browser, creates a fresh page for each URL, writes each screenshot to a unique filename, retries transient failures, and records a result for every URL. This is a useful starting point for a modest batch; tune timeouts and concurrency for the target sites and environment.
Install
npm init -y
npm install playwright
npx playwright install chromium
Save as bulk-screenshots.mjs
import { chromium } from 'playwright';
import { mkdir, writeFile } from 'node:fs/promises';
import { createHash } from 'node:crypto';
const urls = [
'https://example.com',
'https://www.iana.org/domains/reserved',
];
const outputDir = './screenshots';
const concurrency = 3;
const maxAttempts = 3;
const timeoutMs = 45_000;
function fileName(url) {
const host = new URL(url).hostname.replace(/[^a-z0-9.-]/gi, '_');
const id = createHash('sha256').update(url).digest('hex').slice(0, 10);
return `${host}-${id}.png`;
}
async function capture(browser, url) {
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const context = await browser.newContext({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1,
});
try {
const page = await context.newPage();
page.setDefaultNavigationTimeout(timeoutMs);
const response = await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: timeoutMs,
});
if (response && response.status() >= 400) {
throw new Error(`HTTP ${response.status()}`);
}
await page.screenshot({
path: `${outputDir}/${fileName(url)}`,
fullPage: true,
animations: 'disabled',
});
return { url, ok: true, status: response?.status() ?? null, attempt };
} catch (error) {
lastError = error;
if (attempt < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 500 * 2 ** (attempt - 1)));
}
} finally {
await context.close();
}
}
return { url, ok: false, error: String(lastError) };
}
async function runPool(items, limit, worker) {
const results = new Array(items.length);
let next = 0;
async function runWorker() {
while (true) {
const index = next++;
if (index >= items.length) return;
results[index] = await worker(items[index]);
}
}
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, runWorker));
return results;
}
await mkdir(outputDir, { recursive: true });
const browser = await chromium.launch({ headless: true });
try {
const results = await runPool(urls, concurrency, url => capture(browser, url));
await writeFile(`${outputDir}/results.json`, JSON.stringify(results, null, 2));
console.log(`Captured ${results.filter(result => result.ok).length}/${results.length}`);
} finally {
await browser.close();
}
Run it with node bulk-screenshots.mjs. The script uses domcontentloaded so it does not wait indefinitely for pages with long-lived network activity. If the target pages need more client-side rendering, choose an explicit selector wait or a short delay for that site rather than assuming a single readiness event works everywhere.
Useful screenshot options
fullPage: truecaptures the full scrollable page; omit it for the current viewport.pathwrites an image file. Omitpathto receive screenshot bytes, then upload or process the buffer.typeselectspng,jpeg, orwebpwhere supported; JPEG and WebP can reduce output size. Setqualityfor lossy formats.clipcaptures a defined rectangle. Uselocator(selector).screenshot()to capture one element.animations: 'disabled'helps reduce animation differences. You can also setcaret: 'hide'and mask dynamic elements when creating visual baselines.- Set viewport, device scale factor, color scheme, locale, timezone, and any required browser context options explicitly to keep captures comparable.
See the full Playwright Page API for current screenshot and navigation options.
3. Connect the same capture workflow to BrowserCat
BrowserCat changes where the browser runs; your Playwright page navigation and screenshot calls remain the capture logic. Follow BrowserCat’s current quick start and Playwright connection guide to obtain the endpoint and credentials for your account. The connection endpoint and authentication are account-specific, so do not copy an invented URL into production code.
The integration shape is:
import { chromium } from 'playwright';
// Set BROWSERCAT_PLAYWRIGHT_ENDPOINT to the endpoint and credentials
// provided in your BrowserCat account and current documentation.
const browser = await chromium.connectOverCDP(
process.env.BROWSERCAT_PLAYWRIGHT_ENDPOINT
);
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.screenshot({ path: 'example.png', fullPage: true });
} finally {
await browser.close();
}
Check the BrowserCat guide for the exact supported connection method, endpoint format, session lifecycle, and plan limits for your account. For a bulk job, adapt the pool above so each worker creates and closes a page or context according to the service’s documented session model. Do not assume that a displayed maximum concurrency is a sustainable throughput target for arbitrary full-page captures.
4. Choose based on operational needs
Run Playwright yourself when
- You need direct control over the operating system, browser build, installed fonts, runtime flags, or network environment.
- You already operate browser workers and can absorb browser updates, capacity planning, and failure recovery.
- Your workload benefits from running near your application, data, or private network resources.
Evaluate BrowserCat when
- You want managed cloud-browser infrastructure and are willing to trade some environment control for less infrastructure to operate.
- You need to scale browser execution without building the full browser fleet yourself.
- The account’s available regions, configuration, limits, and billing model fit your workload.
BrowserCat’s Playwright Testing guide reports that it has seen teams save up to 75 minutes per deploy by parallelizing tests on its fleet. That is BrowserCat’s reported experience, not an independent benchmark and not a prediction for a screenshot batch. See its guide for context.
5. Make bulk capture reliable
- Keep inputs independent. Give each URL a stable output name and store its result separately. Avoid shared browser state unless the workflow specifically requires a shared login.
- Bound concurrency. Start low, observe memory, CPU, browser crashes, target-site errors, and completion time, then adjust. More parallel pages can increase contention and site rate limiting.
- Retry selectively. Retry navigation timeouts and transient connection failures with backoff. Do not repeatedly retry deterministic errors such as an invalid URL, a consistent 404, or a bad selector.
- Record outcomes. Store URL, timestamp, status code, duration, attempt count, error, screenshot path, viewport, and browser version. This makes partial batches resumable.
- Isolate state. Use a separate context per task when cookies, local storage, or authentication could leak between pages. If authenticated captures are required, provision the right state deliberately and protect credentials.
- Set a capture readiness rule. Choose an appropriate navigation milestone, selector wait, or bounded delay. A page’s load event may never settle because analytics, ads, or streaming requests remain active.
- Make runs resumable. Skip successful outputs only when their metadata matches the requested URL and capture settings. Write to a temporary file and rename after success to avoid treating truncated output as complete.
Playwright Test supports configurable worker processes and parallel execution. It can also shard eligible work across machines. Parallel workers do not share state, so keep test data and file outputs isolated. Those Test-runner features are useful if screenshots are part of a test suite; a standalone capture script can use its own bounded pool as above.
6. Keep screenshots reproducible
Visual output can vary with host operating system, browser version, browser settings, hardware, and headless mode. Playwright calls out these factors in its guidance on visual comparisons. For meaningful comparisons:
- Pin the Playwright version and browser binaries used by the job.
- Use a consistent operating system and font set.
- Fix viewport size, device scale factor, locale, timezone, color scheme, and reduced-motion preference.
- Disable or mask animations, cursors, timestamps, rotating content, and other dynamic regions when the goal is a stable baseline.
- Capture under the same authentication and cookie state.
- Record these settings alongside each output; do not compare images from unlike environments as if the capture engine were the only difference.
7. Estimate performance and cost
There is no universal screenshot-per-second figure for arbitrary websites. Page weight, scripts, lazy-loaded content, full-page height, network conditions, browser startup, and the chosen readiness condition all affect duration. Measure a representative sample, including slow and tall pages, at the concurrency you plan to use.
Track median and tail latency, successful capture rate, retries, browser time, memory use, output size, and total cost. For self-managed Playwright, account for compute, storage, network egress, and engineering time spent operating workers. For BrowserCat, its pricing page describes monthly credits and different rules: successful Utility API requests cost one credit each, while WebSocket sessions are charged in 30-second increments. The page currently lists a free Hobby plan with 1,000 monthly credits and displays 1,000 concurrent requests on its tiers. These are time-sensitive plan details; check the live page and your account terms. Do not turn credits into a screenshot count without measuring the actual execution path and session duration.
To compare fairly, run the same representative URLs, browser settings, viewport sizes, and full-page rules; record completion rate, latency distribution, output consistency, browser time, and total cost. Include the cost of operating your own workers when comparing against a managed service.
8. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Navigation timeout | The site is slow, the timeout is too short, or a chosen readiness event waits on persistent requests. | Use a bounded timeout and an earlier milestone such as domcontentloaded; wait for a specific selector if needed. Retry only transient failures. |
| Screenshot is blank or incomplete | Capture ran before client rendering or lazy content finished. | Wait for a meaningful selector or known rendering condition. For full-page screenshots, verify lazy-loaded sections on representative pages. |
| Different images between runs | Browser, OS, fonts, viewport, device scale, or dynamic page content changed. | Pin the environment and settings, and mask or disable dynamic regions where appropriate. |
| Browser process crashes or machine runs out of memory | Too many large pages are open at once, especially for long full-page captures. | Lower concurrency, close contexts promptly, and monitor memory. Consider chunking the batch. |
| Files overwrite each other | Output paths are derived from a non-unique label or tasks share a path. | Use a stable unique key, such as a URL hash, and keep per-task metadata. |
| 429 or blocked navigation | The target site is rate limiting or rejecting automated traffic. | Reduce concurrency, add spacing, and follow the site’s access rules. Do not treat retries as a way to bypass access controls. |
| BrowserCat connection fails | Endpoint, credentials, supported connection method, or account limits do not match the current setup. | Recheck the endpoint and connection instructions in BrowserCat’s current Playwright guide, verify account access, and test one session before a batch. |
| Batch partially completes | A worker or host failed while other tasks were running. | Persist per-URL results as tasks finish and resume only failed or missing entries. |
9. Or skip the browser setup
If your job is to get screenshots rather than operate browser workers, ScreenshotNeo is a screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture.
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for parameters and response details. It supports full-page and element captures, device presets and custom viewports, PDF settings, custom CSS and JavaScript, selector and network-idle waits, headers and cookies, geolocation and timezone, request blocking, resizing, cache TTL, signed public image links, asynchronous jobs with signed webhooks, bulk requests of up to 100 URLs, usage reporting, and an OpenAPI spec. The parameter names used by other screenshot APIs also work to ease migration.
ScreenshotNeo offers 1,000 screenshots a month free with no card. Paid plans start at $5 for 3,000 shots; higher tiers are $15 for 15,000, $39 for 60,000, $99 for 250,000, and $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card required.
10. Frequently asked questions
Is BrowserCat a replacement for Playwright?
No. Playwright performs browser automation and capture; BrowserCat supplies managed browser infrastructure that Playwright can connect to.
Can I use Playwright Test for screenshots?
Yes. It is useful when captures are part of parallelizable tests, with worker counts and sharding. For a simple URL list, a bounded standalone worker pool may be more direct.
Which option gives more consistent visual output?
Self-managed infrastructure gives more control over the rendering environment. With either approach, consistency depends on keeping browser, operating system, fonts, settings, and page state stable.
How many screenshots can I run concurrently?
There is no single safe number. Start with a small cap and measure the workload, resource use, completion rate, and service or target-site limits before increasing it.
Does BrowserCat have a free plan?
Its pricing page currently lists a Hobby plan with 1,000 monthly credits. Check the live pricing page for current terms and how your session type consumes credits.
