API reference
Cloud and worker REST endpoints: profiles, sessions, organizations, plans, licensing and launching.
VeilBrowser exposes two REST surfaces with different jobs and different credentials:
| Surface | Base URL | Job |
|---|---|---|
| Cloud API | https://api.veilbrowser.net | Provision profiles, read session activity, manage organizations and billing |
| Local API | Each worker, http://<worker>:38923 | Launch and stop browsers |
The worker port is 38923 in the published container image. 3000 is the
from-source default and appears only in a development checkout.
Nothing on the cloud API starts a browser, and nothing on a worker manages your organization. See Authentication for which credential goes where, and Errors for the response envelope.
Interactive schemas
Both services publish OpenAPI at GET /docs — https://api.veilbrowser.net/docs for
the cloud, http://localhost:38923/docs on a worker. That is the machine-readable
source of truth; this page is the narrative one.
Conventions
Successful responses are { "success": true, "data": … }. List endpoints wrap results in
pagination metadata:
Prop
Type
All paginated endpoints accept page, limit, sortBy and sortOrder (asc or
desc, default desc).
Profiles
Requires a dashboard session. All operations are scoped to the caller's active organization.
Sessions
The cloud session list is a read-only activity feed. Workers populate it through the profile-session endpoints below; there is no cloud endpoint that launches or stops a browser.
Organizations
Requires a dashboard session. Role requirements are noted per endpoint; roles are
owner, admin and member.
Billing
Every route acts on the caller's active organization, taken from the dashboard
session rather than from the request, and every one is restricted to owner and admin.
A member gets 403 and AUTHORIZATION_ERROR; a session with no active organization
gets 400 and VALIDATION_ERROR.
Contact
Plans
Launch and stop (Local API)
Browsers run on your workers, so launching goes to the Local API or the cluster load
balancer — never to api.veilbrowser.net. Authenticate with the worker's API_KEY.
curl -X POST http://localhost:38923/sessions/profile \
-H "X-API-Key: $WORKER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "profileId": "<profile-uuid>" }'Supply either profileId for an existing profile or spec to create one from
criteria, never both. Optional browser and proxyOverride objects adjust that single
launch.
The 201 response includes id, status, startedAt, launchId, debugPort,
webSocketDebuggerUrl, devtoolsFrontendUrl and viewUrl. debugPort is the one
Selenium needs, because debuggerAddress takes a host and port rather than a URL.
curl -X DELETE http://localhost:38923/sessions/<session-id> \
-H "X-API-Key: $WORKER_API_KEY"The launch URL is addressed to the worker
webSocketDebuggerUrl is
wss://<host>/browsers/<launchId>/devtools/browser/<guid>?ct=<token> — the worker's
own public address, with the per-launch capability the /browsers/* routes require.
Connect to it from anywhere that can reach the worker. Behind the cluster load
balancer it is prefixed with /w/<workerId>.
Worker endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /health | Status, version and contract version — no auth |
GET | /profiles | Available browser bundles, filterable and paginated |
GET | /profiles/versions | Browser majors installed on this host |
GET | /profiles/android-models | Device models for a given browserVersion |
POST | /sessions/profile | Launch a profile |
GET | /sessions | List sessions — status of running, exited or killed |
GET | /sessions/{id} | Session detail, including the CDP URL |
DELETE | /sessions/{id} | Kill a session |
GET | /fleet/status | Full host snapshot for the cluster manager |
GET | /fleet/live | Lightweight CPU, memory and active-session metrics |
GET /profiles accepts filename, browserVersion, os, osVersion,
windowsVersion (7, 10, 11), osArch (x86, arm), renderer, model and
gpuName, plus the standard pagination parameters.
Worker integration (organization API key)
Self-hosted workers use the organization key against the cloud for licensing and session tracking. You rarely call these directly, but they explain what a worker is doing.
| Method | Path | Purpose |
|---|---|---|
POST | /v1/license/authorize | Concurrency check before launch — fail-closed. Reserves a slot |
GET | /v1/license/sessions | What the organization is being charged concurrency for, and whether each row is stale |
POST | /v1/license/sessions/heartbeat | Worker-authoritative liveness for one machine; anything it omits is released |
POST | /v1/license/sessions/reclaim | End running sessions whose worker is gone, releasing their concurrency |
GET | /v1/profiles/{id}/config | Fetch a profile's launch configuration |
POST | /v1/profiles/{id}/sessions | Report session start; consumes the reservation |
DELETE | /v1/profiles/{id}/sessions/{sessionId} | Report session end |
GET | /v1/chromium/releases | Browser build catalog |
GET | /v1/chromium/releases/{version}/download | Download a build |
POST /v1/license/authorize wraps its answer in the usual { success, data } envelope.
data carries allowed, current, limit (null = unlimited), an optional slotId,
and profileConfig when a profileId was supplied. A refusal is 402 with a reason
of concurrency_limit or profile_limit in the same envelope, so read data.allowed
rather than the status alone.
GET /v1/license/sessions is the one to reach for when a launch is refused and you do
not know why. It answers to the organization key — unlike the dashboard's
GET /v1/sessions, which needs a browser session — and returns current, limit, the
split into runningSessions and reservedSlots, both liveness TTLs, and up to 500
running sessions:
| Field | Meaning |
|---|---|
machineId | Which worker reported the session |
startedAt | When it launched |
heartbeatAt | Last liveness report, or null if the worker has never sent one |
stale | Past its deadline — five minutes since heartbeatAt, or six hours since startedAt when it has never reported |
stale is computed exactly as the reaper decides, so a row flagged here is a row
POST /v1/license/sessions/reclaim would close. See
Licensing & concurrency and
Errors.