stealthDOCS
SDKs

Python SDK

stealth_browser: a standard-library client for the platform API, Python ≥ 3.10, synchronous with an asyncio facade.

Install

Not on PyPI yet; publishing follows the platform launch. Until then, put sdks/python from a repository checkout on your path:

cURL
export PYTHONPATH="/path/to/stealth-browser/sdks/python:$PYTHONPATH"

Standard library only. It mirrors the JavaScript client resource for resource; the one difference is that it does not run the platform contracts locally, so a bad request is reported by the API rather than before sending.

Client

Python
import osfrom stealth_browser import StealthBrowser, StealthApiError client = StealthBrowser(os.environ["STEALTH_API_KEY"], base_url=os.environ["STEALTH_API_URL"])

StealthBrowser(api_key, base_url=..., timeout=60.0, retries=2, opener=None). The key's project is the project. timeout bounds each request; retries applies to idempotent requests only (GETs and creates, which carry an Idempotency-Key). Environment proxies are ignored unless you pass your own urllib.request opener.

Surfaces

SurfaceMethods
client.run(task, **fields, poll_interval=, timeout=, on_event=)Create and wait; fields are run fields (model_id, session_id, workspace_id, profile_id, judge, allowed_domains, …)
client.runscreate, get, list, cancel, wait, events (generator), message(run_id, text), withdraw_message(run_id, message_id)
client.agent_sessionslist, get, rename(id, title), delete(id)
client.models()The catalog
client.browserscreate → CloudBrowser, attach, get, list, stop
client.assetslist(browser_id), get, text, content (bytes), retry
client.profileslist, create, get, update, import_state, save(profile_id, browser_id)
client.workspaceslist, create, get, update, sessions; files.list/upload/download/text/move/folder/delete/archive
client.scheduleslist, get, history, preview, run_now
client.integrations, client.proxieslist, get; policy, credentials
client.billingget, set_budget(usd or None), statement(month), ledger(limit), rates
client.history, client.usage, client.projectlist(**query), get(days=), current
client.request(method, path, body=, query=, ...)Any other route, with the same errors and retries
verify_webhook(body, headers, secret)Standard Webhooks v1 check; returns the event or raises webhook_signature

CloudBrowser

Python
browser = client.browsers.create({"settings": {"locale": "en-US"}})browser.ready()browser.navigate("https://example.com")state = browser.observe(boxes=True)          # the observation object as the API sends itlink = next((t for t in state["targets"] if t["role"] == "link"), None)if link:    browser.click(link["ref"])              # observation frames are handled insidetext = browser.text(format="markdown")["text"]matches = browser.find(role="button", name="sign in")["matches"]browser.stop()

ready, navigate, observe(boxes=, attributes=), preview, snapshot, act(name, params) plus click, type, select, press, scroll, hover, wait, click_at, back, forward, reload, switch_tab, close_tab, new_tab, dialog_policy, the read-only find(**query) and text(format=, max_chars=), and events, control, assets, stop. Frames, stale-frame retries and the read-only / page-changing distinction work as in the JavaScript client.

asyncio

AsyncStealthBrowser is the same client, awaitable: every method is a coroutine, events is an async iterator, browsers.create / attach give an AsyncCloudBrowser. It is a facade over the synchronous client, not a second transport — each call runs the synchronous implementation in a worker thread (asyncio.to_thread), so await never blocks the event loop and asyncio.gather runs calls concurrently, while retries, idempotency and frame handling stay one code path.

Python
import asyncio, osfrom stealth_browser import AsyncStealthBrowser async def main():    async with AsyncStealthBrowser(os.environ["STEALTH_API_KEY"], base_url=os.environ["STEALTH_API_URL"]) as client:        run = await client.runs.create({"task": "Open https://example.com and report the title."})        async for event in client.runs.events(run["id"]):            print(event["type"])        browser = await client.browsers.create()        await browser.ready()        state = await browser.observe()        await browser.stop() asyncio.run(main())

Errors and streams

Every failure is a StealthApiError with status, code (see Errors), request_id, details and local. events() and on_event use the API's server-sent events when offered and fall back to cursor polling — also when the API refuses a stream with rate_limited; client.diagnostics counts both.