Skip to main content

Stagehand: AI-Powered Browser Automation That Combines Code and Natural Language (2026)

· 6 min read
Yassine El Haddad
Software Developer & Automation Specialist

I build production AI agents, web scrapers, and automation pipelines. Most of what I publish here comes from the actual problems they run into: proxies that get banned, anti-bot stacks that fingerprint your client, RAG that drifts when the underlying data moves. Stack: Python, TypeScript, Go, FastAPI, LangChain, Crawlee, Playwright, deployed on AWS, GCP, and Cloudflare.

Stagehand is BrowserBase's open-source framework that extends Playwright with LLM-powered natural language commands. You can page.act("click the login button") or page.extract({ instruction: "...", schema: {...} }) instead of writing brittle selectors. Stagehand shines on complex, dynamic UIs; plain Playwright remains better for high-volume scraping where LLM latency and cost matter. Use Apify for scalable browser automation.

What Stagehand Is

Stagehand wraps Playwright with methods that use an LLM to interpret your intent:

  • page.act(instruction) — Performs an action (click, type, scroll) based on natural language
  • page.extract({ instruction, schema }) — Extracts structured data using an LLM to find and parse content
  • page.observe() — Captures page structure for the LLM to reason about

Under the hood, Playwright handles browser control; the LLM interprets ambiguous or dynamic UI when selectors would break. You need an Anthropic or OpenAI API key. Stagehand is free and open-source; you pay for LLM calls.

Key Methods

MethodPurposeWhen to Use
act(instruction)Perform an action from natural languageButtons, links, forms with changing IDs
extract({ instruction, schema })Extract structured dataTables, lists, cards with variable layout
observe()Get page context for LLMDebugging, multi-step reasoning
extractOne(selector, schema)Extract from a specific elementWhen you have a stable selector
extractMany(selector, schema)Extract list from matching elementsProduct grids, search results

For stable sites with consistent selectors, use Playwright's page.locator() and evaluate(). For sites that change often or have opaque class names, Stagehand's natural language layer reduces maintenance.

How It Works

  1. You call page.act("click the accept cookies button") or page.extract({ instruction: "Get product name and price", schema: { name: "string", price: "string" } }).
  2. Stagehand captures a snapshot of the page (DOM, screenshot, or both) and sends it to the LLM.
  3. LLM determines which element to click or how to map content to your schema.
  4. Stagehand translates that into Playwright calls (page.click(selector), page.fill(), etc.) and executes them.

The LLM adds latency (often 1–5 seconds per call) and cost (tokens per request). For high-throughput scraping, that adds up quickly.

When Stagehand Beats Plain Playwright

  • Complex dynamic UIs — React/Vue apps with generated class names, A/B-tested layouts
  • Sites that change frequently — Selectors break; natural language is more resilient
  • Rapid prototyping — Get something working before investing in robust selectors
  • Ad-hoc extraction — One-off scripts where LLM cost is acceptable
  • Accessibility-driven actions — "Click the submit button" works even if the DOM changes

When Plain Playwright Is Better

  • High-volume scraping — Thousands of pages; LLM cost and latency dominate
  • Stable selectors — Site structure is predictable; locators are reliable
  • CI/test automation — Determinism matters; no LLM variance
  • Budget-sensitive — Minimize API calls; use HTTP + proxy for simple HTML

Use Stagehand for the 10% of targets that resist traditional automation. Use Playwright (and Crawlee) for the rest. See Playwright vs Puppeteer vs Selenium for stack choices.

Code Example: Scraping a Complex SPA

const { Stagehand } = require('@browserbasehq/stagehand');

async function scrapeProductPage(url) {
const stagehand = new Stagehand({
env: "BROWSERBASE",
apiKey: process.env.BROWSERBASE_API_KEY,
enableCaching: true,
});

await stagehand.init();
const page = stagehand.page;

await page.goto(url);

// Natural language action — works even if button ID changes
await page.act("Close the cookie banner if it appears");

// Extract structured data with LLM
const products = await page.extract({
instruction: "Extract all product cards: name, price, and image URL",
schema: {
type: "object",
properties: {
items: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
price: { type: "string" },
imageUrl: { type: "string" },
},
},
},
},
required: ["items"],
},
});

await stagehand.close();
return products;
}

Stagehand can use BrowserBase's cloud browsers or local Playwright. Set env: "LOCAL" for local execution; env: "BROWSERBASE" for cloud. BrowserBase provides persistent sessions and screenshot-based debugging, useful when debugging LLM-driven flows across page transitions. For cost-sensitive development, start with LOCAL and switch to BrowserBase only when you need cloud scale or session persistence.

Setup

npm install @browserbasehq/stagehand playwright

Configure one of:

  • ANTHROPIC_API_KEY — For Claude (default)
  • OPENAI_API_KEY — For GPT-4

Optional: BROWSERBASE_API_KEY for BrowserBase cloud. Without it, Stagehand uses local browsers.

Comparison: Stagehand vs Alternatives

AttributeStagehandPlaywright + AI ExtensionManual Playwright + LLM Fallback
Natural language✓ Built-inVariesManual prompt engineering
Extractionextract() with schemaDependsCustom LLM + parser
Actionsact()VariesManual selector or LLM
CostPer act/extractPer callPer call
DeterminismLower (LLM variance)LowerHigher with selectors
Best forComplex SPAs, rapid protosSimilarFull control

Stagehand gives you a ready-made integration. Manual Playwright + LLM gives more control but more code. Choose based on how much you value speed of development vs. cost and determinism.

Integration with Apify

You can use Stagehand inside an Apify Actor:

  1. Add @browserbasehq/stagehand to package.json
  2. Set ANTHROPIC_API_KEY or OPENAI_API_KEY as a secret in Apify
  3. Use Stagehand in your Actor's main function instead of (or alongside) raw Playwright
  4. Store results in the default dataset

Stagehand fits when an Actor targets a few complex pages (e.g., login flows, dynamic dashboards). For bulk crawling, prefer plain Playwright with Crawlee. A common pattern: use Stagehand for the login or cookie-consent step, then switch to standard Playwright selectors for the main extraction loop. See building an Apify Actor with TypeScript for the Actor structure. For anti-bot sites, combine with Bright Data Scraping Browser.

Apify Affiliate Banner 728x90Apify Affiliate Banner 728x90Apify Affiliate Banner 300x50Apify Affiliate Banner 300x50
Hybrid approach

Use Stagehand for the hard parts (login, dynamic extraction), Playwright for the rest. Deploy to Apify for scheduling and scaling.



Try Apify | Build an Actor

Frequently Asked Questions

Stagehand is BrowserBase's open-source framework that adds LLM-powered natural language to Playwright. You use act() and extract() instead of writing selectors.

Use Stagehand for complex dynamic UIs, sites that change frequently, or rapid prototyping. Use plain Playwright for high-volume scraping and stable sites.

Stagehand itself is free. You pay for LLM API calls (Anthropic or OpenAI). Each act() and extract() costs tokens. For thousands of pages, costs add up.

Yes. Add Stagehand to your Apify Actor's dependencies. Use it for complex pages; use Crawlee/Playwright for bulk crawling. Set LLM API keys as Apify secrets.

Yes. Set env to LOCAL and Stagehand uses local Playwright. You still need ANTHROPIC_API_KEY or OPENAI_API_KEY for act() and extract().

Less so than plain Playwright. LLM output can vary. For critical paths, combine Stagehand with fallback selectors or validation.

Common mistakes and fixes

Stagehand act() fails or times out

Ensure page is loaded. Try more specific instructions. Fall back to page.locator() for critical selectors. Check LLM API key and rate limits.

extract() returns wrong schema

Make instruction more specific. Use stricter JSON schema. Consider multiple smaller extracts instead of one large schema.