Browserbase + Stagehand Developer Cheatsheet (sonnet 4 thinking)
5/30/25
The Ultimate Go-To Guide
Quick Start
Installation & Setup
npm install @browserbasehq/sdk @browserbasehq/stagehand playwright-core zod
Environment Variables
# Required
BROWSERBASE_API_KEY=your_api_key
BROWSERBASE_PROJECT_ID=your_project_id
# LLM Provider (choose one)
OPENAI_API_KEY=your_openai_key
ANTHROPIC_API_KEY=your_anthropic_key
GOOGLE_API_KEY=your_google_key
Basic Initialization
import { Stagehand } from "@browserbasehq/stagehand";
const stagehand = new Stagehand({
env: "BROWSERBASE", // or "LOCAL"
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
modelName: "gpt-4o", // or "claude-3-5-sonnet", "google/gemini-2.0-flash"
modelClientOptions: {
apiKey: process.env.OPENAI_API_KEY,
},
enableCaching: true,
debugDom: true,
});
await stagehand.init();
const page = stagehand.page; // Enhanced Playwright Page
const context = stagehand.context; // Playwright BrowserContext
Browserbase SDK
Session Creation & Management
Basic Session
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,
});
// Connect with Playwright
import { chromium } from "playwright-core";
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0].pages()[0];
Advanced Session Configuration
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
// Stealth & Anti-detection
browserSettings: {
advancedStealth: true, // Scale plan only
solveCaptchas: true, // Auto-solve captchas
blockAds: true, // Block advertisements
// Custom viewport
viewport: { width: 1920, height: 1080 },
// Fingerprinting
fingerprint: {
browsers: ["chrome", "firefox", "edge"],
devices: ["desktop", "mobile"],
locales: ["en-US", "en-GB"],
operatingSystems: ["windows", "macos", "linux"],
screen: { minWidth: 1024, maxWidth: 1920 }
},
// Context persistence
context: {
id: "context_id", // Use existing context
persist: true, // Save changes back to context
},
// Recording & Logging
recordSession: true, // Enable session replay (default)
logSession: true, // Enable logging (default)
},
// Proxy configuration
proxies: true, // Use default Browserbase proxy
// OR specific proxy config:
proxies: [{
type: 'browserbase',
geolocation: {
country: 'US',
state: 'CA',
city: 'Los Angeles'
}
}],
// Session management
keepAlive: true, // Keep session alive after disconnect
timeout: 300, // Session timeout in seconds
region: 'us-west-2', // Deployment region
// Metadata for querying
userMetadata: {
purpose: 'automation',
version: '1.0',
userId: 'user-123'
}
});
console.log(`Session URL: https://browserbase.com/sessions/${session.id}`);
Session Operations
// Get session details
const sessionInfo = await bb.sessions.retrieve(session.id);
// Get live debug URL
const { debuggerFullscreenUrl } = await bb.sessions.debug(session.id);
// List sessions with metadata query
const sessions = await bb.sessions.list({
q: "user_metadata['purpose']:'automation'",
status: 'RUNNING'
});
// Update/release session
await bb.sessions.update(session.id, {
projectId: process.env.BROWSERBASE_PROJECT_ID,
status: 'REQUEST_RELEASE'
});
// Get session logs
const logs = await bb.sessions.logs.list(session.id);
// Get session recording
const recording = await bb.sessions.recording.retrieve(session.id);
Context Management (Session Persistence)
// Create a context for persistent state
const context = await bb.contexts.create({
projectId: process.env.BROWSERBASE_PROJECT_ID
});
// Use context in multiple sessions
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
browserSettings: {
context: {
id: context.id,
persist: true // Saves cookies, localStorage, sessionStorage
}
}
});
// Retrieve context details
const contextDetails = await bb.contexts.retrieve(context.id);
File Operations
// Upload file to session
import fs from 'node:fs';
await bb.sessions.uploads.create(session.id, {
file: fs.createReadStream('./document.pdf')
});
// Use uploaded file in automation (available at /tmp/.uploads/filename)
await page.locator("#fileUpload").setInputFiles('/tmp/.uploads/document.pdf');
// Download files from session (returns ZIP)
const downloadResponse = await bb.sessions.downloads.list(session.id);
const zipBuffer = Buffer.from(await downloadResponse.arrayBuffer());
// Extract ZIP if needed
import AdmZip from 'adm-zip';
const zip = new AdmZip(zipBuffer);
zip.getEntries().forEach(entry => {
console.log(`File: ${entry.entryName}`);
});
Stagehand Configuration
Complete Configuration Options
import type { ConstructorParams } from "@browserbasehq/stagehand";
const StagehandConfig: ConstructorParams = {
// Environment
env: "BROWSERBASE", // "BROWSERBASE" | "LOCAL"
// Browserbase credentials
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
browserbaseSessionID: undefined, // Resume existing session
// LLM Configuration
modelName: "gpt-4o", // "gpt-4o", "claude-3-5-sonnet", "google/gemini-2.0-flash"
modelClientOptions: {
apiKey: process.env.OPENAI_API_KEY,
},
// Custom LLM client: llmClient: customClient,
// Browser Settings
headless: false, // Show browser window
debugDom: true, // Enable DOM debugging
// Performance & Behavior
enableCaching: true, // Cache AI actions
domSettleTimeoutMs: 30000, // DOM settle timeout
waitForCaptchaSolves: true, // Auto-wait for captcha solving
// Logging
verbose: 1, // 0 (silent), 1 (info), 2 (debug)
logger: (logLine) => console.log(`[${logLine.category}] ${logLine.message}`),
// Browserbase session parameters
browserbaseSessionCreateParams: {
proxies: true,
browserSettings: {
solveCaptchas: true,
advancedStealth: true,
fingerprint: { devices: ["desktop"] }
}
},
// Local browser options (for env: "LOCAL")
localBrowserLaunchOptions: {
headless: false,
viewport: { width: 1280, height: 720 }
}
};
export default StagehandConfig;
Core Stagehand Methods
1. act() - AI-Powered Actions
Perform UI interactions using natural language.
// Simple action
await page.act({ action: "Click the sign in button" });
// Action with variables
await page.act({
action: "Fill in the form with name: %name% and email: %email%",
variables: {
name: "John Doe",
email: "john@example.com"
}
});
// Using observed results (for precision)
const [loginButton] = await page.observe("Find the login button");
await page.act(loginButton);
// With specific model and timeout
await page.act({
action: "Click the submit button",
modelName: "claude-3-5-sonnet",
timeoutMs: 15000
});
// Fallback pattern - try Playwright first
try {
await page.locator('button[type="submit"]').click();
} catch (error) {
await page.act({ action: "click the submit button" });
}
2. extract() - Structured Data Extraction
Extract data from pages using AI with schema validation.
import { z } from "zod";
// Simple extraction
const { extraction } = await page.extract("Extract the page title");
// Structured extraction with schema
const productInfo = await page.extract({
instruction: "Extract product details from this page",
schema: z.object({
name: z.string(),
price: z.number(),
inStock: z.boolean(),
rating: z.number().optional(),
reviews: z.array(z.object({
rating: z.number(),
comment: z.string()
})).optional()
})
});
// Extract arrays
const searchResults = await page.extract({
instruction: "Extract all search results",
schema: z.object({
results: z.array(z.object({
title: z.string(),
url: z.string().url(), // Stagehand handles URL mapping
snippet: z.string()
}))
}),
useTextExtract: true // Better for large content
});
// Targeted extraction with selector
const { price } = await page.extract({
instruction: "Extract the current price",
selector: ".price-display",
schema: z.object({ price: z.string() })
});
// Text-only extraction (no LLM call)
const { page_text } = await page.extract();
3. observe() - Element Discovery
Find elements and their selectors dynamically.
// Find interactive elements
const buttons = await page.observe("Find all clickable buttons on the page");
// Returns: [{ selector: "button#submit", description: "Submit button" }, ...]
// Find specific elements with action suggestion
const searchElements = await page.observe({
instruction: "Find the search input field",
returnAction: true // Include suggested method and arguments
});
// Use observed elements with caching
const cacheKey = "login_button";
let loginButton = await getCache(cacheKey);
if (!loginButton) {
[loginButton] = await page.observe("Find the login button");
await setCache(cacheKey, loginButton);
}
await page.act(loginButton);
// Draw overlay on observed elements (debugging)
const elements = await page.observe({
instruction: "Find all form inputs",
drawOverlay: true
});
Agent Mode (Autonomous Tasks)
OpenAI/Anthropic Computer Use Agent
const agent = stagehand.agent({
provider: "openai", // or "anthropic"
model: "computer-use-preview", // For OpenAI CUA
// model: "claude-3-5-sonnet-20241022", // For Anthropic CUA
instructions: `You are a helpful web assistant.
Current page: ${page.url()}
Be thorough but concise in your actions.
Do not ask follow-up questions.`,
options: {
apiKey: process.env.OPENAI_API_KEY, // or ANTHROPIC_API_KEY
}
});
const result = await agent.execute({
instruction: "Search for the latest iPhone on Apple's website and get the price",
maxSteps: 15,
autoScreenshot: true, // Default: true
waitBetweenActions: 1000 // Default: 0ms
});
console.log(result.message); // Summary of what was accomplished
console.log(result.completed); // Boolean: task completed successfully
console.log(result.actions); // Array of actions taken
console.log(result.usage); // Token usage statistics
Default Stagehand Operator
const agent = stagehand.agent(); // Uses built-in operator logic
const result = await agent.execute("Navigate to news site and get top headlines");
Common Patterns & Workflows
Complete E-commerce Automation
async function automateProductPurchase() {
const stagehand = new Stagehand(StagehandConfig);
await stagehand.init();
try {
// Navigate to store
await stagehand.page.goto("https://store.example.com");
// Search for product
await stagehand.page.act({
action: "Search for 'wireless headphones' in the search bar"
});
// Extract product listings
const products = await stagehand.page.extract({
instruction: "Get the first 5 products with name, price, and rating",
schema: z.object({
products: z.array(z.object({
name: z.string(),
price: z.string(),
rating: z.number().optional(),
url: z.string().optional()
}))
})
});
// Click on first product
await stagehand.page.act({
action: "Click on the first product in the search results"
});
// Add to cart
await stagehand.page.act({
action: "Add the product to cart"
});
// Navigate to cart
await stagehand.page.act({
action: "Go to shopping cart"
});
// Extract cart summary
const cartInfo = await stagehand.page.extract({
instruction: "Extract cart total and item count",
schema: z.object({
itemCount: z.number(),
total: z.string(),
items: z.array(z.object({
name: z.string(),
price: z.string(),
quantity: z.number()
}))
})
});
return { products, cartInfo };
} finally {
await stagehand.close();
}
}
Authentication & Session Persistence
async function setupAuthenticatedSession() {
// Create persistent context
const context = await bb.contexts.create({
projectId: process.env.BROWSERBASE_PROJECT_ID
});
// Login session
const loginSession = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
browserSettings: {
context: { id: context.id, persist: true }
}
});
const stagehand = new Stagehand({
...StagehandConfig,
browserbaseSessionID: loginSession.id
});
await stagehand.init();
// Perform login
await stagehand.page.goto("https://example.com/login");
await stagehand.page.act({
action: "Login with email %email% and password %password%",
variables: {
email: "user@example.com",
password: "securepassword"
}
});
await stagehand.close();
// Later: Use authenticated context in new session
const authenticatedSession = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
browserSettings: {
context: { id: context.id, persist: false }
}
});
return authenticatedSession;
}
Form Automation with Validation
async function fillComplexForm(formData: any) {
// Try structured approach first
try {
await page.act({
action: `Fill out the registration form with:
First Name: %firstName%
Last Name: %lastName%
Email: %email%
Phone: %phone%
Company: %company%`,
variables: formData
});
} catch (error) {
// Fallback: field by field
for (const [field, value] of Object.entries(formData)) {
await page.act({
action: `Enter "${value}" in the ${field} field`
});
}
}
// Validate form before submission
const validation = await page.extract({
instruction: "Check if there are any validation errors on the form",
schema: z.object({
hasErrors: z.boolean(),
errors: z.array(z.string()).optional()
})
});
if (!validation.hasErrors) {
await page.act({ action: "Submit the form" });
} else {
console.log("Form validation errors:", validation.errors);
}
}
Robust Error Handling
async function robustAutomation(page: any) {
const maxRetries = 3;
let retries = 0;
while (retries < maxRetries) {
try {
// Try Playwright first (faster)
await page.waitForSelector('#submit-btn', { timeout: 5000 });
await page.click('#submit-btn');
break;
} catch (playwrightError) {
try {
// Fallback to AI
await page.act({ action: "click the submit button" });
break;
} catch (aiError) {
retries++;
if (retries === maxRetries) {
throw new Error(`Failed after ${maxRetries} attempts: ${aiError.message}`);
}
await page.waitForTimeout(2000); // Wait before retry
}
}
}
}
Advanced Features
Proxy Configuration
// Geolocation-specific proxy
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
proxies: [{
type: 'browserbase',
geolocation: {
country: 'GB',
city: 'London'
}
}]
});
// Multiple proxy configurations
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
proxies: [
{
type: 'external',
server: 'http://proxy1.example.com:8080',
username: 'user1',
password: 'pass1',
domainPattern: '.*\\.gov' // Only for .gov domains
},
{
type: 'browserbase', // Fallback for other domains
geolocation: { country: 'US' }
}
]
});
Custom CAPTCHA Handling
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
browserSettings: {
solveCaptchas: true,
captchaImageSelector: '#custom-captcha-image',
captchaInputSelector: '#custom-captcha-input'
}
});
Extensions
// Upload extension
import { createReadStream } from 'node:fs';
const extension = await bb.extensions.create({
file: createReadStream('path/to/extension.zip')
});
// Use extension in session
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
extensionId: extension.id
});
Custom LLM Clients
import { LLMClient } from "@browserbasehq/stagehand";
class CustomLLMClient extends LLMClient {
// Implement your custom LLM integration
}
const stagehand = new Stagehand({
...StagehandConfig,
llmClient: new CustomLLMClient({
modelName: "custom-model",
// your custom configuration
})
});
Debugging & Monitoring
Session Monitoring
// Get live debug URL
const debugInfo = await bb.sessions.debug(session.id);
console.log(`Live view: ${debugInfo.debuggerFullscreenUrl}`);
console.log(`Session replay: https://browserbase.com/sessions/${session.id}`);
// Monitor session status
const sessionDetails = await bb.sessions.retrieve(session.id);
console.log(`Status: ${sessionDetails.status}`);
console.log(`Proxy bytes used: ${sessionDetails.proxyBytes}`);
console.log(`Duration: ${sessionDetails.durationMs}ms`);
Stagehand Metrics & History
// Access performance metrics
console.log(stagehand.metrics);
// { actPromptTokens, actCompletionTokens, totalInferenceTimeMs, ... }
// View action history
console.log(stagehand.history);
// Array of all Stagehand method calls with parameters and results
Logging Configuration
const StagehandConfig = {
// ... other config
verbose: 2, // Maximum logging
logger: (logLine) => {
if (logLine.category === 'error') {
console.error(`❌ [${logLine.category}] ${logLine.message}`);
} else if (logLine.category === 'info') {
console.log(`ℹ️ [${logLine.category}] ${logLine.message}`);
}
}
};
Best Practices
1. Action Design
// ✅ Good - Atomic and specific
await page.act({ action: "Click the 'Add to Cart' button" });
await page.act({ action: "Type 'iPhone 15' into the search box" });
// ❌ Bad - Too broad or multi-step
await page.act({ action: "Buy the product and complete checkout" });
await page.act({ action: "Navigate and search for products" });
2. Schema Best Practices
// ✅ Good - Specific types with descriptions
const schema = z.object({
products: z.array(z.object({
name: z.string().describe("Product name"),
price: z.string().describe("Price as displayed (e.g., '$19.99')"),
originalPrice: z.string().optional().describe("Original price if on sale"),
inStock: z.boolean().describe("Whether product is available"),
rating: z.number().min(0).max(5).optional().describe("Star rating out of 5"),
}))
});
// ❌ Bad - Too generic
const schema = z.object({
data: z.any()
});
3. Caching Strategy
// Cache observed elements for reuse
const observeCache = new Map();
async function cachedObserve(instruction: string) {
if (observeCache.has(instruction)) {
return observeCache.get(instruction);
}
const result = await page.observe(instruction);
observeCache.set(instruction, result);
return result;
}
// Use cached observations
const [searchButton] = await cachedObserve("Find the search button");
await page.act(searchButton);
4. Resource Management
async function automationWithCleanup() {
const stagehand = new Stagehand(config);
try {
await stagehand.init();
// Your automation code here
} catch (error) {
console.error("Automation failed:", error);
throw error;
} finally {
// Always cleanup
await stagehand.close();
// Release Browserbase session if needed
if (stagehand.browserbaseSessionID) {
await bb.sessions.update(stagehand.browserbaseSessionID, {
projectId: process.env.BROWSERBASE_PROJECT_ID,
status: 'REQUEST_RELEASE'
});
}
}
}
5. Performance Optimization
// Enable caching
const config = {
...StagehandConfig,
enableCaching: true,
// Optimize DOM settle time based on your needs
domSettleTimeoutMs: 10000, // Faster for simple pages
// Use text extraction for large content
// (in extract calls: useTextExtract: true)
};
// Batch similar extractions
const allData = await page.extract({
instruction: "Extract all product information, user reviews, and pricing details",
schema: z.object({
products: z.array(/* product schema */),
reviews: z.array(/* review schema */),
pricing: z.object(/* pricing schema */)
})
});
Quick Reference
Essential Commands
| Task | Browserbase SDK | Stagehand |
|---|---|---|
| Create session | `bb.sessions.create({projectId})` | Handled automatically |
| Connect browser | `chromium.connectOverCDP(url)` | `stagehand.init()` |
| Click element | Standard Playwright | `page.act({action: "click..."})` |
| Extract data | Manual DOM parsing | `page.extract({schema})` |
| Find elements | CSS/XPath selectors | `page.observe({instruction})` |
| Debug session | `bb.sessions.debug(id)` | Built-in with Browserbase |
| Upload files | `bb.sessions.uploads.create()` | Same + use in automation |
| Download files | `bb.sessions.downloads.list()` | Same |
Configuration Quick Reference
// Minimal Stagehand setup
const stagehand = new Stagehand({
env: "BROWSERBASE",
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
modelName: "gpt-4o",
modelClientOptions: { apiKey: process.env.OPENAI_API_KEY },
});
// Production-ready setup
const stagehand = new Stagehand({
env: "BROWSERBASE",
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
modelName: "gpt-4o",
modelClientOptions: { apiKey: process.env.OPENAI_API_KEY },
enableCaching: true,
verbose: 1,
browserbaseSessionCreateParams: {
proxies: true,
browserSettings: {
solveCaptchas: true,
advancedStealth: true,
recordSession: true,
},
keepAlive: true,
userMetadata: { environment: "production" }
}
});
This cheatsheet combines the best practices and comprehensive coverage from multiple expert sources. For the latest updates, check the official Browserbase and Stagehand documentation.
Version: BestOfWorlds-2025