Browserbase + Stagehand Developer Cheatsheet (sonnet 4 thinking)

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