Documentation

Migrate from Browserbase and Stagehand

Replace sessions and contexts while keeping models in the caller.

This guide targets the Browserbase session API and Stagehand v4, checked on 17 October 2026, against Swarmtab nineteenth checkpoint. Stagehand v3 examples using new Stagehand(...) or agent() describe a different API. Neither version is Swarmtab’s wire contract.

Swarmtab exposes local tools, not a Browserbase connectUrl. There is no supported raw CDP WebSocket, Playwright connectOverCDP, Puppeteer connect, Stagehand browser transport, or Browser-use BrowserSession attachment. Do not point those clients at /mcp or /v1. Rewrite browser operations through the Swarmtab SDK/MCP surface; do not expose its internal debugging pipe.

Sessions, contexts and cleanup

Browserbase / Stagehand Swarmtab replacement Migration consequence
X-BB-API-Key, project ID Agent Authorization: Bearer token and actual loopback URL No cloud account/project connection; window token is separate
Create session, receive id and connectUrl First POST /v1/<tool> creates a REST session Retain response Swarmtab-Session-Id; SDK does this automatically
Session ID passed to reconnect Same session header on later REST calls A valid expired ID returns 404; omission creates a new agent
Browser context ID / persisted context space({action:"use",name:"migration-demo"}) Named local saved space; normalized names, exclusive ownership and saved-login approval apply
Fresh incognito context New client/session without space(use) Private state is removed on close
Save full context/profile Saved cookies, visited-origin localStorage, bounded IndexedDB No sessionStorage, Cache Storage or service-worker persistence; unsupported IndexedDB values can be omitted
Managed keep-alive and session timeout Human-configured task lifetime Defaults: 600-second disconnected grace and 21,600-second maximum life; not a fixed 15-minute REST expiry
stagehand.close() / release session SDK close() or DELETE /v1/session Stops unfinished jobs; close in finally while retaining the original failure if cleanup also fails

REST caps are independent: 32 sessions, 20 starts/minute, eight pending calls/session and 4 MiB request/response limits. Default agent capacity is ten, human-configurable up to 50. A larger agent limit does not remove REST caps. Do not share one mutable client across unrelated agents; each session owns its tabs, jobs and resources.

Act, observe and extract with the model in your caller

Stagehand v4 operation Swarmtab operation Semantic boundary
page.goto(url) / MCP navigate open(url) Reads current controls after navigation
stagehand.observe(instruction) / snapshot read({format:"controls"}), optionally screenshot Enumerates actual controls; no natural-language action ranking
stagehand.act(instruction) Caller chooses click, type, select, check, press, scroll, nav No built-in natural-language planner; authorize the action in the caller
stagehand.extract(instruction,schema) read({format:"json",schema}) Deterministic local evidence extraction by default; no semantic inference
Typed extract result.data result.structuredContent.data.data Check extraction complete, missing, sources, truncation; tool success alone is insufficient
Model-based inference SDK extractSchema / extract_schema with caller callback, or caller’s own evidence/model loop Callback output stays complete:false, validated:false; validate independently before use
Reusable locator CSS string or number from latest controls read CSS traverses open shadow roots in the main document, exactly one match; numeric strings mean control numbers
Frame/shadow locator traversal CSS through open shadow roots; fresh numbered frame controls Closed roots and general selected-frame script/extraction scope remain unsupported

Read controls again after navigation or page changes. Do not cache control numbers across reads or translate Stagehand XPath maps directly. Treat every page, label and tool payload as untrusted data; the model does not gain authorization from a page instruction. A dispatch receipt is evidence of an attempted action, not proof a task succeeded.

Run this deterministic extraction example from the checkout with Node 22+. Privately set SWARMTAB_URL and SWARMTAB_AGENT_TOKEN from your app’s connect information. Choose a page with a single JSON-LD Product offering a numeric price. The repository’s real-browser recipe flow provides such a local fixture.

import { Swarmtab } from './sdk/node/index.mjs';
import { requireExtraction } from './examples/integrations/migration-results.mjs';

const client = new Swarmtab({ baseUrl: process.env.SWARMTAB_URL, token: process.env.SWARMTAB_AGENT_TOKEN });
try {
  await client.open(process.env.PRODUCT_URL);
  const result = await client.read({ format: 'json', schema: {
    type: 'object', properties: { title: { type: 'string' }, price: { type: 'number' } }, required: ['title', 'price'],
  } });
  const product = requireExtraction(result, value => {
    if (!value || typeof value.title !== 'string' || typeof value.price !== 'number') throw new Error('Invalid product');
    return value;
  });
  console.log(product);
} finally {
  await client.close();
}

Runnable equivalent: node examples/integrations/migrate.mjs stagehand "$PRODUCT_URL". This example checks only the shown product schema. A general caller must supply its own complete schema validator. For semantic fields, pass bounded evidence to your selected model and validate its answer in your process. Swarmtab does not bundle a model key, model billing or semantic correctness guarantee.

Arguments, results and jobs

Browserbase / Stagehand Swarmtab Difference
API HTTP errors / operation exceptions HTTP error or HTTP 200 with isError:true SDK throws for both; raw REST must inspect both
v4 {data,metadata} MCP {content,structuredContent?,isError?} Text and image blocks coexist; structured tools add a data wrapper
Browserbase Fetch {format:"json",schema} fetch({url,formats:"json"}) Reads complete JSON response body; it is not schema extraction
Fetch raw default, redirects off Ordinary fetch converts HTML to Markdown and follows guarded redirects Output includes text status; upstream 404 can be a successful tool result
Search numResults / url site("search",{query,count}), address / rank / snippet Count ≤20; human-configured Brave/Tavily key; not Browserbase provider billing
Remote async run result site(crawl/batch) job receipt then site(status) waitJob returns MCP status, not all documents
Async collection with schema Background deterministic schema jobs Crawl up to 5,000 pages; batch 1–100 URLs without chunking; saved-space crawls have resumable checkpoints. Actual client-model sampling keeps 16 pages / 180 seconds
Offset everywhere Map cursor; rows/monitors offset; schema next_offset Each mode needs its own continuation loop

Node timeouts/polling use milliseconds. Python uses seconds. Standalone wait.seconds and site.max_age use seconds; scrape action waits use milliseconds. A client timeout does not cancel the remote operation or establish that no input happened. Original job results are retained separately from the 200-page scrape cache, within 16 MiB per job and 50 MiB per agent. Page site(status) with offset/limit; saved-space batches also publish batch.jsonl, while private batches keep results in memory.

Files, replay and human-owned settings

Existing operation Swarmtab replacement Boundary
page.screenshot({fullPage:true}) screenshot({full:true}) Inline MCP JPEG by default, or format:"png"; scale 0.1–2 and freeze_animations:true are supported; consent can block cross-origin frames
Write screenshot to path Decode returned image block in caller Do not HTTP-fetch a swarmtab:// resource URI
page.pdf({format:"A4",path}) files({action:"pdf",paper:"a4"}) Scoped local result path; supports landscape, margins in inches, scale 0.1–2, background, pages
setInputFiles(paths) files({action:"upload",ref:<fresh number>,paths}) Atomic selection of 1–20 admitted files, up to 200 MiB total; multiple files require a multiple input. External paths need human admission; validation of every path precedes dispatch, so an invalid path leaves selection unchanged
Admitted upload files({action:"upload",ref,from:"inbox",name}) Human-granted Inbox file; not arbitrary filesystem access
Download catalog files({action:"downloads"}), then files({action:"outbox",name}) Agent-owned download names; chosen remote output directories do not transfer
Recording after session ends Human enables replay and exports before private session closes JPEG frames/actions to HTML/ZIP; no MP4/HLS, no agent replay-export API
Context proxies/headers/extensions Human configures approved saved-space settings No agent setter or uploaded extension-ID compatibility
CAPTCHA solving / private credentials ask_human and human private entry No automatic solver; wait while the human controls the browser

For a saved login, use client.call('space',{action:'use',name:'migration-demo'}) before navigation. This may wait for approval. Reuse that space only when it is free; do not assume Browserbase concurrent context sharing. Local development URLs use 127.0.0.1, with human allow_local approval, rather than container host.docker.internal. Execution stays on this Mac; hosted sessions, fleet regions and cloud uptime are outside the migration.

All documentation