Stagehand Browser Collection: Scripts, Model Configuration and Result Checks

Use Stagehand 4.3.0 interfaces to configure a local browser and optional AI steps, with a verified deterministic login, filtering and pagination example.

16Yun Engineering TeamOct 28, 20267 min read

A browser collection workflow can use code for known steps and a model where semantic interpretation helps. Its completion condition should remain an explicit set of business records.

This guide targets Stagehand 4.3.0. On October 29, 2026, the deterministic local workflow completed login, filtering and two-page collection on both fixture variants, returning the three expected products.

The model configuration and observe/act/extract branch were checked against source and TypeScript interfaces; no model inference was executed. The guide makes no measured claim about AI accuracy, self-healing or token cost.

Specify the task before adding semantic actions

The fixture has five products. The task selects active books and reads both result pages.

IDNameDisplayed price
p1Atlas19.50
p2Beacon24.00
p3Compass31.25

Product p4 is inactive and p5 is a tool, so neither belongs in the output. Preserve prices as strings when exact display text is part of the contract.

The changed variant renames the filter and next-page buttons and increases rendering delay from 100ms to 700ms. Element IDs stay stable. This is a defined test of selectors and readiness checks, not an arbitrary redesign or an AI recovery test.

Obtain the exact executable example

Use Git and a GitHub CLI configured to access public repositories. The sparse checkout limits working files to the selected directories and repository root files.

gh repo clone 16yun-cn/www-16yun-cn-v3 ai-crawling-examples -- --filter=blob:none --sparse
cd ai-crawling-examples
git checkout --detach bc6828e62c417b1a3459199811fd4e2164137a5f
git sparse-checkout set scripts/blog-ai-crawling

This immutable revision contains the executed scripts and dependency locks. Run the following installation commands from its repository root; the website itself does not need to run.

Excerpts below identify their original file and line range. They omit surrounding imports, initialized variables or lifecycle code, so execute the complete checked-out script rather than concatenating fragments.

Keep SDK, schema and browser versions explicit

The example repository is 16yun-cn/www-16yun-cn-v3. Its source checkout contains the example and an independent Node lock file under scripts/blog-ai-crawling/.

ComponentRecorded version
Node.js24.22.2
@browserbasehq/stagehand4.3.0
zod4.4.3
TypeScript5.9.3
Local browser used in the runChrome 153.0.8010.12

An initial Zod mismatch produced type errors. Aligning it with the published SDK's version resolved that check. Mixing initialization examples and schema types from different SDK generations can break an otherwise plausible tutorial.

Start the fixture from the repository root in terminal one.

python3 scripts/blog-ai-crawling/serve.py --port 8765

In terminal two, copy the Node example into a temporary directory, then install and check it.

STAGEHAND_ENV="$(mktemp -d)"
cp scripts/blog-ai-crawling/package.json \
  scripts/blog-ai-crawling/package-lock.json \
  scripts/blog-ai-crawling/stagehand_catalog.mts "$STAGEHAND_ENV/"
cd "$STAGEHAND_ENV"
npm ci
npm run check
npm run stagehand

The local browser may need downloading and host dependencies. Set CHROME_PATH to an existing compatible executable if appropriate. FIXTURE_URL can change the localhost port; the teaching script rejects remote hostnames.

Initialize through Stagehand.create

The optional model path uses the following initialization shape inside the complete script's lifecycle handling.

Example source: scripts/blog-ai-crawling/stagehand_catalog.mts:25-45. This is an equivalent or shortened presentation.

import { localBrowser, Stagehand, type ModelName } from "@browserbasehq/stagehand";
 
const browser = await localBrowser.launch({ headless: true });
const stagehand = await Stagehand.create({
  browser,
  model: {
    modelName: process.env.STAGEHAND_MODEL as ModelName,
    apiKey: process.env.MODEL_API_KEY,
  },
});

Do not transplant an older new Stagehand({ env: "LOCAL" }) example into this pinned version. The default runnable branch omits model configuration and never invokes model methods.

Source review uses browserbase/stagehand revision d59afde. The strict model configuration in packages/protocol/schemas.ts:677-694 defines the connection fields. Adding an arbitrary baseURL there is not supported by that schema.

Establish authentication through deterministic code

The fixture-only credentials are reader and fixture-only. They authenticate only to the local teaching server.

Example source: scripts/blog-ai-crawling/stagehand_catalog.mts:46-52. The excerpt is reformatted for readability.

const [page] = await browser.context.pages();
await page.goto(`${base}/login`);
await page.locator('[name="user"]').fill("reader");
await page.locator('[name="password"]').fill("fixture-only");
await page.locator('button[type="submit"]').click();
await page.waitForSelector('a[href="/catalog"]');

The same browser context carries the fixture session cookie to the catalog. An unauthenticated catalog request returns 401.

This server is not a production authentication design. Never turn its fixed sample credentials into hardcoded production secrets.

Configure a model only when choosing the AI branch

Readers who want to execute the semantic path must supply their own provider configuration.

export STAGEHAND_MODEL='provider/model-id'
export MODEL_API_KEY='replace-with-your-own-provider-key'
npm run stagehand -- --ai

The model identifier above is a placeholder. Replace it with a name supported by the pinned SDK and your provider account. The reviewed version recognizes openai, anthropic, google, groq and cerebras prefixes; provider availability still needs checking for the chosen model.

Keep the key in your execution environment. Preparing this guide did not require a key or execute that AI command.

Observe a candidate, then perform the explicit operation

The optional path asks for the category dropdown and selects a known value through code.

Example source: scripts/blog-ai-crawling/stagehand_catalog.mts:60-67. The excerpt is reformatted for readability.

const observed = await stagehand.observe("Find the category dropdown", { page });
assert.ok(observed.data.length > 0);
await page.locator(observed.data[0].selector).selectOption("book");
await page.locator("#status").selectOption("active");

Candidates are in .data, rather than a top-level array. The reviewed interface is packages/sdk-ts/src/stagehand.ts:263-283.

A nonempty response is not proof that its first candidate is correct. That choice has not been runtime-tested here. Re-observe where necessary after a document change rather than assuming an earlier locator remains meaningful.

Wait for the new result generation after acting

An AI action can apply the filters, while the caller still checks the expected data transition.

Example source: scripts/blog-ai-crawling/stagehand_catalog.mts:69-73. The excerpt is reformatted for readability.

await stagehand.act("Apply the selected category and status filters", { page });
await page.waitForSelector(
  '#results[data-ready="yes"][data-generation="2"]'
);

The fixture provides generation markers to distinguish initial results from filtered results. Merely waiting for the container to exist can read the previous contents.

The executed deterministic branch clicks #apply. Its success does not demonstrate that act() understood the instruction.

Validate structure, then verify business content

The extraction example supplies a schema and restricts the target region.

Example source: scripts/blog-ai-crawling/stagehand_catalog.mts:17-19. This is an equivalent or shortened presentation. The extraction call is at scripts/blog-ai-crawling/stagehand_catalog.mts:78-80.

const schema = z.object({
  products: z.array(z.object({
    id: z.string(), name: z.string(), price: z.string(),
  })),
});
const result = await stagehand.extract(
  "Extract the ID, name and exact displayed price of every product row. Preserve price as a string.",
  schema,
  { page, locator: page.locator("#results") },
);
products.push(...result.data.products);

At packages/sdk-ts/src/stagehand.ts:285-324, the client parses response data using the supplied schema. That can expose structural violations. A string with the wrong product's price can still satisfy the type.

The complete script also checks unique IDs and compares every expected record. The business acceptance condition is independent of the extraction mechanism.

Keep SDK results distinct from application records

The semantic methods and the caller's product schema have different responsibilities.

InterfaceCaller responsibility
observe() result dataConfirm that a candidate identifies the intended control
act() responseWait for and check the required page-state transition
extract() result dataVerify record identities, fields and completeness after schema parsing

The complete file adds imports, expected records and cleanup around the excerpts. Configuration fields do not imply a universal provider endpoint interface, and a type assertion on a model name cannot establish provider availability.

Make pagination completion observable

The teaching task has exactly two pages. Before reading, the script checks ready and page markers. After reading, it checks that Next is enabled on the first page and disabled on the second.

The fixed iteration count is deliberate for this fixture. Adapt the loop and termination rule to the target's actual pagination contract before collecting an unknown number of pages.

Read the evidence at its actual strength

CheckOutcomeWhat it establishes
TypeScriptPassedThe example matches the pinned type interfaces
Original deterministic workflowp1, p2, p3 with correct fieldsLocal login, filtering and two-page reading work
Renamed-button/delayed deterministic workflowSame correct recordsStable IDs and readiness checks handle these changes
AI observe/act/extractNot executedConfiguration, source and type evidence only
Model accuracy, self-healing and token costsUnmeasuredNo AI outcome or cost claim

Reports retain the execution mode, variant and records. A single local deterministic duration is not a model-performance benchmark.

If you execute the optional path, record the model, date, task outcome and available usage data. Use the provider's accounting to calculate cost; deterministic browser time cannot establish inference cost.

Choose the smallest useful semantic boundary

Implement known navigation and explicit conditions in code first. Add model-backed selection or actions where semantic uncertainty justifies them, then retain the same result checks.

Commercial proxies, cross-site session persistence, CAPTCHAs and public-site generalization were not tested. Neither a proxy nor a model removes the need for correct readiness and completion rules.

For the broader decision, see the AI crawling tool selection guide.

Need an enterprise proxy plan?

We can tailor architecture to your target domains, concurrency, and reliability goals.