UNPKG

@browserbasehq/stagehand

Version:

Browserbase's SDK for building browser agents

522 lines (419 loc) • 17.5 kB
<div id="toc" align="center" style="margin-bottom: 0;"> <ul style="list-style: none; margin: 0; padding: 0;"> <a href="https://stagehand.dev"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/browserbase/stagehand/main/media/dark_logo.png" /> <img alt="Stagehand" src="https://raw.githubusercontent.com/browserbase/stagehand/main/media/light_logo.png" width="200" style="margin-right: 30px;" /> </picture> </a> </ul> </div> <p align="center"> <strong>Stagehand is the SDK for browser agents</strong><br> </p> <p align="center"> <a href="https://trendshift.io/repositories/12122" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12122" alt="browserbase%2Fstagehand | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> </p> <p align="center"> <a href="https://docs.stagehand.dev"><strong>Docs</strong></a> · <a href="https://docs.stagehand.dev/v4/first-steps/quickstart"><strong>Quickstart</strong></a> · <a href="https://github.com/browserbase/stagehand/stargazers"><strong>⭐ Star this repo</strong></a> </p> <p align="center"> <a href="https://github.com/browserbase/stagehand/tree/main?tab=MIT-1-ov-file#MIT-1-ov-file"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/browserbase/stagehand/main/media/dark_license.svg" /> <img alt="MIT License" src="https://raw.githubusercontent.com/browserbase/stagehand/main/media/light_license.svg" /> </picture> </a> <a href="https://discord.gg/stagehand"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/browserbase/stagehand/main/media/dark_discord.svg" /> <img alt="Discord Community" src="https://raw.githubusercontent.com/browserbase/stagehand/main/media/light_discord.svg" /> </picture> </a> <a href="https://deepwiki.com/browserbase/stagehand"> <img alt="Ask DeepWiki" src="./media/deepwiki.svg" width="156" height="32" /> </a> </p> <p align="center"> <a href="https://stagehand.dev"> <img src="https://raw.githubusercontent.com/browserbase/stagehand/main/media/stagehand-website-banner.png" alt="Stagehand homepage showing browser automation in TypeScript, Python, and Go and a speed comparison with Playwright" width="100%" /> </a> </p> ## AI that uses the browser like humans. Sign in once, keep the session, and pull structured data out the other side. ```typescript import { localBrowser, Stagehand } from "@browserbasehq/stagehand"; import { z } from "zod/v4"; // Cookies persist in ./browser-data, so the next run starts already signed in const browser = await localBrowser.launch({ userDataDir: "./browser-data" }); const stagehand = await Stagehand.create({ browser, model: { modelName: "openai/gpt-5.4-mini", apiKey: process.env.OPENAI_API_KEY }, }); const [page] = await browser.context.pages(); await page.goto("https://app.example.com/login"); // observe() returns real selectors, so credentials never reach the model const { data: email } = await stagehand.observe("find the email input"); const { data: password } = await stagehand.observe("find the password input"); await page.locator(email[0].selector).fill(process.env.APP_EMAIL!); await page.locator(password[0].selector).fill(process.env.APP_PASSWORD!); // act() self-heals when the site redesigns its form await stagehand.act("click the sign in button"); await stagehand.act("open the billing page"); // extract() returns schema-validated data const { data } = await stagehand.extract( "extract every invoice in the table", z.object({ invoices: z.array(z.object({ number: z.string(), amount: z.number(), paid: z.boolean() })), }), ); console.log(data.invoices); await stagehand.close(); await browser.close(); ``` <details> <summary><b>Python</b></summary> ```python import asyncio import os from pydantic import BaseModel from stagehand import Stagehand, local_browser class Invoice(BaseModel): number: str amount: float paid: bool class Invoices(BaseModel): invoices: list[Invoice] async def main() -> None: # Cookies persist in ./browser-data, so the next run starts already signed in browser = await local_browser.launch(user_data_dir="./browser-data") try: stagehand = await Stagehand.create( browser=browser, model="openai/gpt-5.4-mini", model_api_key=os.environ["OPENAI_API_KEY"], ) try: page = (await browser.context.pages())[0] await page.goto("https://app.example.com/login") # observe() returns real selectors, so credentials never reach the model email = await stagehand.observe("find the email input") password = await stagehand.observe("find the password input") await page.locator(email.data[0].selector).fill(os.environ["APP_EMAIL"]) await page.locator(password.data[0].selector).fill(os.environ["APP_PASSWORD"]) # act() self-heals when the site redesigns its form await stagehand.act("click the sign in button") await stagehand.act("open the billing page") # extract() returns schema-validated data result = await stagehand.extract( "extract every invoice in the table", Invoices, ) print(result.data.invoices) finally: await stagehand.close() finally: await browser.close() asyncio.run(main()) ``` </details> <details> <summary><b>Go</b></summary> ```go package main import ( "context" "errors" "fmt" "log" "os" stagehand "github.com/browserbase/stagehand/packages/sdk-go/v4" ) type invoice struct { Number string `json:"number"` Amount float64 `json:"amount"` Paid bool `json:"paid"` } type invoices struct { Invoices []invoice `json:"invoices"` } func main() { if err := run(context.Background()); err != nil { log.Fatal(err) } } func run(ctx context.Context) (err error) { // Cookies persist in ./browser-data, so the next run starts already signed in browser, err := stagehand.LaunchLocalBrowser(ctx, &stagehand.LocalBrowserLaunchOptions{ UserDataDir: "./browser-data", }) if err != nil { return err } defer func() { err = errors.Join(err, browser.Close(ctx)) }() modelAPIKey := os.Getenv("OPENAI_API_KEY") client, err := stagehand.Create(ctx, stagehand.CreateOptions{ Browser: browser, Model: &stagehand.ModelConfig{ ModelName: "openai/gpt-5.4-mini", APIKey: &modelAPIKey, }, }) if err != nil { return err } defer func() { err = errors.Join(err, client.Close(ctx)) }() browserContext, err := browser.Context() if err != nil { return err } pages, err := browserContext.Pages(ctx) if err != nil { return err } page := pages[0] if _, err := page.Goto(ctx, "https://app.example.com/login", nil); err != nil { return err } // Observe returns real selectors, so credentials never reach the model emailInstruction := "find the email input" email, err := client.Observe(ctx, &emailInstruction, nil) if err != nil { return err } if err := page.Locator(email.Data[0].Selector).Fill(ctx, os.Getenv("APP_EMAIL")); err != nil { return err } passwordInstruction := "find the password input" password, err := client.Observe(ctx, &passwordInstruction, nil) if err != nil { return err } if err := page.Locator(password.Data[0].Selector).Fill(ctx, os.Getenv("APP_PASSWORD")); err != nil { return err } // Act self-heals when the site redesigns its form if _, err := client.Act(ctx, stagehand.ActInstruction("click the sign in button"), nil); err != nil { return err } if _, err := client.Act(ctx, stagehand.ActInstruction("open the billing page"), nil); err != nil { return err } // Extract returns data decoded into a Go type extracted, err := stagehand.Extract[invoices]( ctx, client, "extract every invoice in the table", nil, ) if err != nil { return err } fmt.Println(extracted.Data.Invoices) return nil } ``` </details> ## Install ```bash pnpm add @browserbasehq/stagehand 'zod@~4.4.3' ``` <details> <summary><b>Python</b></summary> ```bash pip install stagehand ``` </details> <details> <summary><b>Go</b></summary> ```bash go get github.com/browserbase/stagehand/packages/sdk-go/v4@v4.0.0 ``` </details> Local runs need [Chrome](https://www.google.com/chrome/) installed. Full setup: [Quickstart](https://docs.stagehand.dev/v4/first-steps/quickstart). ## Why Stagehand <table> <tr> <td><strong>Familiar APIs</strong></td> <td>The Playwright-style methods you and your agents already know: <code>goto</code>, <code>click</code>, <code>locator</code>, <code>screenshot</code>.</td> </tr> <tr> <td><strong>Token efficiency</strong></td> <td>Hybrid accessibility-tree trimming gives agents exactly the page context they need and nothing more.</td> </tr> <tr> <td><strong>Faster in production</strong></td> <td>Stagehand runs as an extension next to the browser, cutting round-trip latency on every action.</td> </tr> <tr> <td><strong>Self-healing</strong></td> <td><code>act</code>, <code>observe</code>, and <code>extract</code> refresh how an action happens when the site changes underneath it.</td> </tr> <tr> <td><strong>Built for agents</strong></td> <td>WebMCP, clipboard support, batch commands, deep locators for nested iframes and closed Shadow DOMs, OTel traces.</td> </tr> <tr> <td><strong>Three languages</strong></td> <td>One complete browser driver across TypeScript, Python, and Go.</td> </tr> </table> ## Run it on Browserbase Point the same script at [Browserbase](https://www.browserbase.com) and get 2x faster execution than Playwright cloud equivalent browsers. Configure the [Model Gateway](https://docs.stagehand.dev/v4/configuration/models#model-gateway) so you never wire up a provider, and enable [server-side caching](https://docs.stagehand.dev/v4/best-practices/caching) to cache repeated actions. ```typescript import { browserbase, Stagehand } from "@browserbasehq/stagehand"; const browser = await browserbase.launch({ apiKey: process.env.BROWSERBASE_API_KEY! }); // No model configuration: the Model Gateway picks the cheapest model for each action // cache: true: identical calls come back from Browserbase, no tokens spent const stagehand = await Stagehand.create({ browser, cache: true }); ``` <details> <summary><b>Python</b></summary> ```python import os from stagehand import Stagehand, browserbase browser = await browserbase.launch(api_key=os.environ["BROWSERBASE_API_KEY"]) # No model configuration: the Model Gateway picks the cheapest model for each action # cache=True: identical calls come back from Browserbase, no tokens spent stagehand = await Stagehand.create(browser=browser, cache=True) ``` </details> <details> <summary><b>Go</b></summary> ```go browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{ APIKey: os.Getenv("BROWSERBASE_API_KEY"), }) if err != nil { return err } // No model configuration: the Model Gateway picks the cheapest model for each action // CacheEnabled(true): identical calls come back from Browserbase, no tokens spent cache := stagehand.CacheEnabled(true) client, err := stagehand.Create(ctx, stagehand.CreateOptions{ Browser: browser, Cache: &cache, }) if err != nil { return err } ``` </details> [Verified mode](https://docs.browserbase.com/platform/identity/verified-customization), [residential proxies](https://docs.browserbase.com/platform/identity/proxies), [persistent contexts](https://docs.browserbase.com/platform/browser/core-features/contexts), and [session recordings](https://docs.browserbase.com/platform/browser/observability/session-replay) come with it. [Get an API key](https://www.browserbase.com/overview) and learn how to configure your browser [here](https://docs.stagehand.dev/v4/configuration/browser). ## Give your coding agent a browser The hosted Browserbase [MCP server](https://docs.stagehand.dev/integrations/mcp/setup) puts `navigate`, `act`, `observe`, and `extract` in any MCP client — no install, no local browser. ```bash claude mcp add --transport http browserbase https://mcp.browserbase.com/mcp \ --header "Authorization: Bearer $BROWSERBASE_API_KEY" ``` <details> <summary><b>Cursor, Codex, and other MCP clients</b></summary> ```json { "mcpServers": { "browserbase": { "url": "https://mcp.browserbase.com/mcp", "headers": { "Authorization": "Bearer YOUR_BROWSERBASE_API_KEY" } } } } ``` </details> ## Search and fetch without a browser [Fetch](https://docs.stagehand.dev/v4/add-ons/fetch) lets you grab the content of any URL as markdown. [Search](https://docs.stagehand.dev/v4/add-ons/search) provides fast, token-efficient web search results. Both as a lightweight complement to browser sessions. ```typescript import { browserbase } from "@browserbasehq/stagehand"; const { results } = await browserbase.search({ apiKey: process.env.BROWSERBASE_API_KEY!, query: "browser agent frameworks", numResults: 5, }); const fetched = await browserbase.fetch({ apiKey: process.env.BROWSERBASE_API_KEY!, url: results[0].url, format: "markdown", }); console.log(fetched.content); ``` ## Docs and resources <table> <tr> <td><a href="https://docs.stagehand.dev/v4/first-steps/quickstart">Quickstart</a></td> <td>Empty directory to working automation in three steps</td> </tr> <tr> <td><a href="https://docs.stagehand.dev/v4/reference/page">page</a> · <a href="https://docs.stagehand.dev/v4/reference/locator">locator</a></td> <td>Playwright-style browser and element APIs</td> </tr> <tr> <td><a href="https://docs.stagehand.dev/v4/basics/webmcp">WebMCP</a></td> <td>Discover and invoke WebMCP tools exposed by web pages</td> </tr> <tr> <td><a href="https://docs.stagehand.dev/v4/basics/act">act</a> · <a href="https://docs.stagehand.dev/v4/basics/extract">extract</a> · <a href="https://docs.stagehand.dev/v4/basics/observe">observe</a></td> <td>Browser actions and data extraction with natural language</td> </tr> <tr> <td><a href="https://docs.stagehand.dev/v4/add-ons/search">Search</a> · <a href="https://docs.stagehand.dev/v4/add-ons/fetch">Fetch</a></td> <td>Web search and page content without a browser</td> </tr> <tr> <td><a href="https://docs.stagehand.dev/v4/migrations/playwright">Migrate from Playwright</a></td> <td>Port an existing suite</td> </tr> <tr> <td><a href="https://docs.stagehand.dev/v4/integrations/overview">Integrations</a></td> <td>CrewAI, Mastra, Deep Agents, Vercel AI SDK, Claude Code, Codex</td> </tr> <tr> <td><a href="./packages/sdk-python/README.md">Python SDK</a> · <a href="./packages/sdk-go/README.md">Go SDK</a></td> <td>Language-specific guides</td> </tr> <tr> <td><a href="https://deepwiki.com/browserbase/stagehand">Ask DeepWiki</a></td> <td>Ask questions about this codebase</td> </tr> </table> ## Join the community Stagehand is built in the open, and the fastest way to shape it is to show up. - **[⭐ Star this repo](https://github.com/browserbase/stagehand/stargazers)** — it is how most people find Stagehand - **[💬 Join the Discord](https://discord.gg/stagehand)** — questions, support, and what we are building next - **[🐛 Open an issue](https://github.com/browserbase/stagehand/issues)** — bug reports are the most useful contribution - **[𝕏 Follow @stagehanddev](https://x.com/stagehanddev)** — releases and demos ### Contributing We're focused on improving reliability, extensibility, speed, and cost, in that order. **Bug fixes and small improvements are the best way to get started.** For anything larger, reach out to [Miguel Gonzalez](https://x.com/miguel_gonzf) or [Paul Klein](https://x.com/pk_iv) on [Discord](https://discord.gg/stagehand) first so we can make sure it lands. Stagehand is a TypeScript, Python, and Go monorepo driven by [`just`](https://github.com/casey/just): ```bash git clone https://github.com/browserbase/stagehand.git cd stagehand just install just generate just build export OPENAI_API_KEY="your-openai-api-key" just example act # runs packages/sdk-ts/examples/act.ts ``` See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full TypeScript, Python, and Go setup. ## Acknowledgements We'd like to thank the following people for their major contributions to Stagehand: - [Paul Klein](https://github.com/pkiv) - [Sean McGuire](https://github.com/seanmcguire12) - [Miguel Gonzalez](https://github.com/miguelg719) - [Sameel Arif](https://github.com/sameelarif) - [Thomas Katwan](https://github.com/tkattkat) - [Filip Michalsky](https://github.com/filip-michalsky) - [Anirudh Kamath](https://github.com/kamath) - [Jeremy Press](https://x.com/jeremypress) - [Navid Pour](https://github.com/navidpour) - [Nick Sweeting](https://github.com/pirate) - [Sam Finton](https://github.com/monadoid) - [Shrey Pandya](https://github.com/shrey150) - [Shriya Lolabattu](https://github.com/shriyatheunicorn) - [Alyssa Maruyama](https://github.com/akeimach) - [Amel Bajramovic](https://github.com/bosniankicks) ## License Licensed under the MIT License. Copyright 2026 Browserbase, Inc. "Stagehand" is a trademark of Browserbase, Inc.