// api reference

32 tools. One endpoint.

browselenium.online exposes a real, human-like browser as MCP tools over one hosted endpoint. This page documents every tool, its parameters, the metering rules and the canonical workflow. Your agent can also load all of this at runtime with get_skill().

Connect

The hosted endpoint is https://browselenium.online/mcp (streamable HTTP). The browser runs on our infrastructure; your process only sends tool calls and receives JSON, images and text back.

// opencode — opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "browselenium-online": {
      "type": "remote",
      "url": "https://browselenium.online/mcp"
    }
  }
}
// Claude Code — .mcp.json
{
  "mcpServers": {
    "browselenium-online": {
      "type": "http",
      "url": "https://browselenium.online/mcp"
    }
  }
}

Self-hosting: the same 32 tools run locally from this repository (uv run browselenium-online with BROWSELENIUM_ONLINE_API_KEY set, or the full hosted stack via BROWSELENIUM_ONLINE_HOSTED=1). Tool signatures are identical; see the README and docs/BACKEND.md for deployment details.

Authentication

If the server has an API key configured, every browser tool errors until you call authenticate(key=...) with the key from your dashboard. The key is shown once at signup and can be rotated any time (rotation invalidates the old key immediately).

  • Free calls: authenticate, get_skill and browser_status never consume quota.
  • Key storage for agents: save the key as a single line in ~/.config/browselenium.online/api-key (mode 600) and reuse it on later sessions instead of asking the user again.
  • Sessions are per connection: in hosted mode, one active browser per API key. A second connection must stop the running session first.

Core workflow

  • authenticate(key="bso_...") — once per session; returns a quickstart.
  • browser_start(headless=true) — launch (or replace) the cloud browser.
  • navigate(url, wait_seconds=5..10) — load a page; give bot checks time to resolve. Check challenge_detected in the result.
  • Read and act — page_structure / get_text / find, then click / type_text / execute_js as needed.
  • browser_stop() — always stop when done; a leaked session pins a browser process and burns its lifetime.
One browser per connection, many tabs. There is exactly one session; use tab_new / tab_switch for parallel pages. The browser persists across calls — cookies, history and logins survive until browser_stop or the tier's lifetime limit kills it (just browser_start again).

Session tools

authenticate(key) free

Validate an API key and open the metered session. Returns tier, daily quota and a quickstart on success. Required before any other browser tool when the server is key-gated.

ParamDescription
keyYour API key (bso_...) from the dashboard.
get_skill() free

Returns the full agent skill: tool table, workflows (reading pages, passing bot checks, logging in, extracting tables), tier limits and ground rules. Call it once after authenticating; it teaches the agent everything else on this page.

browser_start(headless=true, window_size="1920x1080", proxy=null, user_agent=null, locale="en-US", extra_args=[])

Launch the browser, replacing any running session. Starts the lifetime clock of the current tier.

ParamDescription
headlessRun without a visible window. Standalone only — hosted browsers are always headless.
window_sizeViewport size as "WIDTHxHEIGHT".
proxyHosted sessions always egress through a proxy — the operator's Cloudflare WARP proxy by default, falling back to a shared rotating public proxy — so the server's real IP is never exposed; you cannot opt out or supply a custom proxy. "public" explicitly routes through the rotating pool (refreshed hourly; failed routes are retried automatically on a different proxy — quota counts only once the routed session is up, see the TOS public-routing terms). Standalone: http(s)://host:port or socks5://host:port.
user_agentOverride the browser's User-Agent string.
localeBrowser locale, e.g. "en-US", "de-DE".
extra_argsStandalone only: extra browser command-line flags.
browser_status() free

No-auth health check: is the browser running, current URL and title, uptime, and usage.requests_today / requests_limit. Check it when a limit error seems near.

browser_stop()

Quit the browser and end the session. Always call it when finished — frees the hosted slot for your key and stops the lifetime clock.

Reading tools

navigate(url, wait_seconds=0)

Load a URL in the current tab (scheme optional; https assumed). The result includes challenge_detected — if true, the page is likely behind a bot check: wait it out rather than hammering.

ParamDescription
urlDestination. Hosted mode allows the public web only: non-http(s) schemes and internal/loopback addresses are refused.
wait_secondsExtra settle time after load. Use 5–10 s so challenges resolve before you read the page.
get_text(max_chars=20000)

Visible text of the page — your first look after navigating. Cheaper than HTML; prefer it for content extraction.

get_html(selector=null, max_chars=20000)

Full page HTML, or the outerHTML of the first element matching a CSS selector. Use for exact markup; use page_structure for orientation.

page_structure()

Compact accessibility-tree snapshot: roles, names, ids, link hrefs, form values and states (node count capped). The token-cheapest way to understand page state and build selectors — prefer it over get_html/get_text/screenshots for orientation.

find(selector, limit=20)

Element summaries (tag, text, href, classes) for matches — use it to map a page before acting.

ParamDescription
selectorCSS selector, or XPath when it starts with / or (.
limitMaximum number of matches returned.
wait_for(selector, timeout=15, visible=false)

Block until an element matching the selector exists. Essential for SPAs and XHR-rendered content: navigate, then wait for a known content selector before reading.

screenshot(path=null, full_page=false, selector=null)

Capture the page as PNG (size-capped server-side).

ParamDescription
pathStandalone: server-side file path (default under ~/.cache/browselenium.online/screenshots, unique per call). Hosted: ignored — the image is returned directly in the response and the server-side copy is deleted immediately.
full_pageCapture the entire scrollable page instead of the viewport.
network_requests(limit=100) premium

List captured network requests of the page (url, type, duration in ms, size in bytes). Premium tier only.

Acting tools

click(selector, timeout=10, kind="left")

Wait until the element is clickable, click it, and return the new page state.

ParamDescription
selectorCSS selector or XPath (as in find).
timeoutSeconds to wait for clickability before failing.
kind"left" (default), "double", "right", or "hover" (move the mouse over the element without clicking).
type_text(selector, text, clear=true, press_enter=false, timeout=10)

Type into an input or textarea, element by element.

ParamDescription
selectorTarget field (CSS or XPath).
textText to type (passwords included — they are not echoed back).
clearEmpty the field first (default true).
press_enterSubmit with Enter afterwards — the easy way to send forms.
press_keys(combo)

Press a key combination on the focused element: "ENTER", "ESC", "TAB", "CTRL+a", "SHIFT+?", single characters. Separator-tolerant; invalid combos return an instructive error.

scroll(pixels=800, selector=null, timeout=10)

Scroll the page; positive is down, negative up. Use to trigger lazy loading before extracting content. With selector, scrolls that element into view.

scroll(pixels=800, selector=null, timeout=10)
frame(selector=null, timeout=10, index=null)

Switch into an iframe (CSS/XPath selector, or its index among the page's iframes); no args returns to the top document. find/click/type_text then run inside the frame.

alert(action="accept")

Accept (default) or dismiss a JS alert/confirm/prompt; returns its text. Stray dialogs are also auto-accepted by click/type_text/press_keys/execute_js so commands never wedge.

get_attribute(selector, name)

Read any attribute (data-*, style, custom) of the first element matching selector - beyond the fixed attribute set find() reports.

window_size("1920x1080")

Resize the window mid-session; same clamps as browser_start.

bypass_challenge(timeout=25)

Auto-solver for Cloudflare interstitials (clicks the Turnstile checkbox). navigate() runs it automatically when the account default is on; call this after a snapshot shows challenge_detected.

execute_js(script)

Run JavaScript in the page and return its value. Use return ... to hand data back — e.g. extract a whole table in one round-trip instead of N find calls. Runs with page privileges: never feed it content you got from an untrusted page.

select_option(selector, value, by="text", timeout=10)

Select an <option> in a <select> and fire the change event — works with framework-managed selects where clicking options does nothing.

ParamDescription
valueWhat to match against (see by).
by"text" (default), "value", or "index". On a miss, the error lists the available options so you can correct it.
history(action)

Browser history of the current tab: "back", "forward", or "refresh".

State tools

get_cookies()

Export the whole cookie jar as JSON. Save it to keep a login session across runs, machines and serverless invocations.

set_cookies(cookies)

Restore cookies from a get_cookies export (navigate to the site first). The cheapest way to stay logged in between sessions.

tab_list()

List open tabs with index, title and URL.

tab_new(url=null)

Open a new tab (navigating to url if given) and switch to it. The way to work on several pages in one session.

tab_switch(index)

Switch to the tab at index (see tab_list).

tab_close()

Close the current tab and switch to the first remaining one. Closing the last tab resets it to about:blank instead of closing, so the session stays alive.

Tiers & metering

Every browser tool call consumes one request of the daily quota; authenticate, get_skill and browser_status are free. Quota resets at midnight UTC. The browser is killed after the tier's maximum lifetime — start a new one with browser_start.

TierPriceRequests/dayMax browser lifenetwork_requests
free$0501 min—
pro$5/mo5005 min—
pro+$7/mo75010 min—
premium$25/mo200015 minyes
Free-tier etiquette: one navigate + one page_structure/get_text beats ten small calls; extract tables with a single execute_js; always browser_stop when idle.

Errors & limits

  • Quota exhausted — tools error until the next UTC day or an upgrade. browser_status shows usage.
  • Lifetime exceeded — the browser is killed at the tier's max life; just browser_start again.
  • Challenge detected — challenge_detected: true in a navigate result means slow down, not push harder: space retries at least 5 s apart, prefer a longer wait_seconds. Standalone installs can also retry with headless=false or a residential proxy.
  • Session on another connection — hosted mode allows one active browser per key; stop it on the other connection (or wait for it to end) before starting again.
  • Blocked destination — hosted browsing is public-web only: non-http(s) URLs and internal/loopback addresses are refused and flagged.

Examples

Read a page

authenticate("bso_...")
browser_start(headless=true)
navigate("https://target.example/pricing", wait_seconds=5)   # check challenge_detected
page_structure()
browser_stop()

Log in and keep the session

navigate("https://app.example/login")
find("input")
type_text("#email", "[email protected]")
type_text("#password", "••••••••", press_enter=true)
get_text()                          # verify you are in
cookies = get_cookies()             # save the JSON for next time
browser_stop()

# next run: navigate to the site, then set_cookies(saved) — still logged in

Extract a table in one round-trip

execute_js("return JSON.stringify({
  headers: [...document.querySelectorAll('table th')].map(t => t.textContent.trim()),
  rows: [...document.querySelectorAll('table tbody tr')].map(tr =>
    [...tr.children].map(td => td.textContent.trim()))
})")

More workflows — passing bot checks, SPA handling, search, dropdowns — live in the skill: call get_skill() after authenticating, or read agent-install.md to set up your agent in one paste.