Browser Actions API
All routes are under /browser/{sid} where {sid} is the session ID.
Navigate
Section titled “Navigate”POST /browser/{sid}/navigate{"url": "https://example.com", "timeout": 30000}Response:
{ "url": "https://example.com/", "status": 200, "snapshot": "h1 \"Example Domain\"\n...", "truncated": false}POST /browser/{sid}/wait{"selector": ".results", "load": false, "timeout": 30000}Pass "selector" to wait for an element, or "load": true to wait for the
page’s load event (not network-idle — use navigate’s wait: "networkidle" for that).
Response: {"ok": true}
Get URL
Section titled “Get URL”GET /browser/{sid}/urlResponse: {"url": "https://example.com/current-page"}
Snapshot
Section titled “Snapshot”GET /browser/{sid}/snapshot?search=<regex>&locator=<selector>Response:
{ "tree": "navigation\n [1] a \"Home\"...", "truncated": false, "hint": "Page has 600+ nodes. Use search param to filter."}Screenshot
Section titled “Screenshot”POST /browser/{sid}/screenshot{ "full": false, "path": "/tmp/page.png", "width": null, "scale": null, "quality": "medium"}quality is a preset: "low" (400px), "medium" (800px), "high" (1200px). Overrides width when set.
Response: {"path": "/Users/you/.brow/screenshots/2024-01-01T12-00-00.png"}
GET /browser/{sid}/html?locator=<selector>&search=<regex>Response: {"html": "<main>...</main>"}
Console logs
Section titled “Console logs”GET /browser/{sid}/logs?search=<str>&count=50Response: {"logs": "[log] Page loaded\n[error] Failed to fetch..."}
Network requests
Section titled “Network requests”GET /browser/{sid}/network?search=<str>&count=50&include_static=false&include_response=falseResponse: {"network": "GET 200 application/json https://api.example.com/data\n → {\"results\":..."}
DELETE /browser/{sid}/networkResponse: {"ok": true}
POST /browser/{sid}/fetch{ "url": "https://api.example.com/data", "method": "GET", "headers": {"Accept": "application/json"}, "body": null, "no_cookies": false}When no_cookies is false (default), the request runs inside the browser page using fetch() — cookies and session are included. When true, a plain httpx request is made without any browser state.
Response:
{ "status": 200, "contentType": "application/json", "body": "{\"results\": [...]}"}WebSocket messages
Section titled “WebSocket messages”GET /browser/{sid}/websocket?search=<str>&count=50Response: {"websocket": "recv wss://example.com/ws {\"op\":\"replace\",\"path\":\"/price\",\"value\":1.85}\n..."}
DELETE /browser/{sid}/websocketResponse: {"ok": true}
POST /browser/{sid}/click{ "selector": "button[type=submit]", "ref": null, "timeout": 30000, "retry": 0, "wait_for_selector": true}Provide either selector or ref (element ref from snapshot). retry attempts the click up to N additional times (1-second delay each) if it fails.
Response:
{"ok": true, "snapshot": "...", "truncated": false}Click-until
Section titled “Click-until”Repeat a click until a selector clears, or a safety cap is hit — pagination, “load more”, batch-dismiss actions.
POST /browser/{sid}/click-until{ "selector": "button.load-more", "until_gone": "button.load-more", "max_iterations": 25, "settle_ms": 500, "timeout": 30000}settle_ms is a pause after each click for the page to refill before the
next until_gone check.
Response:
{"iterations": 4, "done": true, "reason": "until_gone cleared"}done: false means the cap was hit (or the selector never appeared) with
work possibly still remaining — always check reason, since a capped sweep
looks identical to a finished one until you read it.
POST /browser/{sid}/fill{ "selector": "#email", "ref": null, "value": "user@example.com", "timeout": 30000, "retry": 0, "wait_for_selector": true}Response: {"ok": true, "snapshot": "..."}
Select
Section titled “Select”POST /browser/{sid}/select{"selector": "#country", "ref": null, "value": "Slovakia", "timeout": 30000}Response: {"ok": true, "snapshot": "..."}
POST /browser/{sid}/type{"text": "Hello, world"}Types at the current focus position using real keyboard events.
Response: {"ok": true}
POST /browser/{sid}/key{"key": "Enter"}Accepts any Playwright key string: Enter, Tab, Escape, Meta+a, Control+c, ArrowDown, etc.
Response: {"ok": true, "snapshot": "..."}
POST /browser/{sid}/hover{"selector": "nav .dropdown", "timeout": 30000}Response: {"ok": true}
Scroll
Section titled “Scroll”POST /browser/{sid}/scroll{"pixels": 1000}Or scroll to an element:
{"selector": "#footer"}Response: {"ok": true}
POST /browser/{sid}/drag{"source": ".draggable-card", "target": ".drop-zone"}Response: {"ok": true}
Upload
Section titled “Upload”POST /browser/{sid}/upload{"selector": "input[type='file']", "filepath": "/path/to/file.csv"}Response: {"ok": true}
Action log
Section titled “Action log”GET /browser/{sid}/actions?as_json=falseReturns the action log as formatted text (default) or JSON array.
Response (text):
{"actions": "1 navigate https://example.com [200]\n2 click button[type=submit]\n..."}Response (JSON):
{ "actions": [ {"seq": 1, "action": "navigate", "url": "https://example.com", "status": 200}, {"seq": 2, "action": "click", "selector": "button[type=submit]"} ]}DELETE /browser/{sid}/actionsResponse: {"ok": true}
Replay playbook
Section titled “Replay playbook”POST /browser/{sid}/replay{ "playbook": { "name": "my-playbook", "base_url": "https://example.com", "auth": "none", "stop_on_failure": false, "vars": {"query": "test"}, "steps": [ {"action": "navigate", "url": "/search"}, {"action": "fill", "selector": "input", "value": "{query}"}, {"action": "key", "key": "Enter"}, {"action": "wait", "selector": "#results", "state": "visible", "timeout": 10000}, {"action": "assert", "selector": ".result-count"}, { "action": "fetch", "url": "/api/results?q={query}", "headers": {"X-Api-Key": "{api_key}"}, "output": "results" }, { "action": "for_each", "var": "item", "items": "results", "steps": [{"action": "navigate", "url": "/item/{item[id]}"}] } ] }, "vars": {"query": "override"}}Variables in vars override those in playbook.vars. A top-level auth: "none" applies to every fetch step that doesn’t set its own auth.
stop_on_failure: true halts the whole run — including out of a for_each
— after the first failed step, instead of the default “record it and keep
going.”
Supported actions: navigate, click, fill, key, select, fetch
(method, headers, auth, output — captured JSON becomes {name} and
{name[key]} in later steps, expect_status to override the default
ok = status < 400), wait (selector+state, or ms for a fixed
sleep), assert (same shape as wait, but a non-match fails the step), and
for_each (loop nested steps over a literal or captured list).
Response:
{ "results": [ {"action": "navigate", "ok": true, "url": "https://example.com/search", "status": 200}, {"action": "fill", "ok": true}, {"action": "key", "ok": true}, {"action": "wait", "ok": true}, {"action": "assert", "ok": true}, {"action": "fetch", "ok": true, "url": "...", "status": 200, "data": {...}} ]}