Browserbase SDK
Browserbase runs production headless Chromium in isolated VMs and exposes a CDP WebSocket per session. Code drives the browser with Playwright, Puppeteer, Selenium, or Stagehand (AI primitives on top of Playwright). The @browserbasehq/sdk package is a thin REST client for sessions, contexts, extensions, and projects — it does not drive the browser itself.
Base URL & auth
POST https://api.browserbase.com/v1/sessions
x-bb-api-key: $BROWSERBASE_API_KEY
Content-Type: application/json
Env vars used by every official package:
Each browser runs in a dedicated VM destroyed after the session. SOC 2 Type II across plans, HIPAA on Scale.
Three integration paths
| Path |
Packages |
Best for |
| Raw SDK |
@browserbasehq/sdk |
Manage sessions/contexts/extensions; no browser driving |
| SDK + Playwright/Puppeteer |
@browserbasehq/sdk + playwright-core or puppeteer-core |
Deterministic scripted automation |
| Stagehand |
@browserbasehq/stagehand |
AI-driven act/extract/observe/agent on top of Playwright |
The SDK is REST only. Browser control happens through connectUrl (Playwright/Puppeteer/CDP) or seleniumRemoteUrl (Selenium). Stagehand wraps the SDK + Playwright internally.
Quickstart — Playwright (TypeScript)
import { chromium } from 'playwright-core';
import Browserbase from '@browserbasehq/sdk';
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID!,
});
const browser = await chromium.connectOverCDP(session.connectUrl);
const defaultContext = browser.contexts()[0];
const page = defaultContext.pages()[0];
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.screenshot({ path: 'shot.png', fullPage: true });
await page.close();
await browser.close();
console.log(`Replay: https://browserbase.com/sessions/${session.id}`);
Quickstart — Puppeteer (TypeScript)
import puppeteer from 'puppeteer-core';
import Browserbase from '@browserbasehq/sdk';
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create({ projectId: process.env.BROWSERBASE_PROJECT_ID! });
const browser = await puppeteer.connect({ browserWSEndpoint: session.connectUrl });
const [page] = await browser.pages();
await page.goto('https://example.com');
await browser.disconnect();
Quickstart — Stagehand (TypeScript)
import { Stagehand } from '@browserbasehq/stagehand';
import { z } from 'zod';
const stagehand = new Stagehand({
env: 'BROWSERBASE',
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
model: 'google/gemini-3-flash-preview', // routed through Browserbase Model Gateway
});
await stagehand.init();
const page = stagehand.page;
await page.goto('https://news.ycombinator.com');
await page.act('click the first story link');
const data = await page.extract({
instruction: 'extract the title and points',
schema: z.object({ title: z.string(), points: z.number() }),
});
await stagehand.close();
Quickstart — raw HTTP
curl -X POST https://api.browserbase.com/v1/sessions \
-H "x-bb-api-key: $BROWSERBASE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"projectId":"'"$BROWSERBASE_PROJECT_ID"'"}'
Session — request shape
type CreateSessionParams = {
projectId: string; // optional with SDK; required via REST
extensionId?: string;
timeout?: number; // 60–21600 sec; defaults to project setting
keepAlive?: boolean; // Hobby+; survives disconnect
region?: 'us-west-2' | 'us-east-1' | 'eu-central-1' | 'ap-southeast-1';
proxies?: boolean | ProxyConfig[];
userMetadata?: Record<string, string>; // ≤512 chars JSON, string values, no arrays
browserSettings?: {
context?: { id: string; persist?: boolean };
viewport?: { width: number; height: number };
blockAds?: boolean; // default false
solveCaptchas?: boolean; // default true
recordSession?: boolean; // default true
logSession?: boolean; // default true
advancedStealth?: boolean; // Scale plan
verified?: boolean; // Scale plan, premium fingerprint
captchaImageSelector?: string;
captchaInputSelector?: string;
os?: 'windows' | 'mac' | 'linux' | 'mobile' | 'tablet';
ignoreCertificateErrors?: boolean; // default true
};
};
Session — response shape
type Session = {
id: string;
createdAt: string; updatedAt: string;
startedAt?: string; endedAt?: string; expiresAt?: string;
projectId: string;
status: 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';
region: string;
contextId?: string;
keepAlive: boolean;
proxyBytes: number;
userMetadata?: Record<string, string>;
connectUrl: string; // wss://… CDP endpoint
seleniumRemoteUrl: string; // https://… Selenium grid
signingKey: string;
};
connectUrl is single-use per browser connection. Reconnecting the same connectUrl after disconnect requires keepAlive: true.
Default-context gotcha
Browserbase pre-creates the browser context. Calling browser.newContext() in Playwright spawns a second context that ignores browserSettings.context/proxies. Use the existing one:
const defaultContext = browser.contexts()[0];
const page = defaultContext.pages()[0]; // pre-created blank page
Lifecycle
// Release a session early
await bb.sessions.update(session.id, {
projectId: process.env.BROWSERBASE_PROJECT_ID!,
status: 'REQUEST_RELEASE',
});
// Live debugger URLs
const live = await bb.sessions.debug(session.id);
// { debuggerUrl, debuggerFullscreenUrl, wsUrl, pages: [{ id, url, debuggerUrl, ... }] }
// Filter sessions by metadata
const matches = await bb.sessions.list({ q: "user_metadata['env']:'staging'" });
Feature map
| Feature |
Trigger |
Reference |
| Full session API: create/list/retrieve/update/debug, lifecycle, regions, keep-alive, metadata |
bb.sessions.* |
references/sessions.md |
| Playwright + Puppeteer connection patterns, default-context gotcha, screenshots, navigation |
connectOverCDP, puppeteer.connect |
references/playwright-puppeteer.md |
Stagehand AI primitives: act/extract/observe/agent, ConstructorParams, Model Gateway, BYO LLM |
import { Stagehand } |
references/stagehand.md |
Persistent auth state via Contexts, persist: true, one-per-site rule |
bb.contexts.*, browserSettings.context |
references/contexts.md |
Browserbase-managed/external/none proxies, geolocation, domainPattern rules |
proxies: true, proxies: [{type:...}] |
references/proxies.md |
| Advanced stealth, verified browsers, captcha solving (custom selectors), block-ads |
advancedStealth, verified, solveCaptchas |
references/stealth-and-captcha.md |
Downloads (Browser.setDownloadBehavior to "downloads"), uploads (setInputFiles, large-file API), file chooser |
bb.sessions.downloads.*, bb.sessions.uploads.* |
references/downloads-uploads.md |
| Live view, recordings (rrweb), logs, debugger URLs, embedding |
bb.sessions.debug, sessions.recording, sessions.logs |
references/observability.md |
| Custom extensions via uploaded zip |
bb.extensions.create, extensionId |
references/extensions.md |
Always-fresh docs
Append .md to any docs page for clean Markdown:
Common pitfalls
browser.contexts()[0] not browser.newContext() — only the pre-created context honors browserSettings.context, proxies, and fingerprint.
Browser.setDownloadBehavior downloadPath is the literal string "downloads" — absolute paths like /tmp/downloads break sync.
solveCaptchas defaults to true — disable for compliance/audit runs.
recordSession and logSession default to true — disable for privacy-sensitive flows; cannot be re-enabled mid-session.
connectUrl is one-shot per CDP connect — reconnecting after disconnect requires keepAlive: true.
userMetadata JSON cap is 512 chars, string values only, no arrays — query with q: "user_metadata['env']:'staging'".
- Stagehand requires
BROWSERBASE_PROJECT_ID even though the raw SDK can infer it from the API key.
- Free-tier concurrency is 3 sessions; rate-limited at the create endpoint even when under max concurrency.
verified: true and advancedStealth are Scale-plan only — they 4xx on lower tiers.
browserbase proxies have a 1 MB minimum per session then round to nearest MB; restricted-category sites (Apple, finance, streaming, ticketing, some Google) are blocked.
- Sessions persist beyond script exit unless explicitly released —
REQUEST_RELEASE via sessions.update or let timeout expire; keepAlive prolongs further.
os and verified together require Scale tier — os: 'windows' | 'mac' | 'linux' | 'mobile' | 'tablet'.
- Selenium uses
seleniumRemoteUrl, not connectUrl — different protocol entirely.
1---2name: browserbase-sdk3description: Browserbase cloud-headless-browser SDK expertise for TypeScript/Node. Use when code imports `@browserbasehq/sdk`, `@browserbasehq/stagehand`, `playwright-core`, or `puppeteer-core` and connects to Browserbase; when code POSTs to `api.browserbase.com/v1/sessions`, `/v1/contexts`, `/v1/extensions`, or `/v1/projects`; when sending the `x-bb-api-key` header or using `BROWSERBASE_API_KEY`/`BROWSERBASE_PROJECT_ID`; when the user asks about Browserbase sessions, contexts, proxies, downloads, uploads, recordings, live debugger URL, captcha solving, advanced stealth, verified browsers, fingerprinting, BYOS S3 storage, regions (`us-west-2`/`us-east-1`/`eu-central-1`/`ap-southeast-1`), keepAlive, the `connectUrl` CDP WebSocket, `chromium.connectOverCDP`, `puppeteer.connect({browserWSEndpoint})`, the `browser.contexts()[0]` default-context pattern, Stagehand `act`/`extract`/`observe`/`agent` primitives, the Model Gateway, or deploying serverless via Browserbase Functions.4---56# Browserbase SDK78Browserbase runs production headless Chromium in isolated VMs and exposes a CDP WebSocket per session. Code drives the browser with Playwright, Puppeteer, Selenium, or Stagehand (AI primitives on top of Playwright). The `@browserbasehq/sdk` package is a thin REST client for sessions, contexts, extensions, and projects — it does not drive the browser itself.910## Base URL & auth1112```13POST https://api.browserbase.com/v1/sessions14x-bb-api-key: $BROWSERBASE_API_KEY15Content-Type: application/json16```1718Env vars used by every official package:1920- `BROWSERBASE_API_KEY` — from <https://browserbase.com/settings>21- `BROWSERBASE_PROJECT_ID` — required by Stagehand; inferred from key by `@browserbasehq/sdk`2223Each browser runs in a dedicated VM destroyed after the session. SOC 2 Type II across plans, HIPAA on Scale.2425## Three integration paths2627| Path | Packages | Best for |28|------|----------|----------|29| Raw SDK | `@browserbasehq/sdk` | Manage sessions/contexts/extensions; no browser driving |30| SDK + Playwright/Puppeteer | `@browserbasehq/sdk` + `playwright-core` or `puppeteer-core` | Deterministic scripted automation |31| Stagehand | `@browserbasehq/stagehand` | AI-driven `act`/`extract`/`observe`/`agent` on top of Playwright |3233The SDK is REST only. Browser control happens through `connectUrl` (Playwright/Puppeteer/CDP) or `seleniumRemoteUrl` (Selenium). Stagehand wraps the SDK + Playwright internally.3435### Quickstart — Playwright (TypeScript)3637```typescript38import { chromium } from 'playwright-core';39import Browserbase from '@browserbasehq/sdk';4041const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });4243const session = await bb.sessions.create({44 projectId: process.env.BROWSERBASE_PROJECT_ID!,45});4647const browser = await chromium.connectOverCDP(session.connectUrl);48const defaultContext = browser.contexts()[0];49const page = defaultContext.pages()[0];5051await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });52await page.screenshot({ path: 'shot.png', fullPage: true });5354await page.close();55await browser.close();56console.log(`Replay: https://browserbase.com/sessions/${session.id}`);57```5859### Quickstart — Puppeteer (TypeScript)6061```typescript62import puppeteer from 'puppeteer-core';63import Browserbase from '@browserbasehq/sdk';6465const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });66const session = await bb.sessions.create({ projectId: process.env.BROWSERBASE_PROJECT_ID! });6768const browser = await puppeteer.connect({ browserWSEndpoint: session.connectUrl });69const [page] = await browser.pages();70await page.goto('https://example.com');71await browser.disconnect();72```7374### Quickstart — Stagehand (TypeScript)7576```typescript77import { Stagehand } from '@browserbasehq/stagehand';78import { z } from 'zod';7980const stagehand = new Stagehand({81 env: 'BROWSERBASE',82 apiKey: process.env.BROWSERBASE_API_KEY,83 projectId: process.env.BROWSERBASE_PROJECT_ID,84 model: 'google/gemini-3-flash-preview', // routed through Browserbase Model Gateway85});8687await stagehand.init();88const page = stagehand.page;89await page.goto('https://news.ycombinator.com');9091await page.act('click the first story link');92const data = await page.extract({93 instruction: 'extract the title and points',94 schema: z.object({ title: z.string(), points: z.number() }),95});9697await stagehand.close();98```99100### Quickstart — raw HTTP101102```bash103curl -X POST https://api.browserbase.com/v1/sessions \104 -H "x-bb-api-key: $BROWSERBASE_API_KEY" \105 -H "Content-Type: application/json" \106 -d '{"projectId":"'"$BROWSERBASE_PROJECT_ID"'"}'107```108109## Session — request shape110111```typescript112type CreateSessionParams = {113 projectId: string; // optional with SDK; required via REST114 extensionId?: string;115 timeout?: number; // 60–21600 sec; defaults to project setting116 keepAlive?: boolean; // Hobby+; survives disconnect117 region?: 'us-west-2' | 'us-east-1' | 'eu-central-1' | 'ap-southeast-1';118 proxies?: boolean | ProxyConfig[];119 userMetadata?: Record<string, string>; // ≤512 chars JSON, string values, no arrays120 browserSettings?: {121 context?: { id: string; persist?: boolean };122 viewport?: { width: number; height: number };123 blockAds?: boolean; // default false124 solveCaptchas?: boolean; // default true125 recordSession?: boolean; // default true126 logSession?: boolean; // default true127 advancedStealth?: boolean; // Scale plan128 verified?: boolean; // Scale plan, premium fingerprint129 captchaImageSelector?: string;130 captchaInputSelector?: string;131 os?: 'windows' | 'mac' | 'linux' | 'mobile' | 'tablet';132 ignoreCertificateErrors?: boolean; // default true133 };134};135```136137## Session — response shape138139```typescript140type Session = {141 id: string;142 createdAt: string; updatedAt: string;143 startedAt?: string; endedAt?: string; expiresAt?: string;144 projectId: string;145 status: 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';146 region: string;147 contextId?: string;148 keepAlive: boolean;149 proxyBytes: number;150 userMetadata?: Record<string, string>;151 connectUrl: string; // wss://… CDP endpoint152 seleniumRemoteUrl: string; // https://… Selenium grid153 signingKey: string;154};155```156157`connectUrl` is single-use per browser connection. Reconnecting the same `connectUrl` after disconnect requires `keepAlive: true`.158159## Default-context gotcha160161Browserbase pre-creates the browser context. Calling `browser.newContext()` in Playwright spawns a second context that ignores `browserSettings.context`/proxies. Use the existing one:162163```typescript164const defaultContext = browser.contexts()[0];165const page = defaultContext.pages()[0]; // pre-created blank page166```167168## Lifecycle169170```typescript171// Release a session early172await bb.sessions.update(session.id, {173 projectId: process.env.BROWSERBASE_PROJECT_ID!,174 status: 'REQUEST_RELEASE',175});176177// Live debugger URLs178const live = await bb.sessions.debug(session.id);179// { debuggerUrl, debuggerFullscreenUrl, wsUrl, pages: [{ id, url, debuggerUrl, ... }] }180181// Filter sessions by metadata182const matches = await bb.sessions.list({ q: "user_metadata['env']:'staging'" });183```184185## Feature map186187| Feature | Trigger | Reference |188|---------|---------|-----------|189| Full session API: create/list/retrieve/update/debug, lifecycle, regions, keep-alive, metadata | `bb.sessions.*` | [references/sessions.md](references/sessions.md) |190| Playwright + Puppeteer connection patterns, default-context gotcha, screenshots, navigation | `connectOverCDP`, `puppeteer.connect` | [references/playwright-puppeteer.md](references/playwright-puppeteer.md) |191| Stagehand AI primitives: `act`/`extract`/`observe`/`agent`, ConstructorParams, Model Gateway, BYO LLM | `import { Stagehand }` | [references/stagehand.md](references/stagehand.md) |192| Persistent auth state via Contexts, `persist: true`, one-per-site rule | `bb.contexts.*`, `browserSettings.context` | [references/contexts.md](references/contexts.md) |193| Browserbase-managed/external/none proxies, geolocation, `domainPattern` rules | `proxies: true`, `proxies: [{type:...}]` | [references/proxies.md](references/proxies.md) |194| Advanced stealth, verified browsers, captcha solving (custom selectors), block-ads | `advancedStealth`, `verified`, `solveCaptchas` | [references/stealth-and-captcha.md](references/stealth-and-captcha.md) |195| Downloads (`Browser.setDownloadBehavior` to `"downloads"`), uploads (`setInputFiles`, large-file API), file chooser | `bb.sessions.downloads.*`, `bb.sessions.uploads.*` | [references/downloads-uploads.md](references/downloads-uploads.md) |196| Live view, recordings (rrweb), logs, debugger URLs, embedding | `bb.sessions.debug`, `sessions.recording`, `sessions.logs` | [references/observability.md](references/observability.md) |197| Custom extensions via uploaded zip | `bb.extensions.create`, `extensionId` | [references/extensions.md](references/extensions.md) |198199## Always-fresh docs200201Append `.md` to any docs page for clean Markdown:202203- Introduction: <https://docs.browserbase.com/reference/introduction.md>204- Node SDK reference: <https://docs.browserbase.com/reference/sdk/nodejs.md>205- Create session API: <https://docs.browserbase.com/reference/api/create-a-session.md>206- Full aggregate: <https://docs.browserbase.com/llms-full.txt>207- Index: <https://docs.browserbase.com/llms.txt>208- SDK source: <https://github.com/browserbase/sdk-node>209- Stagehand source: <https://github.com/browserbase/stagehand>210211## Common pitfalls212213- **`browser.contexts()[0]` not `browser.newContext()`** — only the pre-created context honors `browserSettings.context`, proxies, and fingerprint.214- **`Browser.setDownloadBehavior` `downloadPath` is the literal string `"downloads"`** — absolute paths like `/tmp/downloads` break sync.215- **`solveCaptchas` defaults to `true`** — disable for compliance/audit runs.216- **`recordSession` and `logSession` default to `true`** — disable for privacy-sensitive flows; cannot be re-enabled mid-session.217- **`connectUrl` is one-shot per CDP connect** — reconnecting after disconnect requires `keepAlive: true`.218- **`userMetadata` JSON cap is 512 chars, string values only, no arrays** — query with `q: "user_metadata['env']:'staging'"`.219- **Stagehand requires `BROWSERBASE_PROJECT_ID`** even though the raw SDK can infer it from the API key.220- **Free-tier concurrency is 3 sessions; rate-limited at the create endpoint** even when under max concurrency.221- **`verified: true` and `advancedStealth` are Scale-plan only** — they 4xx on lower tiers.222- **`browserbase` proxies have a 1 MB minimum per session** then round to nearest MB; restricted-category sites (Apple, finance, streaming, ticketing, some Google) are blocked.223- **Sessions persist beyond script exit unless explicitly released** — `REQUEST_RELEASE` via `sessions.update` or let `timeout` expire; `keepAlive` prolongs further.224- **`os` and `verified` together require Scale tier** — `os: 'windows' | 'mac' | 'linux' | 'mobile' | 'tablet'`.225- **Selenium uses `seleniumRemoteUrl`, not `connectUrl`** — different protocol entirely.