Automate a multi-step web task (sign in with a test account, search, extract 50 records to typed JSON) with at least 95% success over 50 runs, under 60 s per run, and lower token use on repeat runs than on the first.
Recommended stack
- Stagehand 4 (TypeScript) - act, extract, observe and metrics() on a Playwright-style page API (agent() was removed in v4). Mixes deterministic page calls with natural-language steps in one script and reports token use per method.
- Local Chromium or Browserbase - Browser runtime. Local for development; hosted browsers when you need parallel runs and server-side caching.
- zod 4 (same version Stagehand depends on, 4.4.x; import from "zod/v4") - Schemas for extract. Typed, validated records instead of free text.
- Vitest 5 - Repeat-run harness and assertions. Runs the flow N times and fails on schema or count errors.
Build steps
- Check that the site's terms allow automation and use an official API if one exists; use a dedicated test account and keep request rates polite.
- Walk the flow by hand and mark each step stable (fixed URL or selector: use page.goto and locators) or brittle (layout changes: use act or observe).
- Start with
const browser = await localBrowser.launch({ userDataDir: "./browser-data" })andconst stagehand = await Stagehand.create({ browser, model: { modelName: "anthropic/<model>", apiKey: process.env.ANTHROPIC_API_KEY } }); get the page withconst [page] = await browser.context.pages()and fill credentials with page.locator(...).fill(process.env...) so they never enter a prompt. - Call observe once for brittle steps, store
result.data(the action array) in a JSON file, and replay them with stagehand.act(storedAction) on later runs; fall back to act(instruction) only when a stored action fails. On Browserbase, server-side caching is on by default. - Extract with a zod schema; validate record count and required fields, and retry the page once on failure.
- For open-ended steps, let a model call act/observe/extract as tools in your own loop with at most 20 steps and a domain allowlist; Stagehand 4 has no agent().
- On failure save
page.screenshot()and the HTML frompage.evaluate(() => document.documentElement.outerHTML)(v4 Page has no content()); log metrics() per run. - Run 50 times, record success rate, duration, and tokens, and convert AI steps that always resolve the same way into plain code.
Watch out for
- Using AI for every step makes runs slow and costly. Cache observed actions and keep stable steps deterministic.
- Page content can carry prompt injection. Treat extracted text as data and never let page text pick the next URL outside the allowlist.
- CAPTCHAs and bot checks: do not try to bypass them. Stop the run and alert a human.
- Unbounded model-driven loops wander. Cap steps and time per run.
Definition of done
- The 50-run report shows at least 95% success and under 60 s median.
- Output JSON validates against the schema with 50 records.
- A second run uses fewer tokens than the first (from metrics()).
- A forced failure leaves a screenshot and HTML snapshot artifact.