Protocol
SDK, then remote MCP.
Unbrowse is an authenticated action surface. The SDK is the product. MCP is a thin remote harness over it. Agent Skills are the YAML those harnesses already are.
Canonical REST
All agent traffic goes through /api/v1. The authenticated principal determines the workspace — a caller-supplied workspace id is never authority.
Authorization: Bearer ub_live_… · org keys act for one of your users with X-Unbrowse-End-User: <your user's id>. The full spec is /openapi.json (OpenAPI 3.1); signed in, the console's API & SDK page fills in your key.
Loading the reference…
TypeScript SDK
import { Unbrowse } from "@unbrowse/sdk";
// npm install @unbrowse/sdk
const ub = new Unbrowse({
apiKey: process.env.UNBROWSE_API_KEY!,
baseUrl: "https://unbrowse.ai/api/v1",
});
const run = await ub.run({
task: "top stories on Hacker News",
interactionMode: "unattended",
idempotencyKey: "hn-1",
});
if (run.status === "input_required") {
await ub.resume(run.runId, run.stateRevision, [{
requirementId: run.requirements[0].id,
expectedRevision: run.requirements[0].revision,
action: "accept",
values: { export_format: "csv" },
}]);
}MCP tools
Remote MCP at /mcp is Streamable HTTP (Grok, Claude, Cursor). /api/mcp is the same adapter over JSON-RPC. authorization and run actor. When three or fewer skills match, dedicated unbrowse.skill.* tools are listed with slot schemas from the harness YAML.
unbrowse.run— start a durable run; first request is fulfilled while indexingunbrowse.inspect— status, requirements, verified resultunbrowse.resume— answer progressive fields on the same rununbrowse.cancel— stop new dispatches, return an effect receiptunbrowse.scrape— one page as clean markdown (main content by default), with links and metadata; plain HTTP when the server's HTML has it, otherwise the cloud browser renders it. Every answer carriesmetadata.status(the page's real HTTP status),timing(ms, httpMs, renderMs, queuedMs) andattempts. It never runs pastdeadlineMs(default 45 s, 5–120 s): then it failsscrape_timeoutwith the stage it reached. A page that met a bot wall over HTTP and in the browser answersblockedat once for 10 minutes, withretryAfter. Over REST it isPOST /api/v1/scrapewith an API key ({ url, country, formats, format: "raw_html", onlyMainContent, render, deadlineMs }): 400 invalid input, 402 out of calls, 429 blocked with Retry-After, 504 scrape_timeout; charged only on successunbrowse.map— a site's URLs from its sitemaps and the page's links, same-site and filterableunbrowse.sites— what Unbrowse knows about each site before you act: public or behind a sign-in, the kept session (active, expired, logged out), the last sign-in, saved logins, tools already learned there, and bot checks.unbrowse.credits— free credits left this month (Unbrowse calls only) and paid credits (never expire), recent history, and a Stripe Checkout link for a packunbrowse.usage— this month's verified calls, rendered runs and their passthrough cost, and the quota left. Only verified successes billunbrowse.forget— delete one of your own learned capabilities (or unpin a public one)unbrowse.credentials.list,unbrowse.credentials.request,unbrowse.credentials.status— the password manager: saved logins as masked hints, and a one-time link for the person to save a login the agent needs. Values are filled into pages (browse.actautofill, orvaulton a field) and never returned to the modelunbrowse.discover— private space, then public registry. Each learned capability carrieshints: health from its run ledger,warm/rendered/cold, p50/p95 latency and the next stepunbrowse.browse.open,unbrowse.browse.snapshot,unbrowse.browse.act,unbrowse.browse.finish,unbrowse.browse.close— drive a recorded patchright cloud browser with@refsnapshots; the first task is fulfilled and finish (or close) compiles the site into alearned.*capability — an API call or a server-rendered results page, returned as{ title, text, links }. Logins fill from the vault (vault: "password"), never from the agentunbrowse.index,unbrowse.index.status— cover a whole site ahead of need: a background job where Unbrowse's own agent performs the site's core read-only capabilities, proves each with a browserless replay and adds them to your toolsunbrowse.replay.list,unbrowse.replay.search,unbrowse.replay.get,unbrowse.replay.timeline,unbrowse.replay.ask— session replay: find recorded sessions and moments, read a session as a text timeline, get its summary (intent, outcome, drop-off, bugs) and ask questions across sessions with cited moments. Your own cloud-browser sessions become replays (sida_<browse session id>); visitor sessions on unbrowse.ai are admin-only. Alsoclient.replays.*in the SDK andunbrowse replay …in the CLIunbrowse.learn— compile HAR files or recorded traces into a one-calllearned.*capability; see the live demo
Sites as tools
Unbrowse is a browser engine and a compiler: it compiles a website's own requests into an API, and the API into tools. Every compiled site is an MCP server at /api/v1/sites/<host>/mcp and an OpenAPI 3.1 document at /api/v1/sites/<host>/openapi.json; each tool carries its input schema and an output schema learned from verified responses. The public registry holds pre-indexed sites (search and page reads, re-verified on a schedule); your own learned capabilities appear as my__… tools in the main server, tools shared inside your org as org__…, and public ones you use stay in your tool list.
For agent builders
Building agents for other people? Create an org at /app/org and run Unbrowse for all of your users under one key and one bill.
- Send the org key with
X-Unbrowse-End-User: <your user's id>(REST or MCP). Each call runs in that user's own workspace, created on first use: their logins and sessions never mix with anyone else's. Clients that cannot send headers get a per-user key fromPOST /api/v1/org/users. - When a site needs your user's login, the run returns
signIn.url, a one-time/connect/…link that says “Your App wants to use your site account”. Your user needs no Unbrowse account; the login is sealed in their workspace and never reaches you or your model. - Every user's verified calls come out of the org's balance: its free allowance, then paid credits. Usage is shown per user; adding user ids adds no allowance.
- A read-only route one user teaches Unbrowse is shared with your other users as an
org__…tool, scrubbed of their data, never with a login. It goes public only if the org opts in.
Full guide: docs/orgs.md.
Logins
Unbrowse is a remote MCP server: agents call it, and it signs in to sites for them in its own cloud browser, or replays the site's API with the kept session. Agents never receive a password — they get a masked hint and which fields were filled. You keep logins in the password manager; Unbrowse seals each one (AES-256-GCM under a per-workspace key), types it into the site's own login form, and logs every use. Learned tools that sign in take the login from the vault by themselves.
- When a site needs a login you have not saved, the agent gets a one-time link (MCP error
-32042, or “open this link in the user's browser”). You save it on unbrowse.ai and the agent continues — it never sees the value. - A site you are signed in to keeps its session, sealed in the vault, and runs reuse it automatically. Opt out per site (Never, or Ask me to approve each reuse) in the password manager, or for the whole workspace with “Reuse my kept sign-ins automatically” there or
PUT /api/v1/workspace/session-reuse { reuse: false }. - Import from KeePassXC, 1Password, Bitwarden, Chrome, Firefox, Apple, LastPass or Dashlane (CSV).
Statuses
Every error code, and what to do about each: Errors & statuses.
- succeeded
- Declared business outcome independently verified. HTTP 200 is not enough.
- input_required
- Waiting on a versioned requirement. Not a capability failure. Resume the same run.
- outcome_unknown
- A mutation may have landed. Automatic mutating retries stay suspended until reconciliation.
- failed / cancelled
- Known effects are retained. Cancellation cannot unsend a delivered request.
Harness YAML
unbrowse/v1alpha1 packages declare slots, operations, bindings, guards and independent outcome checks. YAML is authoring; the runtime executes a typed IR. No eval, no shell, no website-provided expressions.