> Documentation index: https://unbrowse.ai/llms.txt. Fetch it to find every page.

# Call an indexed website

> Every website Unbrowse has compiled is an HTTP API: one `POST` per tool, typed inputs, a verified result. This page is the contract — the endpoint, auth, the answer, the options and the errors — with the same call in curl, TypeScript and Python.

## What each site gives you

For a host such as `docs.rs`:

| What | Where |
|---|---|
| Tools, with ready-to-copy calls | `GET https://unbrowse.ai/api/v1/sites/docs.rs` (no key needed) |
| OpenAPI 3.1 document | `GET https://unbrowse.ai/api/v1/sites/docs.rs/openapi.json` (no key needed) |
| One tool | `POST https://unbrowse.ai/api/v1/sites/docs.rs/call/<tool>` |
| The same tools as an MCP server | `https://unbrowse.ai/api/v1/sites/docs.rs/mcp` |
| The page people read | `https://unbrowse.ai/sites/docs.rs` |

Find a site with `GET /api/v1/sites?q=<words>`. A site Unbrowse has not compiled yet can be learned: see the [Quickstart](/docs/quickstart).

## The OpenAPI document

`openapi.json` is a complete OpenAPI 3.1 description, so any OpenAPI tool can read it:

- one operation per tool, `operationId` = the tool's name;
- the request body is the tool's inputs as a JSON Schema, with an `example`: an input Unbrowse checked the tool with. Send it as-is for a first call that works;
- the `200` answer's `result` is typed from verified answers;
- every status the endpoint returns (`200 202 400 401 402 404 409 422 429 504`), the headers it reads, and `x-codeSamples` in curl, TypeScript and Python.

Generate a client with any OpenAPI generator, for example:

```bash
npx openapi-typescript https://unbrowse.ai/api/v1/sites/docs.rs/openapi.json -o docs-rs.d.ts
```

Signed in (with your key), the document also lists your own tools on that site (`my__…`) and your organisation's (`org__…`). Add `?minVersion=YYYY.MM.DD` to leave out tools an older Unbrowse version generated.

## Auth

Every call takes your API key as a bearer token. Mint one in [MCP & keys](/app/keys); keys start with `ub_live_`.

```
Authorization: Bearer ub_live_…
```

An OAuth access token (from the MCP sign-in) works the same way. With an organisation key, add `X-Unbrowse-End-User: <their id>` to run as one of your users, in their own workspace with their own logins.

## Call a tool

The body is the tool's inputs. Nothing else is required.

```bash
curl -s 'https://unbrowse.ai/api/v1/sites/docs.rs/call/docs_rs__get_search' \
  -H "authorization: Bearer $UNBROWSE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"serde"}'
```

TypeScript, with the SDK (`npm i @unbrowse/sdk`):

```ts
import { Unbrowse } from "@unbrowse/sdk";

const ub = new Unbrowse(); // reads UNBROWSE_API_KEY
const run = await ub.callTool("docs.rs", "docs_rs__get_search", { query: "serde" });
if (run.status === "succeeded") console.log(run.result);
```

Python:

```python
import os, requests

r = requests.post(
    "https://unbrowse.ai/api/v1/sites/docs.rs/call/docs_rs__get_search",
    headers={"Authorization": f"Bearer {os.environ['UNBROWSE_API_KEY']}"},
    json={"query": "serde"},
    timeout=120,
)
r.raise_for_status()
run = r.json()
if run["status"] == "succeeded":
    print(run["result"])
```

## The answer

Every call answers with the run:

```json
{
  "runId": "lrun_12meih1",
  "status": "succeeded",
  "capabilityId": "public.docs_rs.get_search",
  "result": { "title": "…", "text": "…", "links": [{ "text": "serde", "href": "https://docs.rs/serde" }] },
  "via": "http"
}
```

| `status` | HTTP | Meaning | Billed |
|---|---|---|---|
| `succeeded` | 200 | `result` is the site's answer, verified | once |
| `failed` | 200 | `error` says why (the site refused, changed, or did not answer) | never |
| `input_required` | 202 | the tool needs a choice it found on the site: answer `requirements` with `POST /api/v1/runs/{runId}/responses` | when it succeeds |

Only verified successes bill. A tool marked `x-unbrowse-personal` runs signed in as you on its site: its answers are your account's.

## Options

| Option | How | What it does |
|---|---|---|
| Deadline | header `x-unbrowse-deadline-ms`, or `deadlineMs` in the body (5,000–300,000; default 60,000) | Answer within this time. Past it: `504 run_timeout` with a `runId`; the run keeps going, poll `GET /api/v1/runs/{runId}` |
| Retry safely | header `Idempotency-Key: <your id>` | A repeat with the same key returns the same run instead of starting another |
| Smaller answers | `select` in the body: `["results[].{title,url}", "total"]` | Keep only these parts of the result. Results over 40,000 characters are shortened (`truncated`) |
| Act for an end user | header `X-Unbrowse-End-User` (organisation keys) | Runs in that user's workspace |

A tool that has an input named `deadlineMs` or `select` keeps it; the option then goes in the header (deadline) or is not available (select).

## Errors

Errors answer `{ "error": { "code", "message" } }`; the message says what to do.

| HTTP | `code` | What to do |
|---|---|---|
| 400 | `bad_request`, `invalid_input` | Send JSON; fix the value the message names |
| 401 | `unauthorized` | Send `Authorization: Bearer <key>` |
| 402 | `quota_exceeded`, `insufficient_paid_credits` | Out of verified calls this month, or out of paid credits: top up |
| 404 | `not_found` | No such tool on this site: list them with `GET /api/v1/sites/<host>` |
| 409 | `tool_quarantined` | The tool failed its checks and is rechecked automatically; the message names what to use now |
| 422 | `unknown_argument` | An input the tool does not take; the message lists the ones it does |
| 429 | `rate_limited` | Retry after `Retry-After` |
| 504 | `run_timeout` | Poll the `runId` it gives, or raise the deadline |

Every code: [Errors & statuses](/docs/errors).

## Which tools you see

Tools are checked on a schedule. One that keeps failing is quarantined: it leaves listings and the OpenAPI document until it passes again, and calling it answers `409 tool_quarantined`. A tool that needs a sign-in is offered only when your workspace has a saved login or a live session for that site.

## Or use MCP

The same tools are an MCP server per site, for agents that speak MCP:

```bash
claude mcp add --transport http docs-rs https://unbrowse.ai/api/v1/sites/docs.rs/mcp
```

All sites at once, with discovery and the cloud browser: `https://unbrowse.ai/mcp`.
