Robust browser session management for Playwright & Browser.cash.
Powered by Browser.cash remote browsers.
Features β’ Installation β’ Usage β’ Configuration β’ Contributing
License Node.js Version TypeScript Visit Browser.cash
Follow on X Follow on LinkedIn Join our Discord
π‘ Pro Tip: See this library in action in Teracrawl and Browser SERP.
Browser Pool is a specialized library designed to manage pools of remote browser sessions. It handles the lifecycle of Playwright browsers connected to Browser.cash, ensuring your application always has a healthy browser ready to perform tasks.
It abstracts away the complexity of connection management, error recovery, and session recycling, making it ideal for building high-concurrency scrapers and automation tools.
- Automatic Pooling: Maintains a fixed number of active browser sessions.
- Target-Aware Pooling: Specify exactly how many browsers you want per node, country, or node type.
- Self-Healing: Automatically detects and replaces dead or disconnected browsers.
- Health Checks: Periodically verifies browser responsiveness.
- Concurrency Control: Queues requests when all sessions are busy.
- Type-Safe: Written in TypeScript with full type definitions.
npm install @browsercash/pool
Note: You must also have playwright-core installed as a peer dependency.
import { chromium } from "playwright-core"; import { SessionPool, type PoolConfig } from "@browsercash/pool"; const cfg: PoolConfig = { apiKey: process.env.BROWSER_API_KEY, chromium, targets: [ { id: "us-hosted", count: 2, country: "US", type: "hosted" }, { id: "de-consumer", count: 1, country: "DE", type: "consumer_distributed" }, { id: "pinned-node", count: 1, nodeId: "node_123" }, ], enableHealthCheck: true, waitQueueTimeoutMs: 30_000, }; // 1. Create the pool const pool = new SessionPool(cfg); // 2. Initialize await pool.init(); // 3. Acquire a session (waits if none available) const session = await pool.acquire(); try { // Use the standard Playwright browser instance const page = await session.browser.newPage(); await page.goto("https://example.com"); console.log(await page.title()); } finally { // 4. Always release the session back to the pool // Pass 'true' as second arg if the session encountered a fatal error pool.release(session); } // 5. Cleanup on shutdown await pool.shutdown();
There is also a dedicated example config file in
examples/browser-pool.config.mjs.
If you just want the old behavior, size still works:
const pool = new SessionPool({ apiKey: process.env.BROWSER_API_KEY, chromium, size: 3, });
import { chromium } from "playwright-core"; import { type PoolConfig } from "@browsercash/pool"; export const browserPoolCfg: PoolConfig = { apiKey: process.env.BROWSER_API_KEY!, chromium, targets: [ { id: "us-primary", count: 2, country: "US", type: "hosted" }, { id: "eu-fallback", count: 2, country: "DE", type: "consumer_distributed" }, { id: "pinned-fraud-node", count: 1, nodeId: "node_123" }, ], maxUses: 25, maxAgeMs: 10 * 60 * 1000, maxIdleMs: null, enableHealthCheck: true, healthCheckIntervalMs: 30_000, sessionReadyTimeoutMs: 30_000, cdpConnectTimeoutMs: 15_000, waitQueueTimeoutMs: 60_000, };
Equivalent standalone file:
// examples/browser-pool.config.mjs import { chromium } from "playwright-core"; export const browserPoolCfg = { apiKey: process.env.BROWSER_API_KEY, chromium, targets: [ { id: "de-consumer", count: 1, country: "DE", type: "consumer_distributed" }, ], maxUses: 25, maxAgeMs: 10 * 60 * 1000, maxIdleMs: null, enableHealthCheck: true, healthCheckIntervalMs: 30_000, sessionReadyTimeoutMs: 30_000, cdpConnectTimeoutMs: 15_000, waitQueueTimeoutMs: 60_000, };
For a larger production mix, expand targets with more entries such as hosted US nodes or pinned nodeId targets.
| Option | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
Required | Your Browser.cash API key. |
chromium |
ChromiumModule |
Required | The Playwright Chromium module. |
size |
number |
1 |
Backward-compatible shortcut for a single default target group. |
targets |
PoolTarget[] |
[] |
Explicit target mix. Sum of count values becomes the pool size. |
maxUses |
number |
50 |
Max times a browser is reused before recycling. |
maxAgeMs |
number |
300000 |
Max age (ms) of a session before recycling. |
maxIdleMs |
number | null |
null |
Max idle time before recycling. null or 0 disables idle recycling. |
enableHealthCheck |
boolean |
false |
Enable background remote health checks. |
healthCheckIntervalMs |
number |
30000 |
Interval for health checks (ms). |
healthCheckTimeoutMs |
number |
min(interval, 10000) |
Timeout for each health-check request. |
sessionReadyTimeoutMs |
number |
20000 |
How long to wait for Browser.cash to return a usable CDP URL. |
cdpConnectTimeoutMs |
number |
15000 |
Timeout for Playwright connectOverCDP. |
enableWaitQueue |
boolean |
true |
Queue acquire requests if pool is full. |
waitQueueTimeoutMs |
number |
60000 |
Max time an acquire call will wait in queue. |
enableDisconnectHandling |
boolean |
true |
Replace sessions when the CDP connection disconnects. |
createPage |
boolean |
false |
Pre-create a page for each pooled session. |
debug |
boolean |
false |
Enable verbose logging. |
logger |
(message, data) => void |
console.log |
Custom logger used when debug is enabled. |
type PoolTarget = { id?: string; count: number; nodeId?: string; country?: string; type?: "consumer_distributed" | "hosted"; sessionOptions?: Record<string, unknown>; };
- Use
countto pin how many sessions should exist for that target. - Use
nodeIdwhen you want a specific Browser.cash node. - Use
countryandtypewhen you want the pool to maintain a regional mix. - Use
sessionOptionsas a pass-through for newer Browser.cash session-create fields.
Contributions are welcome! We appreciate your help in making Browser Pool better.
- Fork the Project: click the 'Fork' button at the top right of this page.
- Create your Feature Branch:
git checkout -b feature/AmazingFeature - Commit your Changes:
git commit -m 'Add some AmazingFeature' - Push to the Branch:
git push origin feature/AmazingFeature - Open a Pull Request: Submit your changes for review.
This project is licensed under the MIT License - see the LICENSE file for details.