**Donut Browser ships an HTTP server that starts when you launch the app.** It listens on loopback only — your local API surface, never the network.

### Ports and endpoints

- **REST API:** `http://127.0.0.1:10108` — base path `/v1`, OpenAPI spec at `/openapi.json`.
- **Per-profile CDP:** the run response returns a per-profile CDP port (default `9222`). Connect Playwright, Puppeteer, or any CDP client.
- **MCP server:** stdio transport — invoked by Claude / Cursor / your MCP host via the `donut-mcp` binary shipped with the app. See the [MCP page](/docs/mcp).

### First request

Grab your bearer token from **Settings → Integrations → Local API** (see [Authentication](/docs/authentication) for the panel screenshot) and export it as `DONUT_API_KEY` (Unix shells) or `$env:DONUT_API_KEY` (PowerShell). Then ping the spec:

```bash
curl -sS http://127.0.0.1:10108/openapi.json \
  -H "Authorization: Bearer $DONUT_API_KEY" | jq '.info'
```

```javascript
// Node 18+ has fetch built-in. Set DONUT_API_KEY in your environment.
const response = await fetch("http://127.0.0.1:10108/openapi.json", {
  headers: { Authorization: `Bearer ${process.env.DONUT_API_KEY}` },
});
const spec = await response.json();
console.log(spec.info);
```

```python
# pip install requests
import os, requests

response = requests.get(
    "http://127.0.0.1:10108/openapi.json",
    headers={"Authorization": f"Bearer {os.environ['DONUT_API_KEY']}"},
    timeout=10,
)
response.raise_for_status()
print(response.json()["info"])
```

```powershell
$response = Invoke-RestMethod -Uri "http://127.0.0.1:10108/openapi.json" `
    -Headers @{ Authorization = "Bearer $env:DONUT_API_KEY" }

$response.info | ConvertTo-Json
```

Expected response shape:

```json
{
  "title": "donutbrowser",
  "description": "Simple Yet Powerful Anti-Detect Browser",
  "version": "0.24.2"
}
```