Guide
Moving from Browserbase
Your Playwright and Puppeteer code keeps working. You change how a session is created and connected, and what ends it. Code built on Browserbase's SDKs needs rewriting against REST or MCP, and we haven't tried Stagehand.
Browserbase details are from its documentation on 2026-11-04. Check its current docs for anything that matters to you. For agents, this guide is also at /migrate-from-browserbase.md.
The change
Create a session
curl -X POST https://api.steerpane.app/v1/sessions \
-H "Authorization: Bearer stp_live_YOUR_TOKEN" -H "Content-Type: application/json" \
-d '{"initial_url": "https://example.com", "timeout_min": 15, "viewport": {"width": 1280, "height": 800}}'Playwright
import time, requests
from playwright.sync_api import sync_playwright
API, TOKEN = "https://api.steerpane.app", "stp_live_YOUR_TOKEN"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
session = requests.post(f"{API}/v1/sessions", headers=HEADERS, json={"initial_url": "https://example.com"}).json()
while session["status"] == "starting": # the session isn't ready as soon as it's created
time.sleep(0.5)
session = requests.get(f"{API}/v1/sessions/{session['id']}", headers=HEADERS).json()
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(session["cdp_url"], headers=HEADERS) # your Playwright code from here on is unchanged
page = browser.contexts[0].pages[0]
print(page.title())
browser.close()
requests.post(f"{API}/v1/sessions/{session['id']}/stop", headers=HEADERS) # closing the connection doesn't end the sessionHow the API maps
| Browserbase | Steerpane | |
|---|---|---|
| API key | x-bb-api-key header | Authorization: Bearer stp_live_…. The token belongs to one organization, so there's no projectId to send. project_id is optional. |
| Create a session | POST /v1/sessions | POST /v1/sessions, with flat fields instead of browserSettings: viewport, profile_id, keep_alive, record, solve_captchas. |
| Time limit | timeout, in seconds | timeout_min, in minutes, capped by your plan. There's also idle_timeout_min, 20 by default. |
| Connect over CDP | connectUrl, returned ready to use | cdp_url. Send the token as an Authorization header, or append ?access_token=. It's a separate step, and the session may still be starting: poll GET /v1/sessions/{id} first. |
| End a session | POST /v1/sessions/{id} with status: REQUEST_RELEASE | POST /v1/sessions/{id}/stop |
| Saved logins | Contexts, passed as browserSettings.context | Profiles, passed as profile_id. A profile has one writer: a second gets 409 profile_locked unless it uses profile_mode: "read_only". |
| Live view | GET /v1/sessions/{id}/debug, debuggerFullscreenUrl | watch_url is the dashboard page, for people in your organization. For your own users, make an embed link (Scale plan and up). |
| Metadata | userMetadata | metadata, read back from GET /v1/sessions/{id}/meta. Filtering sessions by it isn't supported. |
| Downloads | GET /v1/sessions/{id}/downloads | The download action returns a file_id in the file store. POST /v1/sessions/{id}/files/zip gives one ZIP. |
| Uploads | POST /v1/sessions/{id}/uploads | POST /v1/files for a file id, then the upload_file action. |
| Fetch and search | /v1/fetch, /v1/search | /v1/fetch, /v1/search. Fields differ: format is markdown, html or raw, and mode is fast, deep or auto. |
| Allowed domains | allowedDomains | allowed_domains |
| CAPTCHAs | solveCaptchas, on by default | solve_captchas, off by default and Pro plan and up, or solve_captcha on demand over MCP. |
act, observe, extract | Stagehand, and their MCP server | MCP tools act, observe and extract, using your own model key. |
What behaves differently
- Closing a CDP connection doesn't end a session. It keeps running until you stop it or it goes idle for
idle_timeout_min, so stop sessions you're done with, or create them withauto_close: true. - Native dialogs are dismissed by default. Set
dialog_policy: "accept"if your code relies on confirming them. - A person can take over at any time. While they do, input and navigation over CDP are held until they hand back, and REST actions return
409 human_in_control. Your code should expect a wait. - Errors share one shape:
{"error": {"code", "message", "details"}}. Branch oncode.
What Steerpane doesn't have
- Selenium. Only Playwright, Puppeteer and other CDP clients.
- Browserbase's SDKs. Use REST, MCP or CDP. We haven't tried the Stagehand library against a Steerpane
cdp_url, so don't count on it. - Scale. Browsers are limited to 15 at once on the Scale plan, and all run on one node today. Check the pricing page against your concurrency before moving a large workload.
- Regions. Sessions run in the regions listed by
GET /v1/platform/public, and today that's one. - Managed proxies. There's no vendor behind them yet. Bring your own HTTP or SOCKS5 proxy.
- A hardened browser with partner recognition.
stealthmakes the user agent and client hints consistent. It isn't Browserbase's Verified browser. - HLS replay. Recordings are frames and events.
- A model gateway. Agent runs,
act,observeandextractuse your own model key. - Ready-made framework integrations. Anything that speaks MCP can connect, but there are no dedicated packages.
- Compliance reports. No SOC 2 or HIPAA.
What you gain
- Takeover with a queue. A person takes over and the agent's commands wait. The agent then gets
doneornot_doneand a note fromwait_for_human. - Takeover inside your product, with
postMessageevents and per-user links (Scale plan and up). - Login codes without a person.
wait_for_codetypes email or SMS codes, andvault_filltypes passwords and TOTP codes, without the value reaching your model. Both check the page's site first. - Profile locks, so parallel agents can't overwrite a login.
- One-call tools with no session:
screenshot_url,pdf_url,map_site,crawl_sitewith broken link checks, andextract_url.