env: "LOCAL" with cdpUrl and cdpHeaders) and opened https://example.com. The script also type-checks. We didn't run act() or extract(), which need your OpenAI key; those lines follow Stagehand's v3 API.Prerequisites
- A Browsa API key (it starts with
agt_). Sign up for 100 free credits, no card. - Node.js 20.19 or newer (Stagehand 3.7 requires it).
- A model key. The script uses OpenAI; Stagehand also takes
anthropic/...andgoogle/...model names.
Install
npm install @browserbasehq/stagehand@3 browsa zod
npm install -D tsx
npm pkg set type=moduleThen put your keys in the environment:
export BROWSA_API_KEY="agt_live_..."
export OPENAI_API_KEY="sk-..."@browserbasehq/stagehand@3. Stagehand v4 attaches through a browser extension that it loads over CDP from your disk. In our test, v4's localBrowser.connect() failed on a Browsa browser with Method not available. v3 talks to the browser over plain CDP and works.The script
One file. It starts a 5-minute Browsa browser, asks it for its WebSocket address, attaches Stagehand, clicks and extracts with your model, then destroys the browser.
import { Client } from "browsa";
import { Stagehand } from "@browserbasehq/stagehand";
import { z } from "zod";
const apiKey = process.env.BROWSA_API_KEY!;
const browsa = new Client({ apiKey });
const cloud = await browsa.createAgent({ ttl: "5m" }); // a Browsa cloud browser, 1 credit
try {
// Stagehand v3 attaches to a browser-level WebSocket URL. Ask the browser for it.
const res = await fetch(`${cloud.cdp_http}/json/version`, {
headers: { "X-API-Key": apiKey },
});
const { webSocketDebuggerUrl } = await res.json();
const stagehand = new Stagehand({
env: "LOCAL",
localBrowserLaunchOptions: {
cdpUrl: webSocketDebuggerUrl,
cdpHeaders: { "X-API-Key": apiKey },
},
model: { modelName: "openai/gpt-5.6-luna", apiKey: process.env.OPENAI_API_KEY },
});
await stagehand.init();
const page = stagehand.context.pages()[0];
await page.goto("https://example.com");
await stagehand.act("click the link on the page");
const { title } = await stagehand.extract(
"the main heading of the page",
z.object({ title: z.string() }),
);
console.log(title);
await stagehand.close();
} finally {
await browsa.destroyAgent(cloud.id);
}Run it:
npx tsx agent.tsHow it works
- Start a browser.
createAgent({ ttl: "5m" })starts a Chromium on Browsa and returns its id and itscdp_httpaddress. - Get the WebSocket address. Stagehand v3 connects to a WebSocket URL, so the script reads
/json/versionatcdp_http(with your header) and takeswebSocketDebuggerUrl, awss://browsa.ioaddress. - Attach and act.
env: "LOCAL"withcdpUrlmakes Stagehand attach instead of launching Chrome, andcdpHeaderssends your key on the WebSocket.stagehand.close()leaves the browser running, sodestroyAgent()infinallyshuts it down.
Troubleshooting
401 Unauthorized when connecting
The X-API-Key header didn't reach Browsa, or the key is wrong. Check that BROWSA_API_KEY is set in the shell that runs the script and that it starts with agt_; the Browsa SDK refuses keys that don't. Pass the header exactly as the script does.
The connection drops after a few minutes
The browser reached the end of its TTL and shut down. The scripts use ttl="5m" (1 credit). For longer runs, ask for more, up to "1h"; any TTL over 5 minutes costs 5 credits. Start a new browser for the next run.
Method not available. on connect
You're on Stagehand v4. Install v3: npm install @browserbasehq/stagehand@3, and use the new Stagehand({ env: "LOCAL", ... }) form shown above.
Top-level await or require errors
The script is an ES module. Keep "type": "module" in package.json and run it with npx tsx agent.ts.
Next steps
Use the same browser from Playwright or Puppeteer, or move to a Python agent with browser-use.