Local API
Local API quickstart.
On this page
The desktop app has a REST API for your own scripts. It listens on 127.0.0.1 only, so only programs on your computer can reach it. It is off by default.
Turn it on
- Open Integrations in the sidebar (Cmd/Ctrl+I) and choose the Local API tab.
- Turn on Enable Local API Server.
- Copy the Authentication Token. See Authentication.
| What | Where |
|---|---|
| REST API | http://127.0.0.1:10108, paths under /v1 |
| OpenAPI spec | /openapi.json and /v1/openapi.json, no token needed |
| Browser CDP | A free port per launch, returned by POST /v1/profiles/{id}/run |
| MCP | Remote only, at https://api.donutbrowser.com/api/mcp. See Remote MCP |

You can change the port on the same tab. If the port is in use when the app starts, the server takes a random free port and the tab shows it.
First request
Export the token as DONUT_API_KEY, then list your profiles:
curl -sS http://127.0.0.1:10108/v1/profiles \
-H "Authorization: Bearer $DONUT_API_KEY" | jq '.profiles[].name'// Node 18+ has fetch built in.
const response = await fetch("http://127.0.0.1:10108/v1/profiles", {
headers: { Authorization: `Bearer ${process.env.DONUT_API_KEY}` },
});
const { profiles } = await response.json();
console.log(profiles.map((p) => p.name));# pip install requests
import os, requests
response = requests.get(
"http://127.0.0.1:10108/v1/profiles",
headers={"Authorization": f"Bearer {os.environ['DONUT_API_KEY']}"},
timeout=10,
)
response.raise_for_status()
print([p["name"] for p in response.json()["profiles"]])$response = Invoke-RestMethod -Uri "http://127.0.0.1:10108/v1/profiles" `
-Headers @{ Authorization = "Bearer $env:DONUT_API_KEY" }
$response.profiles | Select-Object nameLaunch a profile and attach Playwright
Launching and driving browsers needs Pro, Team or Enterprise. Other plans get 402.
curl -sS -X POST http://127.0.0.1:10108/v1/profiles/<profile id>/run \
-H "Authorization: Bearer $DONUT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","headless":false}'
# {"profile_id":"…","remote_debugging_port":53127,"headless":false,"attached":false}The browser binds its DevTools port to 127.0.0.1. If the profile already runs, the call returns its current port with "attached": true and does not restart it.
import { chromium } from "wayfern"; // or from "playwright"
const browser = await chromium.connectOverCDP(`http://127.0.0.1:${port}`);
const context = browser.contexts()[0];
const page = context.pages()[0] ?? (await context.newPage());
await page.goto("https://example.com");POST /v1/profiles/{id}/kill stops the profile. See Wayfern and Playwright for humanized input, and the API reference for every route.
What needs a plan
| Routes | Plan |
|---|---|
| Profiles, groups, tags, statuses, templates, proxies, VPNs, extensions, cookies, import | Every plan |
run, kill, open-url, batch run and stop, agent routes | Pro, Team or Enterprise |
run-remote and remote sessions | Pro, Team or Enterprise |
cloud-sync | A plan with cloud backup: Solo, Pro, Team or Enterprise |
| Cookie Bot routes | Solo, Pro, Team or Enterprise |
Launch routes count against an hourly automation limit when your plan sets one. Over the limit they answer 429 with a Retry-After header. Inside the browser, Wayfern accepts up to 100 automation (CDP) requests a minute on Pro, Team and Enterprise.
Checked against the app on .