Skip to main content

The envelope

Errors are JSON. detail is the field to read, and it takes three shapes:
  • A string, on most 401, 403, 404, 409 and 501 answers:
  • An object, on every answer a program should branch on. It always has error (a stable slug) and usually message; some add retryable, and context such as limit, balance, tier, upgrade_url, retry_after_seconds, max_ttl, job_id:
  • A list, on 422 validation failures, one entry per problem with type, loc, msg and input:
Some errors use a top-level shape: 500 {"status": "error", "detail": "internal error"}, the 504 {"error": "deadline_exceeded", ...} and {"error": "browser_timeout", ...} answers, 413 {"detail": "request body too large"}, MCP errors, and credential-route errors such as {"error": "credential not found", "detail": "credential not found"}. A tolerant reader takes detail.error when detail is an object, detail when it is a string, joins loc and msg when it is a list, and falls back to a top-level error. A wrong value answers the 422 list shape with the reason in msg; for example POST /v1/session with {"region": "zz"} answers Value error, unknown region 'zz'; see GET /v1/regions.

Status codes

Retry transient 429, 502, 503 and 504 responses with exponential backoff and jitter, honouring Retry-After; send an Idempotency-Key when retrying session creation. Retry 409 session_busy after the current work ends, or idempotency_key_in_use after the create settles. For session_driven_by_client, disconnect the main CDP, native Playwright, or VNC client that holds the session. CDP observers can remain connected. Other 4xx usually require changing the request. Custom-proxy validation and connection failures use customer_proxy_* codes. See Custom proxy errors. CDP connection limits use WebSocket close codes 4409, 4429, and 4403; see Connection close codes.

Error slugs by route family

The error values the API answers with, grouped by where you meet them. A slug appears with the status it carries. Authentication and account, any route: 401 (plain string), 403 account_suspended, 403 scope refusal (plain string), 402 insufficient_credits, 402 quota_exceeded, 402 key spend limit (plain string), 429 rate_limited, 400 bad_path. Session create, POST /v1/session: 400 stealth_always_on, 400 stealth_max_needs_premium_egress, 402 premium_egress_exhausted, 409 idempotency_key_in_use, 409 region_unavailable (with available), 409 proxy_type_unavailable, 422 ttl_exceeds_plan (with max_ttl), 429 concurrency_limit (with limit, tier, upgrade_url), 502 session_launch_failed, 503 capacity_unavailable (Retry-After: 5), 503 residential_vendor_unavailable (Retry-After: 60), 503 residential_egress_unavailable, 503 full_browser_unavailable, 503 recording_unavailable. Session reads and release, GET /v1/session/{id}, /status, DELETE: 404 (plain string), 410 terminal record, 410 browser_crashed (retryable: false), 410 plain string when the session’s server is unreachable, 503 plain string when a release could not be confirmed. Page reads, POST /v1/fetch, GET /v1/job/{id}: 404 plain string for an unknown job, 409 session_busy (with the running job_id), 410 session_closed_during_fetch (with reason, origin, cause), 422 session_ttl_cannot_hold_job, 504 with error: "deadline" in the fetch body itself. Extract, scrape, crawl, inspect: 501 structured_extraction_unavailable, 502 extract_failed, 502 scrape_failed (retryable: true), 502 inspect_failed, 504 inspect_timeout, 429 too_many_concurrent_inspects. Search, POST /v1/search: 422 invalid_region, 422 unsupported_region, 422 region_with_session, 429 concurrency_limit, 502 session_launch_failed, 502 region_unavailable, 503 region_routing_unavailable. Tabs, elements, act, screenshot, navigate: 404 element_not_found, 400 bad_selector, 409 no_page_loaded, 409 last_tab, 409 session_busy, 409 session_driven_by_client, 404 plain No such tab, 429 tab_limit, 502 Data Plane tab creation failed. Files and downloads, POST /v1/session/{id}/files/fetch: 409 no_tab, 413 plain string, 502 file_fetch_failed (retryable: true), 502 download_failed, 504 file_fetch_timeout. Saved logins, POST /v1/session/{id}/save-auth, /v1/auth/*: 400 incomplete_cookie_set, 400 plain string when there is nothing to save, 404 plain string for an unknown domain. Agent and templates, POST /v1/agent, POST /v1/template: 422 invalid_agent_params, 422 invalid_template_params (with issues), 400 plain string for an unknown task or template. Sidecar, /v1/session/{id}/sidecar*: 403 sidecar_not_enabled, 409 sidecar_unavailable, 422 sidecar_start_failed, 502 sidecar_unreachable. Credits, POST /v1/credits/redeem: 400 unknown_code, 409 already_redeemed. Deadlines, any route: 504 deadline_exceeded, 504 browser_timeout (with cdp_method and retry_with).

When a session has ended

A session ends when you release it, when it idles past idle_timeout, when it reaches ttl, when its browser crashes, or when the service ends it. Its record is kept for an hour after that, and during that hour:
  • GET /v1/session/{id} and every drive call answer 410 with the record in detail.
  • GET /v1/session/{id}/status answers 200 with the same record.
  • DELETE /v1/session/{id} answers 204 with Location: /v1/session/{id}/status.
  • After the hour, all of them answer 404 No such session.
The record:
Two 410s carry a different detail: {"error": "browser_crashed", "retryable": false, "message": "..."} when the browser crashed before the record was written, and {"error": "session_closed_during_fetch", "message": "...", "reason": ..., "origin": ..., "cause": ...} on a fetch whose session ended under it. A WebSocket on an ended session is refused on the handshake with the same 410 or 404; a bad token is refused with 403; an open socket whose browser crashes is closed with code 4410.