Reference

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

MethodPathPurpose
POST/sessionsCreate a session (201)
GET/sessionsList 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}/releaseGracefully 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}/debugDevTools discovery: debugger_url, ws_url, and per-page debugger URLs
POST/sessions/{id}/tokensMint an extra scoped token
GET/sessions/{id}/screenshotCapture the current viewport as PNG / JPEG / WebP
GET/sessions/{id}/downloadsList files the browser downloaded (sessions created with downloads: true)
GET/sessions/{id}/downloads/{name}Fetch one downloaded file (works after destruction)
POST/sessions/{id}/filesUpload a file into the session for <input type=file> (201)
GET/sessions/{id}/captchaAutomatic CAPTCHA solver status (sessions created with solve_captchas: true)
POST/sessions/{id}/captcha/solveSolve a CAPTCHA you located yourself and get the token back (paid plans)
GET/proxies/countriesCountries available for managed residential proxies (no auth)
GET/sessions/{id}/replayReplay manifest for a recorded session (works after destruction)
POST/sessions/{id}/agent/messagesSend the hosted AI agent a task; streams SSE events (paid plans, sessions created with agent: true)
GET/sessions/{id}/agentAgent state: idle | running | needs_input, usage totals, pending question
GET/sessions/{id}/agent/eventsReplay or tail the agent's event log (?after=N&follow=1)
POST/sessions/{id}/agent/replyAnswer a question the agent asked (needs_input)
POST/sessions/{id}/agent/interruptStop the running turn
DELETE/sessions/{id}/agentReset the agent conversation (204)
GET/agent/modelsModels the hosted agent can run, with list prices
GET/healthzLiveness probe, no auth

Create a session

Every field is optional:

POST /sessions
{  "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.server is scheme://host[:port] with scheme http, https, socks5, or socks4. 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 alias max_lifetime_seconds) is clamped to your plan's per-session cap (15 minutes on Free, 4 hours on paid plans).
  • proxies and solve_captchas need 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 a 402 proxy_quota_exhausted. Passing both proxies and proxy is a 400.

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

POST /sessions/{id}/tokens
{  "scope": "view",        // view | control | cdp | agent  "ttl_seconds": 900      // default 3600, up to 604800 (7 days)} // 200 → { "token": "...", "scope": "view", "ttl_seconds": 900 }

Screenshot

GET /sessions/{id}/screenshot?format=png&quality=80
// 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 yet

Screenshots 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

GET /sessions/{id}/debug
{  "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 it

Proxy countries

GET /proxies/countries
// no auth, cached{ "countries": [ { "code": "US", "name": "United States", "timezone": "America/New_York", "locale": "en-US" }, ... ] }

Fetch a replay

GET /sessions/{id}/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

ScopeGrants
viewWatch the live stream. Input is refused server-side — spectators physically cannot click or type.
controlWatch plus full mouse and keyboard.
cdpChrome 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).

EventWhendata
session.createdA session is first observedsession_id, start_url, width, height, created_at, record
session.endedThe session is destroyed or reapedsession_id, start_url, width, height, started_at_ms, ended_at_ms, end_reason
replay.readyA recorded session's replay finished finalizingsession_id, duration_ms, video_bytes, page_count, ended_at_ms
delivery
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| < 300s

The 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).

StatusMeaning
400Invalid 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).
402Managed proxies refused: the included pool is used with no overage available (proxy_quota_exhausted) or the subscription is past due (payment_failed).
401Missing or invalid API key, or missing session token.
403Bad, 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.
404Session not found (or not yours); replay not ready yet; no downloads for this session.
409Screenshot requested before the session has a page.
413Upload larger than 256 MiB.
422Request failed schema validation (width out of range, unknown token scope, …).
429Rate limit or capacity: your plan's concurrent-session limit, or the fleet is momentarily full. Honor the Retry-After header (30–60s).
502The session failed to start, or an in-session operation (screenshot, upload, CAPTCHA solve) failed.
503Authentication 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.