Skip to main content
Two changes account for most of this migration:
  1. agent() is gone. Nothing in v4 replaces it one-for-one.
  2. The SDK surface moved. Construction, page access, and result shapes changed.
act(), extract(), and observe() still exist and still take natural-language instructions, but their place has changed.

Why agent() is gone

agent() was built for models that couldn’t reliably drive a browser on their own. It wrapped act(), extract(), and observe() in a loop and asked the model to pick one tool per step, which was the right shape for the models available at the time. Now, that built-in orchestrator is gone. v4 exposes discrete tools and leaves the control flow to you. Keep calling act(), extract(), and observe() where a natural-language instruction beats a selector, but stop treating them as the whole toolset you hand a model. Two approaches replace it:
  • Code mode puts the model in front of the run. A coding assistant writes a Stagehand script, and you run that script. Browserbase recommends starting here.
  • Tool calling keeps a model in the loop during the run, driving the browser through the full Stagehand API as its tools.

Code mode

Ask your coding assistant to write a Stagehand script, then run the script. The model writes the code once instead of driving the browser on every run. You get ordinary code: reviewable, diffable, and free of per-step inference. When a site changes, re-run the assistant on the step that broke. Start with AI rules. It carries the rule files and MCP servers that keep generated code on the v4 API instead of the v2 and v3 patterns in training data. Here’s a prompt that produces a working script:
What comes back should read like the script you would have written yourself:
Generated code should use page.locator() and page.goto() wherever a selector is stable, and spend a model call only where the page needs judgement. agent() couldn’t make that split, because every step it ran was an inference call.

Tool calling

To keep a model in the loop at runtime, give it the whole Stagehand surface rather than three broad tools. Each method maps to one tool with a narrow contract, so a step names a specific browser operation instead of routing through a sentence of English. Expose the real API: page.snapshot() anchors the loop. It returns formattedTree, the accessibility tree, plus an xpathMap, so the model reads real page structure and hands back a selector you can drive deterministically.
Escalate on observe(), never on act(). A failed act() may already have clicked, submitted, or paid before the error surfaced, so retrying it can repeat the side effect. observe() only plans, so retrying it is free. Cost optimization applies the same idea to model escalation.

Let a coding assistant do the rest

The rest of this guide is a mapping table, so hand it to a coding assistant. Point it at this page instead of retyping the rules:
Set up AI rules first so the assistant stays on the v4 API instead of the v2 and v3 patterns in its training data. Then work through the sections below for anything it missed.
  1. Get one script constructing and closing cleanly on v4, before changing any instructions.
  2. Replace page and context access, since it moved.
  3. Unwrap results: every primitive now returns { data, metadata }.
  4. Replace agent() calls with code mode or tool calling.
  5. Turn on server-side caching once the flow is stable.

Coming from Python or Go

v3 shipped Python and Go as clients for the hosted Stagehand API. You created a session and called methods on it. Python:
Go:
v4 gives all three languages the same SDK: a browser from a factory, a Stagehand instance built on it, and act(), extract(), and observe() on that instance. Sessions and their IDs are gone from the calling surface, and the package names changed, so this is a rewrite against the new shape rather than a rename pass. The code mode examples show the target in each language.

Breaking changes

These diffs are TypeScript, because v3’s TypeScript SDK is the one whose surface maps onto v4 rename by rename. The v4 side of each diff is the shape for every language.

Initialization

The constructor is private and init() is gone. Get a browser from a factory, then hand it to Stagehand.create():
Use localBrowser.launch() for a browser on your machine, and localBrowser.connect({ cdpUrl }) or browserbase.connect({ apiKey, sessionId }) to attach to one that’s already running. Stagehand closes only the browsers it launched, so call browser.close() yourself. See browser configuration.

Pages and the browser context

The context moved off the Stagehand instance and onto the browser handle, and page lookups are now async:
If you only hold the instance, reach the handle through it: stagehand.browser.context.

act(), extract(), and observe() moved to the instance

In v3 these hung off the page. In v4 they’re top-level methods on Stagehand, and you pick the target page with options.page when it isn’t the active one:

Every primitive returns data and metadata

act(), extract(), and observe() return { data, metadata }. Your value is on data; metadata carries the action ID, cache status, and token usage.

extract() takes positional arguments

Calling extract() with no schema returns { extraction: string }.

Model configuration

modelName and modelClientOptions collapse into one model object:
Model names always carry a provider prefix. Omit model entirely on a Browserbase browser and the Model Gateway picks one for you. Pass the same shape to a single call to override it there. See models.

Caching

enableCaching is gone. v4 caches act(), observe(), and extract() results on Browserbase’s servers instead, keyed on the instruction, page content, and options:
Caching needs a Browserbase browser and the API key you passed to browserbase.launch(). See caching.

Logging

verbose and logger become one logging object with a level, a format, and a callback:
Levels are debug, info, warn, error, and off. See logging.

Metrics

Metrics became a method:

The Browserbase session ID

stagehand.browserbaseSessionID is gone. Create the session yourself when you need its ID, then attach:
Observability shows the full version, including reading session metrics back.

deepLocator() folded into locator()

page.deepLocator() is gone, but its behavior isn’t. page.locator() resolves the same selectors in v4, so drop the call and keep the selector:
>> hop notation and deep XPath such as /html/body/iframe[2]//div work unchanged. See Locator.

Quick reference

Troubleshooting

Constructor of class 'Stagehand' is private. Use await Stagehand.create({ browser }). Property 'context' does not exist on type 'Stagehand'. The context lives on the browser handle: browser.context, or stagehand.browser.context. Property 'act' does not exist on type 'Page'. act(), extract(), and observe() are methods on the Stagehand instance now. Target a specific tab with options.page. Property 'length' does not exist on an observe() result. Read .data first. Your generated script uses v3 APIs. The assistant is drawing on v2 and v3 patterns in its training data. Install the rule files from AI rules. Property 'deepLocator' does not exist on type 'Page'. Rename the call to page.locator(). The selector stays the same. A retried step repeats a side effect. You’re retrying act(). Retry observe() instead and pass the resulting action to act() once.

Next steps

AI rules

Set your coding assistant up to write v4 code

Act

Perform one action, or replay an observed one

Observe

Plan actions without performing them

Caching

Cut inference out of a stable flow