Docs

Errors & statuses

Two kinds of failure. A request Unbrowse refuses is an HTTP error with a code. A run that started but did not succeed is a normal response whose status says so. Check the run's status, not only the HTTP code.

Error shape

Refused REST requests return a JSON body with a stable code and a human message:

{ "error": { "code": "quota_exceeded", "message": "…" } }

Branch on code. The message may change and sometimes carries the next step (a top-up link, how long to wait).

A run that fails comes back as a run, with status: "failed" and error: { code, message }:

{ "runId": "lrun_…", "status": "failed", "error": { "code": "rate_limited", "message": "…" }, "signIn": null }

POST /api/v1/runs answers 200 for a finished run whether it succeeded or failed, and 202 while it is accepted, working or input_required. A site tool call (POST /api/v1/sites/<host>/call/<tool>) answers 202 for input_required, otherwise 200.

Run statuses

statusmeaningwhat to do
accepted, workingStill running.Poll GET /api/v1/runs/<runId>.
succeededThe declared outcome was verified. A 200 from the site is not enough.Use result. The only status that bills.
input_requiredWaiting on a requirement: a choice, a value, an approval. Not a failure.Answer the open requirements on the same run: POST /api/v1/runs/<runId>/responses or MCP unbrowse.resume. Don't start a new run.
failedDid not succeed. error.code says why (below).Act on the code. Never billed.
outcome_unknownA write may have landed but no answer came back.Check the site before retrying. Automatic retries of the write stay off.
cancelledStopped at your request.A request already sent can't be unsent. Known effects are kept.

Auth and billing

codeHTTPwhenwhat to do
unauthorized401No key, or a key Unbrowse doesn't know.Send Authorization: Bearer ub_live_…. Mint one at MCP & keys.
revoked403The key was revoked.Mint a new key.
forbidden403No grant on this workspace, or X-Unbrowse-End-User sent with a key that isn't an org key.Ask the workspace owner, or use an org key.
quota_exceeded402Free calls used up and no paid credits left. Checked before anything is sent.Buy credits at Billing. Failed calls never bill.
insufficient_paid_credits402The call costs more than the paid balance.Buy credits.

Request shape

codeHTTPwhenwhat to do
bad_request400The body isn't JSON.Send a JSON object.
unknown_argument422The tool doesn't take an argument you sent. The message lists the arguments it does take.Rename or drop the argument.
invalid_input400 / 422A run input matches no slot, or a choice isn't one of the options offered.Use the names and options from the message.
capability_not_found404The capability id you pinned doesn't exist.Search again, or run by task.
idempotency_conflict409The same Idempotency-Key was used with different input.Use a new key per distinct call.
not_in_scope403The connection is in a tool scope (/mcp/<slug>, ?scope=, ?apps=) and the tool or capability is outside it.Use a connection whose scope includes it, or add the app or tool to the scope in Apps & tools.
unknown_scope404No tool scope with that slug in this workspace.Create it in Apps & tools or POST /api/v1/scopes.
invalid_scope, too_many_scopes400A scope's slug isn't 1-40 lowercase letters, digits or dashes, it lists more than 200 apps and tools, or the workspace already has 20 scopes.Fix the body, or delete a scope.
not_found404No such route, site, tool or run.Check the path. GET /api/v1/sites/<host> lists a site's tools.
internal500Something broke on our side.Retry once. If it repeats, report the runId.

Capacity and limits

codeHTTPwhenwhat to do
browser_capacity429Every cloud browser is busy.Retry in about 30 seconds, or finish an open browse session.
too_many_sessions429Your workspace already has 3 browse sessions open.Finish or close one.
index_busy429An index job is already running for you.Wait for it: GET /api/v1/index/<id>.
index_limit429Daily index-job limit reached (5 a day).Try again tomorrow.
slot_unavailable, unavailable503A server is starting or failing over.Retry after the Retry-After header (1 to 5 seconds).

Why a run failed

These arrive as error.code on a run with status: "failed".

codewhenwhat to do
no_capabilityNo tool fits the task yet. result.next.tool is unbrowse.index.POST /api/v1/index with the URL, then GET /api/v1/index/{jobId}. Done means status: done and indexed > 0. model_unavailable means the indexing model is out of credit, not that the site refused and not that the workspace quota is spent. unbrowse.browse.open is an MCP tool: call it only when it is in this session's tool list. There is no POST /api/v1/browse/open, and the CLI does not open a browser.
session_or_permissionThe site answered 401/403 or showed a sign-in page. The run carries signIn.url when you have no login saved.Open signIn.url in the person's browser, save the login, then call again. Unbrowse signs in by itself from then on.
mfa_requiredThe site asked for a second factor. Also carries signIn.Same as above. Save a TOTP seed with the login to make it unattended.
rate_limitedThe site returned 429. The wait is in the message (the site's Retry-After, else 60 seconds).Wait that long, then retry.
challengeA bot check appeared in an unattended run.Run it again as interactive.
challenge_budget_exhaustedBot checks kept coming back.Retry later.
schema_or_auth_driftThe site returned a page where it used to return data: it changed, or the session is gone.Browse the flow once more so Unbrowse re-learns it.
outcome_not_verifiedThe site answered, but with an empty page or without the fields the tool promises.Check your inputs. Don't treat it as success.
not_foundThe site returned 404 or 410 for these inputs.Fix the inputs.
upstream_error, network_errorThe site erred or couldn't be reached.Retry later.
browser_capacity, render_failedThe page needed a browser and none could render it.Retry in about 30 seconds.
destination_deniedThe URL is private, loopback, or outside the tool's site.Don't retry.
policy_deniedThe task or page tried to move a secret somewhere it shouldn't go.Don't retry.
declinedA required field was declined.Start a new run.
worker_lost, resource_lockedThe worker stopped, or another agent holds the same resource.Retry.

Answering an input_required run

codeHTTPwhenwhat to do
conflict409The run isn't waiting for input.GET the run and act on its status.
stale_revision409The run changed since you read it.GET it again and send the current stateRevision.
stale_requirement, expired_requirement409That requirement was replaced or expired (after 15 minutes).GET the run and answer its current requirements.
unknown_requirement, invalid_option, invalid_answer422The answer names no open requirement, or the value isn't one of the options.Answer from the run's requirements.

Logins

codeHTTPwhenwhat to do
credential_required412A browse autofill found no saved login for the site. The error carries a one-time url.Open the url, save the login, repeat the action. The agent never sees the value.
no_login_form422No login fields are visible on the page.Go to the sign-in page first.
request_closed409The one-time link was already used, declined, or expired (after 30 minutes).Ask for a new link.

Browse sessions

codeHTTPwhenwhat to do
session_expired410The session ended: idle for more than 5 minutes, finished, or the host restarted.Open a new session. What it learned is kept.
stale_ref409The @ref isn't in the latest snapshot.Take a new snapshot and use its refs.
site_unreachable, page_unresponsive504The page didn't load in 30 seconds, or the snapshot took longer than 15.Wait, then retry.
egress_unavailable502The residential network refused the connection from three IPs in a row.Retry in a minute, or pass another country.
blocked502A bot check or an empty page over both HTTP and the browser.Don't retry soon.

MCP

Tool failures come back as JSON-RPC errors. The string code is in error.data.code, the same code as in REST. The numeric code follows the HTTP status: 401 → -32001, 403 → -32003, 404 → -32004, anything else → -32000. On MCP, error.data.details also carries fields like retryAfter.

A run with status: "failed" is not a JSON-RPC error. It is a normal result with isError: true.

`-32042` (URL elicitation). If your client declares capabilities.elicitation.url at initialize, a missing login or an empty balance comes back as -32042, with data.elicitations[0].url. Show that link to the person. When they finish, call again. Clients without URL elicitation get the plain error, or a run result carrying signIn.

An unauthenticated request to /mcp gets 401 with a WWW-Authenticate header that points to OAuth discovery. OAuth clients sign in from there. Other clients send Authorization: Bearer ub_live_….