Limits & Billing
Billing model, rate limits, idle timeout, and known limitations
Last updated Jul 6, 2026
Billing
Browser API sessions are billed at 1 credit per 2 minutes, rounded up. A session that lasts 3 minutes costs 2 credits. A session that lasts 4 minutes 1 second also costs 2 credits (you're billed at the start of each 2-minute window).
| Rate | 1 credit / 2 min |
| First credit | Charged on connect (covers minutes 0-2) |
| Subsequent credits | Charged at the start of each new 2-minute window |
| Recording | Free — included in the per-minute rate |
| Geo-targeting | Free — no per-country surcharge |
| Saved-session load | Free — no extra charge for loading cookies |
| Auto-disconnect | When your credit balance reaches 0 |
Limits
| Parameter | Value | Notes |
|---|---|---|
| Max session duration | 2 hours | Hard cap; session ends automatically |
| Idle timeout | ~2 minutes | No CDP messages from client → disconnect |
| Max message size | 50 MB | Per CDP frame |
| Concurrent connections | per-account plan limit | See pricing |
| Authentication | X-API-Key header or ?api_key= query param | Header preferred; query param needed for clients that can't set custom WS headers |
Avoiding the idle timeout
The connection is closed if your script is silent for about 2 minutes. Long workflow pauses (MFA wait, slow API polls, OAuth callbacks) need a keepalive — call any cheap browser API every ~60 seconds:
import asyncio
# during a long wait...
while waiting_for_mfa():
await page.evaluate("() => 0") # cheap keepalive
await asyncio.sleep(60)Playwright's page.wait_for_function() and page.wait_for_selector() also keep the session alive while they're polling — preferred for any "wait until X appears" loop.
The cleanest practice is browser.close() as soon as you're done — don't leave idle connections open just because you can.
Not supported
- Selenium / ChromeDriver — only CDP-native clients (Playwright, Puppeteer, chromedp) are supported.
Error responses
Authentication and capacity errors come back as standard HTTP responses before the WebSocket upgrade, so your CDP client receives them as connection failures. Saved-session errors (404 / 409 / 422) are raised after the upgrade and arrive as a WebSocket close frame with code 1008 whose reason is "<status>: <json body>", e.g. 404: {"error": "session not found, not yet saved, or in use", ...} — Playwright surfaces it in the connectOverCDP error message.
| Status | Error | Cause |
|---|---|---|
| 401 | X-API-Key required | No API key sent |
| 403 | auth rejected: invalid_api_key | API key not recognised |
| 402 | insufficient credits | Credit balance is 0 |
| 404 | session not found | ?session_id / ?session_name doesn't resolve |
| 409 | session is being automated | The session you're loading is in use by another job |
| 422 | session has no stored data | Session was created but never saved |
| 429 | Rate-limited | Too many active connections for this account |
| 503 | browser service unavailable | Transient — retry with backoff |
| 503 | session saving not configured | Saved sessions temporarily unavailable (contact support) |