Sessions API reference
Create, inspect, extend and release browser sessions over HTTP: the full create body, the two keep-alive clamps and every field on the session record.
Last updated:
A session is one hardened browser container with a deadline. The four endpoints on this page create it, read its record, extend it and release it. Every call authenticates with an API key sent as a bearer token in the Authorization header, and request bodies are strict: a misspelled field comes back as a 400 invalid_request rather than being silently ignored.
/v1/sessions
Creates a session; the minimal body is `{}`, and every field below is optional.
Create body
| Name | Type | Description |
|---|---|---|
shape
|
object | Browser shape: `viewport` (default 1440×900), `locale` (default `de-DE`), `timezone` (default `Europe/Berlin`), `headless` (default `true`) and allowlisted `extraArgs`. A disallowed argument is refused with `disallowed_browser_arg`. |
profileId
|
string | Restores the named profile's cookies, storage and logged-in state into the browser. Naming a profile that does not exist creates it, quota-checked against your plan; the state is captured again at teardown. |
credentialIds
|
string[] | Vault credentials the session may use; each value is only ever released on that credential's granted site. |
egress
|
object | `allowDomains` and `denyDomains` restrict where the browser may connect. |
recording
|
object | `enabled` (default `true`) and `retentionHours` (default 24) control session recording. |
ttlSeconds
|
integer |
Initial lifetime, 30 to 259200 seconds. The plan's ceiling still applies through `maxLifetimeAt`.
Default: 900
|
keepAlive.idleTimeoutSeconds
|
integer | Releases a session nobody is using. This is a different number from the deadline: `expiresAt` and `maxLifetimeAt` bound even a busy session. |
keepAlive.checkpointSeconds
|
integer | Interval at which session state is checkpointed; the latest checkpoint time appears as `checkpointedAt` on the record. |
poolId
|
string | Claims a browser from a reserved pool. A pooled session never falls back to a cold start and may not ask for a different shape — a mismatch is refused. |
metadata
|
object | Free-form labels of your own, echoed back on the session record. |
/v1/sessions/:id
Returns the session record, including the `connectUrl` for CDP clients while the session is `ready`.
/v1/sessions
The organisation’s open sessions, hydrated from the registry, so a reconnecting client can rediscover and attach its handles.
/v1/sessions/:id/connect-token
A fresh `connectUrl` with a new five-minute access token, for a caller that waited longer than the token on the create or get response lives. Session-bound: it opens no other session.
/v1/sessions/:id/watch-link
A login-free viewer link, read-only unless `allowTakeover` is true, valid `ttlSeconds` (60–3600, default 3600). Answers `capacity_unavailable` on a deployment with no public base URL.
/v1/sessions/:id/keepalive
Extends the session's deadline; the body is `{}` or names a new `ttlSeconds`.
Keep-alive body
| Name | Type | Description |
|---|---|---|
ttlSeconds
|
integer | The extension you are asking for, measured from now, within the 30 to 259200 second bounds. Omit it for the default extension. |
Reading the two clamps
The response carries the updated session plus two booleans that say what actually happened. clampedToMaxLifetime means the session sits at the ceiling its plan fixed at creation — no further keep-alive will help, so plan a fresh session. clampedToPlanLimit means this one extension was trimmed to what the plan allows right now; the session keeps running, and you can simply ask again later. maxLifetimeAt itself never moves.
/v1/sessions/:id
Releases the session and its container; a named profile is captured on this teardown path too.
Session record fields
Responses are loose: a newer server may add fields, so tolerate ones you do not know.
| Name | Type | Description |
|---|---|---|
connectUrl
|
string | A `wss://` CDP endpoint carrying a `?t=` access token while the session is `ready`; null otherwise. The token is checked at the WebSocket upgrade and lives five minutes from the response; treat the URL as a credential. |
connectUrlExpiresAt
|
timestamp | When the token in `connectUrl` stops opening the socket. Null when there is no token. Since protocol 1.4. |
status
|
string | Open enum: `pending`, `ready`, `releasing`, `released`, `failed` today — keep a default branch for values a newer server adds. |
warmStart
|
boolean | True when the browser came from a pool or the warm pool instead of a cold container launch. |
readyAt
|
timestamp | When the browser became usable. The billing clock starts here, not at admission. |
expiresAt
|
timestamp | The current deadline; a keep-alive moves it forward. |
maxLifetimeAt
|
timestamp | The ceiling set from the plan at creation. A keep-alive never moves it. |
shutdownReason
|
string | Why the session ended, as an open enum — protocol 1.2.0 added `idle_timeout`, so switch with a default case. |
checkpointedAt
|
timestamp | When session state was last checkpointed, if `keepAlive.checkpointSeconds` was set. |
lastActiveAt
|
timestamp | The last time anything used the session — the input the idle timeout works from. |
Create, read, release
One pass over the lifecycle: create with metadata, read the record's timing fields, release.
Requires: api-key
BASE="https://browserberg.com"
AUTH="Authorization: Bearer $BROWSERBERG_API_KEY"
JSON="Content-Type: application/json"
CREATED=$(curl -s -X POST "$BASE/v1/sessions" -H "$AUTH" -H "$JSON" -d '{
"ttlSeconds": 120,
"metadata": { "purpose": "reference walkthrough" }
}')
ID=$(echo "$CREATED" | python3 -c 'import json,sys; print(json.load(sys.stdin)["session"]["id"])')
curl -s "$BASE/v1/sessions/$ID" -H "$AUTH" \
| python3 -c 'import json,sys; s = json.load(sys.stdin)["session"]; print(json.dumps({
"status": s["status"], "warmStart": s["warmStart"], "readyAt": s["readyAt"],
"expiresAt": s["expiresAt"], "maxLifetimeAt": s["maxLifetimeAt"]}, indent=2))'
curl -s -X DELETE "$BASE/v1/sessions/$ID" -H "$AUTH" > /dev/null
echo "released"
{
"status": "ready",
"warmStart": true,
"readyAt": "2026-01-01T00:00:00.000Z",
"expiresAt": "2026-01-01T00:00:00.000Z",
"maxLifetimeAt": "2026-01-01T00:00:00.000Z"
}
released