API reference
The full REST surface at https://sessions.browserview.io. Lifecycle endpoints take your API key; per-session traffic takes a scoped session token.
Authentication
Send your API key on every lifecycle request, either as a bearer token or an x-api-key header:
Authorization: Bearer bv_live_...# orx-api-key: bv_live_...Keys are minted in the console and can be revoked there at any time; revocation takes effect within about a minute. Your key sees only your own sessions — a session that isn't yours is a 404, never a 403.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /sessions | Create a session (201) |
GET | /sessions | List your live sessions; filter with ?metadata.<key>=<value> |
GET | /sessions/{id} | Inspect one session (returns fresh URLs and tokens) |
DELETE | /sessions/{id} | Destroy a session (204) |
POST | /sessions/{id}/release | Gracefully end a session (202, idempotent; end_reason released) |
PATCH | /sessions/{id} | Adjust timeout_seconds / idle_timeout_seconds / keep_alive on a running session |
GET | /sessions/{id}/debug | DevTools discovery: debugger_url, ws_url, and per-page debugger URLs |
POST | /sessions/{id}/tokens | Mint an extra scoped token |
GET | /sessions/{id}/screenshot | Capture the current viewport as PNG / JPEG / WebP |
GET | /sessions/{id}/downloads | List files the browser downloaded (sessions created with downloads: true) |
GET | /sessions/{id}/downloads/{name} | Fetch one downloaded file (works after destruction) |
POST | /sessions/{id}/files | Upload a file into the session for <input type=file> (201) |
GET | /sessions/{id}/captcha | Automatic CAPTCHA solver status (sessions created with solve_captchas: true) |
POST | /sessions/{id}/captcha/solve | Solve a CAPTCHA you located yourself and get the token back (paid plans) |
GET | /proxies/countries | Countries available for managed residential proxies (no auth) |
GET | /sessions/{id}/replay | Replay manifest for a recorded session (works after destruction) |
POST | /sessions/{id}/agent/messages | Send the hosted AI agent a task; streams SSE events (paid plans, sessions created with agent: true) |
GET | /sessions/{id}/agent | Agent state: idle | running | needs_input, usage totals, pending question |
GET | /sessions/{id}/agent/events | Replay or tail the agent's event log (?after=N&follow=1) |
POST | /sessions/{id}/agent/reply | Answer a question the agent asked (needs_input) |
POST | /sessions/{id}/agent/interrupt | Stop the running turn |
DELETE | /sessions/{id}/agent | Reset the agent conversation (204) |
GET | /agent/models | Models the hosted agent can run, with list prices |
GET | /healthz | Liveness probe, no auth |
Create a session
Every field is optional:
{ "start_url": "https://example.com", // http(s) URL or "about:blank" (default) "width": 1280, // 320–3840, default 1280 "height": 800, // 240–2160, default 800 "wait": true, // block until the browser answers on CDP "record": false, // capture a session replay // lifecycle (see /docs/lifecycle) "timeout_seconds": 1800, // hard lifetime cap, clamped to the plan (alias: max_lifetime_seconds) "idle_timeout_seconds": 300, // end after N idle seconds; 0 disables (default 900) "keep_alive": true, // false = end 30s after the last client disconnects "region": "us-east-1", // placement; the only value today // stealth + egress (see /docs/stealth, /docs/proxies, /docs/captcha) "stealth": true, // honest Linux Chrome with a per-session fingerprint profile "fingerprint": { "screen": { "width": 1920, "height": 1080 }, "hardware_concurrency": 8, "device_memory": 8 }, "proxies": true, // managed residential exit; or { country, state, city, type, sticky } "proxy": { "server": "http://proxy:8080", "username": "u", "password": "p", "bypass": "*.internal" }, // BYO; exclusive with proxies "solve_captchas": false, // auto-detect + solve reCAPTCHA / hCaptcha / Turnstile (paid plans) "record_console": false, // stealth sessions: also record the Console/Runtime domains // browser environment "user_agent": "Mozilla/5.0 ...", // must be Chrome, same major as the image, Linux x86_64 "locale": "en-US", "timezone": "America/New_York", // IANA name (defaults follow the proxy exit country) "geolocation": { "lat": 40.71, "lon": -74.0, "accuracy": 50 }, // persistence + files "context_id": "acme-crm", // restore this context's cookies; archive them back on end "downloads": false, // enable the downloads/uploads endpoints for this session // labels, searchable via GET /sessions?metadata.key=value "metadata": { "job": "crawl-42", "env": "staging" }}proxy.serverisscheme://host[:port]with schemehttp,https,socks5, orsocks4. Credentials are answered over CDP and never appear on a command line or in the session object (they come back redacted).context_id(letters, digits,. _ -, 1–64 chars) names a persistent context owned by your account: cookies saved at the end of one session are restored at the start of the next session using the same id — logins survive across disposable browsers.metadata: up to 16 keys ([A-Za-z0-9._-], ≤64 chars), string values ≤256 chars, 2 KB total.timeout_seconds(and its aliasmax_lifetime_seconds) is clamped to your plan's per-session cap (15 minutes on Free, 4 hours on paid plans).proxiesandsolve_captchasneed a paid plan (403 proxies_not_on_plan/403 captcha_not_on_plan); once the included proxy pool is used with no overage available the create is a402 proxy_quota_exhausted. Passing bothproxiesandproxyis a400.
Returns 201 with the session object. With "wait": true (the default) the call returns once the browser answers on DevTools — typically a few seconds; if it hasn't within 60 seconds the session is torn down and you get a 503. Validation problems (unsupported start_url scheme, bad locale or timezone, oversized metadata) are a 400.
The session object
{ "id": "3f9c62d81b4a", "status": "running", // pending | running | completed | timed_out | error "health": "healthy", // starting | healthy | unhealthy | unknown "end_reason": null, // api | lifetime | idle | dead | unhealthy | sweep | released | client_disconnect "start_url": "https://example.com", "width": 1280, "height": 800, "created_at": "2026-07-29T17:04:05Z", "expires_at": "2026-07-29T17:34:05Z", // created_at + lifetime "idle_timeout_seconds": 300, "keep_alive": true, "region": "us-east-1", "record": false, // whether a replay is being captured "max_lifetime_seconds": 14400, // plan cap applied at create (0 = none) "requested_lifetime_seconds": 1800, // your timeout_seconds, clamped (0 = none) "config": { // the per-session options that were applied "stealth": true, "fingerprint": { "screen": { "width": 1920, "height": 1080 }, "hardware_concurrency": 8, "device_memory": 8, "timezone": "America/New_York", "locale": "en-US" }, "proxies": { "country": "US", "type": "residential", "sticky": true }, "solve_captchas": true, "locale": "en-US" }, "usage": { "duration_seconds": 412, "proxy_bytes": 1048576, "captcha_solves": 0 }, // live; final on completion "metadata": { "job": "crawl-42" }, "viewer_url": "/sessions/3f9c62d81b4a/viewer?token=...", // control scope "watch_url": "/sessions/3f9c62d81b4a/viewer?token=...", // view scope "cdp_url": "/sessions/3f9c62d81b4a/cdp", // no token embedded "cdp_token": "...", "token_ttl_seconds": 3600}The URL fields are paths relative to https://sessions.browserview.io (the SDKs absolutize them for you). config only contains the options you set. GET /sessions returns a bare JSON array and omits the URL and token fields; fetch a single session to get fresh ones. GET /sessions/{id} additionally reports browser health: restarts (times the in-session browser restarted, or null when the status probe is unreachable) and degraded (true once it has restarted). DELETE /sessions/{id} returns 204 with no body.
Mint a token
{ "scope": "view", // view | control | cdp | agent "ttl_seconds": 900 // default 3600, up to 604800 (7 days)} // 200 → { "token": "...", "scope": "view", "ttl_seconds": 900 }Screenshot
// format: png (default) | jpeg | webp quality: 1–100 (jpeg/webp only, default 80)// 200 → image bytes (Content-Type image/png | image/jpeg | image/webp)// 409 → the session has no page to screenshot yetScreenshots are rate-limited to 10 per minute per IP. For continuous observation use the live viewer or your own CDP connection instead.
Downloads and uploads
Create the session with "downloads": true and files the browser saves land in a per-session store that outlives the session.
GET /sessions/{id}/downloads// 200 → { "session_id": "...", "files": [ { "name": "report.pdf", "size_bytes": 48213, "modified_ms": 1754038923000 } ] } GET /sessions/{id}/downloads/report.pdf// 200 → the file (Content-Disposition: attachment), or a 302 to a short-lived signed URL POST /sessions/{id}/files (multipart/form-data, field "file"; live session only)// 201 → { "name": "avatar.png", "path": "/downloads/avatar.png", "size_bytes": 10240 }// then over CDP: DOM.setFileInputFiles({ files: ["/downloads/avatar.png"], nodeId })Uploads are capped at 256 MiB per file and return 413 beyond that; uploading to a session that wasn't created with downloads enabled is a 400.
Lifecycle: release and update
POST /sessions/{id}/release// 202 (idempotent) → graceful end; end_reason "released"; replays finalize, webhooks fire as usual PATCH /sessions/{id}{ "timeout_seconds": 7200, "idle_timeout_seconds": 0, "keep_alive": false } // each optional// 200 → the updated session object (values clamped to the plan caps)The full model — the status enum, idle and lifetime timers, keep-alive semantics — is on the session lifecycle page.
Debug endpoint
{ "debugger_url": "https://sessions.browserview.io/sessions/3f9c62d81b4a/devtools/...?token=...", "ws_url": "wss://sessions.browserview.io/sessions/3f9c62d81b4a/cdp?token=...", "pages": [ { "id": "A1B2...", "url": "https://example.com", "title": "Example Domain", "debugger_url": "https://sessions.browserview.io/sessions/3f9c62d81b4a/devtools/...?token=..." } ]}A proxied view of Chrome's /json/list: open a page's debugger_url in a browser for full DevTools against the live session, or hand ws_url to a raw CDP client. Every URL carries a short-lived cdp-scope token; call again for fresh ones.
CAPTCHA solving
Sessions created with solve_captchas: true (paid plans) detect and solve reCAPTCHA v2/v3/Enterprise, hCaptcha, and Turnstile on their own, logging browserview-solving-started / browserview-solving-finished to the page console and emitting captcha.* session events. See the CAPTCHA guide.
GET /sessions/{id}/captcha// 200 → { "enabled": true, "status": "idle" | "detected" | "solving" | "solved" | "failed",// "type": "recaptcha_v2", "last_error": null, "solves": 1, "updated_at": "..." } POST /sessions/{id}/captcha/solve // manual: you found the sitekey{ "type": "recaptcha_v2", // recaptcha_v2 | recaptcha_v3 | recaptcha_enterprise | hcaptcha | turnstile "sitekey": "6Le...", "url": "https://example.com/login", "action": "login" // recaptcha_v3 only}// 200 → { "token": "<solution>", "type": "recaptcha_v2" } inject it into the page yourself// 403 captcha_not_on_plan → Free plan; 502 → the provider could not solve itProxy countries
// no auth, cached{ "countries": [ { "code": "US", "name": "United States", "timezone": "America/New_York", "locale": "en-US" }, ... ] }Fetch a replay
// while the session runs → 200 { "status": "recording", "session_id": "..." }// not created with record: true → 404 "session is not being recorded"// just ended, still finalizing → 404 — not an error; retry every few seconds// once finalized → 200 with the full manifest:{ "status": "ready", "end_reason": "api", // api | idle | lifetime | dead | unhealthy | sweep | released | client_disconnect "video": { "url": "...", "format": "webm", "codec": "vp8", "start_time_ms": ..., "duration_ms": ..., "size_bytes": ... }, "pages": [ { "page_id": "p_000", "url": "...", "start_time_ms": ..., "end_time_ms": ... } ], "events": { "actions": {...}, "console": {...}, "network": {...}, "errors": {...} }, "urls_expire_at_ms": ...}Only sessions created with record: true have replays. The SDKs' waitForReplay / wait_for_replay helpers poll through the recording and finalizing states for you. See the session replay guide for stream formats and timeline math.
Hosted AI agent
Sessions created with agent: true on a paid plan expose the /agent endpoints above: send a task, stream its reasoning, steps, and answer as server-sent events, answer its questions, or stop it. Bodies, the event table, and credit pricing are documented on the AI agent (Pilot) page.
Token scopes
| Scope | Grants |
|---|---|
view | Watch the live stream. Input is refused server-side — spectators physically cannot click or type. |
control | Watch plus full mouse and keyboard. |
cdp | Chrome DevTools Protocol access for Playwright, Puppeteer, or raw CDP clients. |
Tokens are signed, scoped to one session, and expire (one hour by default). Pass them as the x-session-token header or ?token= query parameter. A bad, expired, or wrong-scope token is a 403; a missing one is a 401.
Connecting over CDP
The API rewrites Chromium's discovery JSON so webSocketDebuggerUrl points back through the proxy with your token attached — Playwright and Puppeteer work with no special configuration beyond the header. Always use connectOverCDP (not connect) so you attach to the session's existing context. For stealth sessions we recommend Patchright, a drop-in Playwright fork that avoids the Runtime.enable automation tell:
// Playwright (or: import { chromium } from "patchright")const browser = await chromium.connectOverCDP(cdpUrl, { headers: { "x-session-token": cdpToken },}); // Puppeteerconst browser = await puppeteer.connect({ browserURL: cdpUrl + "?token=" + cdpToken,});Sessions are placed in the region you pass at create (region, currently only us-east-1); the CDP endpoint is always the single base URL above.
Webhooks
Register HTTPS endpoints under Settings in the console to be notified of session lifecycle events — up to 20 endpoints per account, each subscribed to any subset of the event types (or all of them).
| Event | When | data |
|---|---|---|
session.created | A session is first observed | session_id, start_url, width, height, created_at, record |
session.ended | The session is destroyed or reaped | session_id, start_url, width, height, started_at_ms, ended_at_ms, end_reason |
replay.ready | A recorded session's replay finished finalizing | session_id, duration_ms, video_bytes, page_count, ended_at_ms |
POST https://your-endpoint.example.com/hookscontent-type: application/jsonuser-agent: browserview-webhooks/1x-browserview-signature: t=1754038923,v1=<hex hmac-sha256> { "id": "<uuid>", "type": "session.ended", "created": 1754038923, "data": { ... } } // verify: hmac_sha256(secret, `${t}.${rawBody}`) === v1, and |now - t| < 300sThe signing secret (whsec_…) is shown once when the endpoint is created. Any 2xx counts as delivered; anything else (including redirects) is retried with exponential backoff for up to 12 attempts. Delivery history is visible per endpoint in the console. Destinations must be public HTTPS hosts.
Errors and rate limits
Errors are JSON: {"detail": "..."} (schema validation errors use FastAPI's 422 list form).
| Status | Meaning |
|---|---|
| 400 | Invalid option value (start_url scheme, locale, timezone, metadata caps, unknown region, non-Chrome user_agent, proxies + proxy together, downloads/uploads not enabled for the session). |
| 402 | Managed proxies refused: the included pool is used with no overage available (proxy_quota_exhausted) or the subscription is past due (payment_failed). |
| 401 | Missing or invalid API key, or missing session token. |
| 403 | Bad, expired, or wrong-scope session token — or a plan limit on create: included browser-hours exhausted on the free tier, a failed payment, or proxies / solve_captchas on a plan without them (proxies_not_on_plan, captcha_not_on_plan). Upgrade or manage billing in the console. |
| 404 | Session not found (or not yours); replay not ready yet; no downloads for this session. |
| 409 | Screenshot requested before the session has a page. |
| 413 | Upload larger than 256 MiB. |
| 422 | Request failed schema validation (width out of range, unknown token scope, …). |
| 429 | Rate limit or capacity: your plan's concurrent-session limit, or the fleet is momentarily full. Honor the Retry-After header (30–60s). |
| 502 | The session failed to start, or an in-session operation (screenshot, upload, CAPTCHA solve) failed. |
| 503 | Authentication service briefly unavailable, recording/downloads storage temporarily unavailable, no managed-proxy provider configured (proxies_unavailable), or the browser did not become ready in time — retry. |
Default limits: 120 requests per minute per IP, and 10 session creates per minute (per IP and per account). Repeated authentication failures from one IP are throttled to 429. 429 and 503 are always safe to retry — the server never commits a session before returning them. Liveness is exposed at GET https://sessions.browserview.io/healthz.