@browserview/typescript
v0.3.1
Published
TypeScript client for the browserview.io API — disposable cloud browser sessions with a live viewer and CDP access.
Maintainers
Readme
@browserview/typescript
TypeScript client for the browserview.io API. browserview.io runs disposable cloud Chromium sessions that humans can watch and control in a live viewer while agents drive the same browser over the Chrome DevTools Protocol.
Zero runtime dependencies. Requires Node 18+ (any runtime with fetch works).
Install
npm install @browserview/typescriptConfiguration
| Setting | Option | Env var | Default |
| --- | --- | --- | --- |
| API key | apiKey | BROWSERVIEW_API_KEY | — (required) |
| Base URL | baseUrl | BROWSERVIEW_BASE_URL | https://sessions.browserview.io |
| Retries | maxRetries | — | 3 (0 disables) |
| Timeout | timeoutMs | — | 60000 |
Explicit constructor options take precedence over environment variables.
API keys are minted in the browserview.io console and look like bv_live_ + 40 hex chars. A key sees only its own sessions.
The SDK sends the key as Authorization: Bearer <key>; the API equally accepts x-api-key: <key>.
Quickstart
import { BrowserView } from "@browserview/typescript";
const bv = new BrowserView(); // reads BROWSERVIEW_API_KEY
// or: new BrowserView({ apiKey: "...", baseUrl: "https://sessions.browserview.io" })
const session = await bv.sessions.create({
startUrl: "https://example.com", // server default: "about:blank"
width: 1280, // server default (320–3840)
height: 800, // server default (240–2160)
});
console.log(session.viewer_url); // live viewer, control access
console.log(session.watch_url); // live viewer, view-only
// ...when you're done:
await bv.sessions.destroy(session.id);create() blocks until the browser is ready by default (server-side wait: true, typically ~5s). Pass wait: false to return immediately; the SDK only sends fields you set.
Other operations:
const sessions = await bv.sessions.list(); // no URLs/tokens in list responses
const fresh = await bv.sessions.get(session.id); // fresh URLs/tokens + restarts/degraded
const token = await bv.sessions.mintToken(session.id, {
scope: "view", // "view" | "control" | "cdp"
ttlSeconds: 3600, // 1..604800 (7 days); default 3600
});get() additionally reports session health: restarts (number of browser restarts, or null when the in-container status server is unreachable) and degraded (true once the browser has restarted). All session objects include mem_limit_bytes, the container memory limit.
The server returns viewer_url, watch_url, and cdp_url as relative paths; the SDK absolutizes them against your base URL before you see them.
Drive the session with Playwright
Connect an agent to the same browser a human is watching in the viewer:
import { chromium } from "playwright";
const session = await bv.sessions.create({ startUrl: "https://example.com" });
const browser = await chromium.connectOverCDP(session.cdp_url, {
headers: { "x-session-token": session.cdp_token },
});
const page = browser.contexts()[0].pages()[0];
await page.goto("https://news.ycombinator.com");The token can also be passed as ?token= on the CDP URL. Puppeteer's puppeteer.connect({ browserURL }) works the same way.
Session replay
Create the session with record: true and BrowserView captures everything server-side — a video of the display plus structured streams of actions, console output, network requests, and errors:
const session = await bv.sessions.create({
startUrl: "https://example.com",
record: true,
});
// ... drive the session ...
await bv.sessions.destroy(session.id);
// The replay is ready seconds after the session ends.
const replay = await bv.sessions.waitForReplay(session.id); // polls up to 2 min
console.log(replay.video?.url); // seekable WebM
console.log(replay.pages); // main-frame navigation timeline
console.log(replay.events?.console?.url); // JSONL: {"ts": ..., "level": ..., ...}sessions.replay(id) fetches the manifest without polling: it returns { status: "recording" } while the session is alive and throws a 404 BrowserViewError while the recording finalizes. Every event line carries an absolute epoch-ms ts; align it with the video via (ts - replay.video.start_time_ms) / 1000 seconds. Artifact URLs expire at urls_expire_at_ms — call replay() again for fresh ones.
Errors and retries
Failed calls throw BrowserViewError with status, message, and retryAfter (seconds, parsed from the Retry-After header on any status). status is 0 when no HTTP response was received (e.g. timeout).
The client retries automatically before throwing:
- 429 (rate/capacity limits — creates send
Retry-After: 30) and 503 (auth backend temporarily down —Retry-After: 10) are retried for every method; the server does not commit a session create before returning these. - Network errors and timeouts are retried for idempotent
GET/DELETEonly. - Waits honor
Retry-After(a single wait is capped at 30s), otherwise back off 1s, 2s, 4s. Default 3 retries; configure withmaxRetries(0 disables). - Each attempt is aborted after
timeoutMs(default 60s, sized for create-with-wait).
Other statuses you may see: 401 invalid/missing key (repeated failures escalate to 429 per IP), 404 unknown session or one your key cannot see, 502 session create/backend failure.
import { BrowserViewError } from "@browserview/typescript";
try {
await bv.sessions.get("nope");
} catch (err) {
if (err instanceof BrowserViewError && err.status === 404) {
// session gone or not yours
}
}Health endpoints
GET /healthz and GET /readyz are unauthenticated and return {"status":"ok"} — useful for probes; the SDK does not wrap them.
