🐍 Looking for Stagehand in Python ? Switch to v2 →
Recommended Migration Process
Backup your project . If you use a version control system, make sure all previous versions are committed.
Upgrade to Stagehand v3 .
Follow the breaking changes guide below.
Verify your project is working as expected.
Commit your changes.
Stagehand v3 Package Version
Update your package.json to use Stagehand v3:
npm install @browserbasehq/stagehand@latest
Overview of Major Changes
Stagehand v3 introduces significant improvements to the API design and functionality:
Removing Playwright Dependency : Stagehand v3 is now a standalone library that does not depend on Playwright. You can still use Stagehand with Playwright, check out our Playwright integration for more details.
Simplified Method Signatures : Cleaner, more intuitive parameter structures.
Unified Model Configuration : Model configuration is now consolidated into a single model parameter.
Automatic iframe & Shadow DOM Support : No more manual flags required.
Improved Type Safety : Better TypeScript inference and type checking.
Enhanced Multi-Page Support : New Context API for managing multiple pages.
Streamlined Timeouts : Consistent timeout naming across all methods.
Auto-caching : Stagehand v3 now automatically caches actions and agent steps using the file system cache .
Agent Improvements : Renamed parameters (instructions → systemPrompt), unified model configuration, and new executionModel option for cost optimization.
Breaking Changes
Stagehand Initialization
Model Configuration Consolidation
The modelName and modelClientOptions parameters have been unified into a single model parameter.
Stagehand v2
import { Stagehand } from "@browserbasehq/stagehand" ;
const stagehand = new Stagehand ({
env: "BROWSERBASE" ,
apiKey: process . env . BROWSERBASE_API_KEY ,
projectId: process . env . BROWSERBASE_PROJECT_ID ,
modelName: "openai/gpt-5" ,
modelClientOptions: {
apiKey: process . env . OPENAI_API_KEY
baseURL : "https://custom-proxy.com/v1"
}
});
Stagehand v3
import { Stagehand } from "@browserbasehq/stagehand" ;
// Option 1: String format (recommended for simplicity, auto-loads model API key from env)
const stagehand = new Stagehand ({
env: "BROWSERBASE" ,
apiKey: process . env . BROWSERBASE_API_KEY ,
projectId: process . env . BROWSERBASE_PROJECT_ID ,
model: "openai/gpt-5"
});
// Option 2: Object format (for advanced configuration)
const stagehand = new Stagehand ({
env: "BROWSERBASE" ,
apiKey: process . env . BROWSERBASE_API_KEY ,
projectId: process . env . BROWSERBASE_PROJECT_ID ,
model: {
modelName: "gpt-5" ,
apiKey: process . env . OPENAI_API_KEY ,
baseURL: "https://custom-proxy.com/v1"
}
});
DOM Settle Timeout Rename
The domSettleTimeoutMs parameter has been renamed to domSettleTimeout for consistency.
Stagehand v2
const stagehand = new Stagehand ({
env: "LOCAL" ,
domSettleTimeoutMs: 5000
});
Stagehand v3
const stagehand = new Stagehand ({
env: "LOCAL" ,
domSettleTimeout: 5000
});
Changes to return value of stagehand.init()
The init() used to return debugUrl, sessionUrl and sessionId
Stagehand v2
const result = await stagehand . init ();
console . log ( result );
In v2, the returned object contains:
{
debugUrl: 'https://www.browserbase.com/devtools/inspector.html?wss=connect.browserbase.com/debug/f8a21b4a-6fa1-4ab9-9007-fbfe61dc14f0/devtools/page/5474B0E0510C5B6E629BEB06E799CD70?debug=true',
sessionUrl: 'https://www.browserbase.com/sessions/f8a21b4a-6fa1-4ab9-9007-fbfe61dc14f0',
sessionId: 'f8a21b4a-6fa1-4ab9-9007-fbfe61dc14f0'
}
In v3 the return value is Promise<void>. The sessionId, sessionUrl, and debugUrl are now directly accessible via the stagehand object:
Stagehand v3
console . log ( "debugUrl: " , stagehand . browserbaseDebugURL )
console . log ( "sessionUrl: " , stagehand . browserbaseSessionURL )
console . log ( "sessionId: " , stagehand . browserbaseSessionID )
Example output:
debugUrl: 'https://www.browserbase.com/devtools/inspector.html?wss=connect.browserbase.com/debug/f8a21b4a-6fa1-4ab9-9007-fbfe61dc14f0/devtools/page/5474B0E0510C5B6E629BEB06E799CD70?debug=true',
sessionUrl: 'https://www.browserbase.com/sessions/f8a21b4a-6fa1-4ab9-9007-fbfe61dc14f0',
sessionId: 'f8a21b4a-6fa1-4ab9-9007-fbfe61dc14f0'
Caching Changes
The enableCaching boolean has been replaced with a cacheDir string for more flexible cache management.
Stagehand v2
const stagehand = new Stagehand ({
env: "LOCAL" ,
enableCaching: true
});
Stagehand v3
const stagehand = new Stagehand ({
env: "LOCAL" ,
cacheDir: "./stagehand-cache" // Specify cache directory
});
Page Access Changes
Direct page access has changed to use the Context API.
Stagehand v2
const stagehand = new Stagehand ({ env: "LOCAL" });
await stagehand . init ();
// Direct page access
const page = stagehand . page ;
await page . goto ( "https://example.com" );
Stagehand v3
const stagehand = new Stagehand ({ env: "LOCAL" });
await stagehand . init ();
// Access via context
const page = stagehand . context . pages ()[ 0 ];
await page . goto ( "https://example.com" );
// Or `await` the active page
const page = stagehand . context . awaitActivePage ();
await page . goto ( "https://example.com" );
Context and Multi-Page Management
New Context API
v3 introduces a structured Context API for managing multiple pages.
Stagehand v2
const stagehand = new Stagehand ({ env: "LOCAL" });
await stagehand . init ();
// Limited multi-page support
const page = stagehand . page ;
Stagehand v3
const stagehand = new Stagehand ({ env: "LOCAL" });
await stagehand . init ();
// Access all pages
const pages = stagehand . context . pages ();
const mainPage = pages [ 0 ];
// Create new page
const newPage = await stagehand . context . newPage ();
// Set active page
stagehand . context . setActivePage ( newPage );
// implicitly takes action on newPage
await stagehand . act ( "click button" );
act() Method Changes
Method Signature Simplification
The action parameter has been removed from ActOptions. Now you only pass the instruction as a string.
Stagehand v2
await page . act ({
action: "click the login button" ,
modelName: "openai/gpt-5-mini" ,
variables: { username: "john" },
timeoutMs: 10000 ,
domSettleTimeoutMs: 5000 ,
iframes: true
});
Stagehand v3
// Clean, simple string instruction
await stagehand . act ( "click the login button" );
// With options
await stagehand . act ( "click the login button" , {
model: "openai/gpt-5-mini" ,
variables: { username: "john" },
timeout: 10000 ,
page: page // Optional: specify which page
});
Method Location Change : In v3, act() is called on the stagehand instance, not the page object.
Model Configuration in act()
Model configuration follows the same pattern as initialization.
Stagehand v2
await page . act ({
action: "fill the form" ,
modelName: "anthropic/claude-sonnet-4-5" ,
modelClientOptions: {
apiKey: process . env . ANTHROPIC_API_KEY
}
});
Stagehand v3
// String format
await stagehand . act ( "fill the form" , {
model: "anthropic/claude-sonnet-4-5"
});
// Object format
await stagehand . act ( "fill the form" , {
model: {
modelName: "anthropic/claude-sonnet-4-5" ,
apiKey: process . env . ANTHROPIC_API_KEY
}
});
Timeout Parameter Rename
timeoutMs has been renamed to timeout.
Stagehand v2
await page . act ({
action: "click button" ,
timeoutMs: 15000
});
Stagehand v3
await stagehand . act ( "click button" , {
timeout: 15000
});
Automatic iframe Support
The iframes flag has been removed. iframe support is now automatic.
Stagehand v2
await page . act ({
action: "click button inside iframe" ,
iframes: true // Required to interact with iframes
});
Stagehand v3
// Automatic iframe support - no flag needed
await stagehand . act ( "click button inside iframe" );
Automatic Support : Stagehand v3 automatically handles iframe and Shadow DOM interactions without requiring explicit flags.
Result Structure Changes
The ActResult structure has been enhanced with more detailed information.
Stagehand v2
const result = await page . act ( "click the button" );
console . log ( result . action ); // Single action string
Stagehand v3
const result = await stagehand . act ( "click the button" );
console . log ( result . actionDescription ); // Overall description
console . log ( result . actions ); // Array of action details
// ActResult structure:
// {
// success: boolean;
// message: string;
// actionDescription: string;
// actions: Array<{
// selector: string;
// description: string;
// method?: string;
// arguments?: string[];
// }>;
// }
Method Location and Signature
extract() has moved from the page object to the stagehand instance, with a cleaner parameter structure.
Stagehand v2
import { z } from 'zod/v3' ;
const result = await page . extract ({
instruction: "extract product details" ,
schema: z . object ({
name: z . string (),
price: z . number ()
}),
modelName: "openai/gpt-5" ,
domSettleTimeoutMs: 5000 ,
selector: "xpath=/html/body/div" ,
iframes: true
});
Stagehand v3
import { z } from 'zod/v3' ;
// Cleaner parameter structure
const result = await stagehand . extract (
"extract product details" ,
z . object ({
name: z . string (),
price: z . number ()
}),
{
model: "openai/gpt-5" ,
selector: ".container" , // NEW: CSS selector support
timeout: 10000 ,
page: page // Optional: specify which page
}
);
Parameter Order : In v3, instruction and schema are separate positional parameters, with options as an optional third parameter.
Schema-less extraction also has a simpler interface.
Stagehand v2
// String instruction
const result = await page . extract ( "get the page title" );
// Returns: { extraction: "Page Title" }
// Raw page content
const content = await page . extract ();
// Returns: { page_text: "..." }
Stagehand v3
// String instruction
const result = await stagehand . extract ( "get the page title" );
// Returns: { extraction: "Page Title" }
// Raw page content
const content = await stagehand . extract ();
// Returns: { pageText: "..." }
Stagehand v2
const data = await page . extract ({
instruction: "extract data" ,
schema: DataSchema ,
modelName: "anthropic/claude-sonnet-4-5" ,
modelClientOptions: {
apiKey: process . env . ANTHROPIC_API_KEY
}
});
Stagehand v3
const data = await stagehand . extract (
"extract data" ,
DataSchema ,
{
model: "anthropic/claude-sonnet-4-5"
}
);
Automatic iframe Support
Stagehand v2
const data = await page . extract ({
instruction: "extract data from iframe" ,
schema: DataSchema ,
iframes: true // Required for iframe content
});
Stagehand v3
// Automatic iframe support
const data = await stagehand . extract (
"extract data from iframe" ,
DataSchema
);
Array Schema Changes
Array extraction now has a more ergonomic syntax.
Stagehand v2
import { z } from 'zod/v3' ;
// Had to wrap array in object
const ApartmentListingsSchema = z . object ({
apartments: z . array ( z . object ({
address: z . string (),
price: z . string (),
bedrooms: z . number ()
}))
});
const result = await page . extract ({
instruction: "extract all apartment listings" ,
schema: ApartmentListingsSchema
});
// Access via: result.apartments
Stagehand v3
import { z } from 'zod/v3' ;
// Can use array schema directly
const ApartmentListingsSchema = z . array (
z . object ({
address: z . string (),
price: z . string (),
bedrooms: z . number ()
})
);
const result = await stagehand . extract (
"extract all apartment listings" ,
ApartmentListingsSchema
);
// Result is directly the array
console . log ( result [ 0 ]. address );
observe() Method Changes
Method Signature Updates
Stagehand v2
const results = await page . observe ({
instruction: "find all buttons" ,
modelName: "openai/gpt-5" ,
domSettleTimeoutMs: 5000 ,
drawOverlay: true ,
iframes: true
});
Stagehand v3
const results = await stagehand . observe ( "find all buttons" , {
model: "openai/gpt-5" ,
timeout: 10000 ,
selector: ".container" , // NEW: scope observation to selector
page: page // Optional: specify which page
});
Method Location Change : Like act() and extract(), observe() is now called on the stagehand instance.
Draw Overlay Removed
The drawOverlay option has been removed in v3.
Stagehand v2
const results = await page . observe ({
instruction: "find buttons" ,
drawOverlay: true // Visual debugging
});
Stagehand v3
// drawOverlay is no longer available
const results = await stagehand . observe ( "find buttons" );
Automatic iframe Support
Stagehand v2
const results = await page . observe ({
instruction: "find elements in iframe" ,
iframes: true
});
Stagehand v3
// Automatic iframe support
const results = await stagehand . observe ( "find elements in iframe" );
Observe with act() Integration
The observe → act workflow remains similar but with updated method signatures.
Stagehand v2
const [ action ] = await page . observe ( "find the login button" );
await page . act ( action );
Stagehand v3
const [ action ] = await stagehand . observe ( "find the login button" );
await stagehand . act ( action );
agent() Method Changes
Agent Configuration Updates
The agent configuration has been significantly restructured in v3 with renamed parameters and new capabilities.
Stagehand v2
const agent = stagehand . agent ({
provider: "google" ,
model: "gemini-2.5-computer-use-preview-10-2025" ,
instructions: "You are a helpful assistant that can navigate websites." ,
options: {
apiKey: process . env . GEMINI_API_KEY
},
integrations: [ "https://mcp-server.example.com" ],
tools: customTools
});
Stagehand v3
const agent = stagehand . agent ({
model: "google/gemini-2.5-computer-use-preview-10-2025" , // Provider now in model string
systemPrompt: "You are a helpful assistant that can navigate websites." , // Renamed from 'instructions'
cua: true , // NEW: Computer Use Agent mode
integrations: [ "https://mcp-server.example.com" ],
tools: customTools
});
Key Changes :
provider removed - now part of the model string (e.g., "anthropic/claude-sonnet-4-5")
instructions renamed to systemPrompt
options removed - use model object format for advanced configuration
executionModel added - specify a different model for tool execution
cua flag added - enable/disable Computer Use Agent mode
Model Configuration in agent()
Model configuration follows the same unified pattern as other methods.
Stagehand v2
const agent = stagehand . agent ({
provider: "google" ,
model: "gemini-2.5-computer-use-preview-10-2025" ,
options: {
apiKey: process . env . GEMINI_API_KEY ,
baseURL: "https://custom-proxy.com/v1"
}
});
Stagehand v3
// String format (recommended)
const agent = stagehand . agent ({
model: "google/gemini-2.5-computer-use-preview-10-2025"
});
// Object format for advanced configuration
const agent = stagehand . agent ({
model: {
modelName: "gemini-2.5-computer-use-preview-10-2025" ,
apiKey: process . env . GEMINI_API_KEY ,
baseURL: "https://custom-proxy.com/v1"
}
});
Execute Method Changes
The execute() method has been simplified with some options removed and new ones added.
Stagehand v2
const result = await agent . execute ({
instruction: "Search for products" ,
maxSteps: 20 ,
autoScreenshot: true ,
waitBetweenActions: 1000 ,
context: "Focus on electronics category"
});
Stagehand v3
const result = await agent . execute ({
instruction: "Search for products" ,
maxSteps: 20 ,
page: page , // NEW: specify which page to operate on
highlightCursor: true // NEW: visual cursor for debugging
});
Removed Options :
autoScreenshot - no longer available
waitBetweenActions - no longer available
context - use the systemPrompt in agent config instead
Execution Model Configuration
v3 introduces a new executionModel option to use a different (often faster/cheaper) model for tool execution.
Stagehand v3
const agent = stagehand . agent ({
model: "anthropic/claude-sonnet-4-5" , // Main reasoning model
executionModel: "anthropic/claude-haiku-4-5" // Faster model for tool execution (act, extract, observe)
});
// The agent will use claude-sonnet-4-5 for high-level reasoning
// but claude-haiku-4-5 for executing individual actions
const result = await agent . execute ( "Complete the checkout process" );
Agent with Multi-Page Support
v3 agents can now specify which page to operate on.
Stagehand v3
const page1 = stagehand . context . pages ()[ 0 ]
const page2 = await stagehand . context . newPage ();
const agent = stagehand . agent ({
model: "google/gemini-2.5-computer-use-preview-10-2025"
});
// Execute on specific page
await page2 . goto ( "https://example.com/dashboard" );
const result = await agent . execute ({
instruction: "Export the data table" ,
page: page2 // Operate on page2 instead of default page
});
History and Metrics
History API
History is now async and returns a promise.
Stagehand v2
const history = stagehand . history ;
Stagehand v3
const history = await stagehand . history ;
Metrics API
Metrics is now async and returns a promise.
Stagehand v2
const metrics = stagehand . metrics ;
Stagehand v3
const metrics = await stagehand . metrics ;
Complete Migration Example
Here’s a complete example showing a full migration:
Stagehand v2
Stagehand v3
import { Stagehand } from "@browserbasehq/stagehand" ;
import { z } from 'zod/v3' ;
// Initialize
const stagehand = new Stagehand ({
env: "BROWSERBASE" ,
apiKey: process . env . BROWSERBASE_API_KEY ,
projectId: process . env . BROWSERBASE_PROJECT_ID ,
modelName: "openai/gpt-5" ,
modelClientOptions: {
apiKey: process . env . OPENAI_API_KEY
},
enableCaching: true ,
domSettleTimeoutMs: 5000
});
await stagehand . init ();
const page = stagehand . page ;
// Navigate
await page . goto ( "https://example.com" );
// Act
await page . act ({
action: "click the login button" ,
timeoutMs: 10000 ,
iframes: true
});
// Extract
const ProductSchema = z . object ({
name: z . string (),
price: z . number (),
inStock: z . boolean ()
});
const product = await page . extract ({
instruction: "extract product details" ,
schema: ProductSchema ,
domSettleTimeoutMs: 5000 ,
iframes: true
});
// Observe
const actions = await page . observe ({
instruction: "find all buttons" ,
drawOverlay: false ,
iframes: true
});
await stagehand . close ();
import { Stagehand } from "@browserbasehq/stagehand" ;
import { z } from 'zod/v3' ;
// Initialize - simplified configuration
const stagehand = new Stagehand ({
env: "BROWSERBASE" ,
apiKey: process . env . BROWSERBASE_API_KEY ,
projectId: process . env . BROWSERBASE_PROJECT_ID ,
model: "openai/gpt-5" , // Unified model configuration
cacheDir: "./cache" , // Flexible cache directory
domSettleTimeout: 5000 // Consistent naming
});
await stagehand . init ();
const page = stagehand . context . pages ()[ 0 ]; // Context API
// Navigate
await page . goto ( "https://example.com" );
// Act - cleaner interface, automatic iframe support
await stagehand . act ( "click the login button" , {
timeout: 10000
// No iframes flag needed - automatic!
});
// Extract - cleaner parameter order
const ProductSchema = z . object ({
name: z . string (),
price: z . number (),
inStock: z . boolean ()
});
const product = await stagehand . extract (
"extract product details" ,
ProductSchema
// Automatic iframe support, no extra flags needed
);
// Observe - simplified
const actions = await stagehand . observe ( "find all buttons" );
// Automatic iframe support
// Get metrics
const metrics = await stagehand . metrics ;
console . log ( 'Total tokens used:' ,
metrics . totalPromptTokens + metrics . totalCompletionTokens );
await stagehand . close ();
Quick Reference: Breaking Changes
Show Stagehand Initialization
Feature Stagehand v2 Stagehand v3 Model Config modelName + modelClientOptionsmodel: "provider/model" or { modelName, apiKey, baseURL }DOM Settle domSettleTimeoutMsdomSettleTimeoutCaching enableCaching: booleancacheDir: stringPage Access stagehand.pagestagehand.context.pages()[0]
Feature Stagehand v2 Stagehand v3 Method location page.act()stagehand.act()Parameters { action, ...options }(instruction, options?)Timeout timeoutMstimeoutResult structure { action }{ actionDescription, actions[] }
Feature Stagehand v2 Stagehand v3 Method location page.extract()stagehand.extract()Parameters { instruction, schema, ...options }(instruction, schema, options?)
Feature Stagehand v2 Stagehand v3 Method location page.observe()stagehand.observe()Draw overlay drawOverlay: booleanRemoved
Feature Stagehand v2 Stagehand v3 Provider provider: "openai" | "anthropic"Part of model string Instructions instructions: stringsystemPrompt: stringModel model: "model-name"model: "provider/model-name"Options options: Record<string, unknown>Use model object format Execute params autoScreenshot, waitBetweenActions, contextRemoved; added page, highlightCursor
Feature Stagehand v2 Stagehand v3 iframe support iframes: true flag requiredAutomatic (no flag needed) Shadow DOM Manual handling Automatic (no flag needed)
Show Properties & Methods
Feature Stagehand v2 Stagehand v3 History stagehand.historyawait stagehand.historyMetrics stagehand.metricsawait stagehand.metrics
Troubleshooting
Error: Cannot find property ‘page’ on Stagehand instance
Problem : Direct stagehand.page is not supported in Stagehand v3.
Solution : Use the Context API or await the active page:
// Use context API (recommended)
const page = stagehand . context . pages ()[ 0 ];
// Or grab the active page
const page = await stagehand . context . awaitActivePage ();
Error: act() method not found on page
Problem : v3 moved act(), extract(), and observe() to the stagehand instance.
Solution : Call these methods on the stagehand instance:
// v2 ❌
await page . act ( "click button" );
// v3 ✅
await stagehand . act ( "click button" );
TypeScript: Model configuration type errors
Problem : TypeScript errors with model configuration.
Solution : Use the proper format:
// String format
model : "openai/gpt-5"
// Object format
model : {
modelName : "openai/gpt-5" ,
apiKey : process . env . OPENAI_API_KEY
}
Agent configuration errors
Problem : Using old provider and instructions parameters.
Solution : Update to v3 format:
// v2 ❌
const agent = stagehand . agent ({
provider: "anthropic" ,
model: "claude-sonnet-4-5" ,
instructions: "You are a helpful assistant that..."
});
// v3 ✅
const agent = stagehand . agent ({
model: "anthropic/claude-sonnet-4-5" ,
systemPrompt: "You are a helpful assistant that..."
});
Agent execute options not recognized
Problem : Using removed options like autoScreenshot, waitBetweenActions, or context.
Solution : Remove these options and use v3 alternatives:
// v2 ❌
await agent . execute ({
instruction: "task" ,
autoScreenshot: true ,
waitBetweenActions: 1000 ,
context: "additional context"
});
// v3 ✅
const agent = stagehand . agent ({
model: "google/gemini-2.5-computer-use-preview-10-2025" ,
systemPrompt: "Your context here." // Move context to systemPrompt
});
await agent . execute ({
instruction: "task" ,
highlightCursor: true // Use new option for visual feedback
});
Best Practices for v3
Use the string model format for simplicity: model: "openai/gpt-5"
Leverage automatic iframe support - remove all iframes flags
Use the Context API for multi-page scenarios
Monitor metrics to track token usage and optimize costs
Use history for debugging and understanding automation flow
Set appropriate timeouts based on your use case
Specify cache directory to improve performance for repeated actions
Use executionModel for agents - configure a faster/cheaper model for tool execution while keeping a powerful model for reasoning (e.g., model: "anthropic/claude-sonnet-4-5", executionModel: "google/gemini-2.0-flash")
Additional Resources
If you encounter any issues during migration, please open an issue on our GitHub repository.