---
title: "Coming soon. Open a DevTools Protocol (CDP) WebSocket to a cloud browser"
method: GET
path: "/v1/browser"
tags: ["Browser"]
---

# Coming soon. Open a DevTools Protocol (CDP) WebSocket to a cloud browser

`GET /v1/browser`

## Request

- Base URL: `https://api.spicrawl.com`
- URL: `https://api.spicrawl.com/v1/browser`
- Auth: one of:
  - HTTP bearer
  - API key in query parameter `apikey`

Coming soon.
A WebSocket upgrade, not a JSON endpoint. Connect with any CDP client, using the URL as its browser WebSocket endpoint. Authenticate with, in order of preference: `Authorization: Bearer <key>`
on the upgrade; a single-use `token` from `POST /v1/browser/token` (60 s, one connection, bound to its
options — the URL the CLI and MCP return); or the `apikey` query parameter (`api_key` alias), kept for
ZenRows-style URLs. When `token` is present it is the only credential consulted. Requires the
`browser` scope.

Every refusal happens before the upgrade and is an RFC 7807 problem. After the 101 there is no HTTP
response left; the session ends by closing the TCP connection, never with a close frame or error
message.

Why a session ends (recorded server-side, not sent to you):
`client_closed` (you disconnected, or stopped answering WebSocket pings: the API pings every 15 s and
closes after 2 unanswered, so a half-open client frees its slot within about 30 s),
`ttl_expired` (`session_ttl` elapsed; a hard wall, not idle),
`idle_timeout` (no CDP messages in either direction for the deployment idle timeout, 2 minutes by
default, measured from the last message; pings and pongs do not count),
`byte_limit` (the deployment's per-session traffic cap was reached, if configured),
`upstream_closed` (the browser went away).

Pricing: a session costs 8 credits per started minute, the same as one full-browser-rendered page. At
admission the session HOLDS the price of its whole `session_ttl` against the monthly credit
allowance (default `session_ttl` 180 s = 24 credits; 60–900 s range = 8–120 credits). At close it
is charged for the minutes it actually held (rounded up, capped at `session_ttl`'s minutes) and
the unused part of the hold is returned immediately. A session the platform ends (upstream closed,
or API shutdown) costs 0; a session that never opens costs 0 and returns its hold.
While the operator has switched billing off, sessions hold and cost 0.

Admission, before the upgrade, in this order:
- Rate limit on session opens per key: 429 `ERR::LIMIT::RATE_LIMITED`, with `X-RateLimit-*` and `Retry-After`.
- Monthly credit allowance cannot cover the whole `session_ttl` hold: 402 `ERR::LIMIT::QUOTA_EXCEEDED`,
  refused before any browser is leased. A shorter `session_ttl` may fit.
- Simultaneous sessions per organization (`browser_concurrency`, default 2): 429
  `ERR::LIMIT::CONCURRENCY_EXCEEDED`, `Retry-After: 5`, `Concurrency-Limit`/`Concurrency-Remaining: 0`.
  The slot is held until the socket closes, so always close your browser.
- If the limiter store is unreachable the session is refused (fail closed): 503
  `ERR::INTERNAL::UNAVAILABLE`, `Retry-After: 10`.
- No CDP backend on this deployment: 501 `ERR::ENGINE::UNAVAILABLE`, `retryable: false`. Do not retry.

Rate and concurrency headers appear only on refusals; the 101 is the browser backend's handshake
forwarded verbatim.

## Query parameters

- `token` string
- `apikey` string
- `api_key` string
- `engine` 'browser' | 'js'
- `proxy_region` string
- `proxy_country` string
- `session_ttl` integer
- `headless` 'true' | 'false' | '1' | '0'
- `sticky_key` string

## Headers

- `Upgrade` 'websocket', required
- `Connection` string, required
- `Sec-WebSocket-Key` string, required
- `Sec-WebSocket-Version` string, required

## Other responses

- `101`: Switching Protocols. The socket now carries raw CDP JSON messages in both directions, spliced to the browser without inspection.
- `400`: The request is invalid. Not retryable; fix the request using `code` and `diagnostics.hint`.
- `401`: Missing, invalid, revoked or expired key. Not retryable with the same key.
- `402`: `ERR::LIMIT::QUOTA_EXCEEDED`: the organization is out of credits. Not retryable until credits are added.
- `403`: The key lacks the scope this route requires (`ERR::AUTH::INSUFFICIENT_SCOPE`), or the action is not permitted.
- `429`: Rate, concurrency or live-session limit reached. Retryable after `Retry-After`.
- `500`: Internal error. Retryable when `retryable` is true.
- `501`: The capability is not deployed on this host. Not retryable.
- `502`: The browser backend could not be reached or refused the session (`ERR::UPSTREAM::ERROR`), or the worker reported another upstream-class code. Not caused by your request; retry with backoff and quote the `request_id` if it persists.
- `503`: No proxy exit or engine capacity was available. Retryable after `Retry-After`; 0 credits.

---

[API](https://skmtc.dev/spicrawl/apis/spicrawl-api.md) · [All operations](https://skmtc.dev/spicrawl/apis/spicrawl-api/llms.txt) · [OpenAPI document](https://skmtc.dev/spicrawl/apis/spicrawl-api/revisions/c4b4c6f97219?raw)
