SDKs
The Python and Node clients for the Scalebrowser daemon: a typed REST client plus a direct-CDP driver, in the two languages the daemon ships clients for.
Two clients, one shape. Each is a typed wrapper around the REST API plus a direct-CDP driver, so the target attach, the frame bookkeeping and the humanized input are already wired up.
They are also the only public artifact this product ships. The daemon and the engine come with your licence; the clients are MIT-licensed, because a client nobody can install is a client nobody uses.
Install
pip install scalebrowserPython needs 3.10 or newer, Node 18 or newer. The Node package ships ESM and CJS side by side with type declarations.
Connect and drive a page
Both clients take the daemon's address and your API token. launch does the whole dance: start the profile, open a CDP session, hand back something you can drive, and stop the profile when the block ends.
from scalebrowser import ScalebrowserClient, CreateProfileBody
sb = ScalebrowserClient(base_url="http://127.0.0.1:8787", token="…")
profile = sb.create_profile(CreateProfileBody(name="acct-01"))
with sb.launch(profile.id, headless=True) as page:
page.navigate("https://example.com")
print(page.evaluate("document.title"))
page.humanize_click(120, 240)
sb.close()await using in the Node example stops the profile and closes the CDP session when the block exits. Without it, startProfile returns the same cdp_ws endpoint the REST API returns, connectCdp opens the session, and you close both yourself.
Python mirrors the sync client method for method in AsyncScalebrowserClient for asyncio code.
Every route reaches both clients
That is a rule with a test behind it rather than an intention. sdk_coverage compares the daemon's own route table against both clients and fails the build when a route reaches neither, which is how the gap was found in the first place: the daemon served 57 routes and the clients reached 37 and 36, missing runs, mailboxes, passkeys, interruptions, artifacts and the cookie reveal, with nothing failing anywhere.
Method names follow the route, in snake_case for Python and camelCase for Node:
| Area | What it covers |
|---|---|
| Profiles | create, get, list, update, delete, start, stop |
| Bulk | create from a preset, start, stop, delete, assign a proxy or extensions |
| Groups and presets | the full set, plus get_persona_constraints for the regions a preset may pin |
| Proxies | the full set, plus check_proxy_config to probe a configuration before saving it |
| Extensions | attach and detach per profile, and the daemon-wide library |
| Credentials and cookies | list and store; revealing a value needs the vault password |
| Sessions | export and import a profile's cookies |
| Mailboxes | where a profile's confirmation codes arrive |
| Passkeys | metadata only, because the private key has no field and no endpoint |
| Agent runs | read-only: runs, steps, screenshots, activity |
| Interruptions | who may answer when the browser asks something |
| Artifacts | hand the daemon a file, or fetch a screenshot, download or saved PDF as bytes |
| Tasks and profile memory | a profile's task list, and its PROFILE.md |
| Videos | recordings, renders, share links, media, voice providers, codecs |
| Secrets | the values an agent may use but not read, plus scrubbing and run |
| Remote access and the exit rule | read and set the two machine-local switches |
| Input, metrics, account, events | send_input, get_metrics, get_account, health, the event stream |
Four blocks are deliberately absent, each named in the coverage test with its reason. The clearest is profile sync: four of its routes carry the sync passphrase and refuse any caller that is not on loopback, so an SDK method could only ever be refused.
The event stream
async for event in sb_async.events():
print(event.type) # profile_started / profile_crashed / …Same stream as /v1/events, typed on the way in.
When a call fails
Errors carry the daemon's own contract rather than a client-side interpretation: an API error has the HTTP status, the numeric code and the message. Python raises ApiError, NetworkError when the daemon is unreachable, and CdpError for protocol failures; Node mirrors it. The numbers are listed under Errors.
The driver, and what it never does
The CDP session gives you send for any command, navigate, evaluate, event subscription, and the humanized input calls that route through the daemon.
evaluate never calls Runtime.enable. That command is a known detection signal, so the driver uses an isolated world instead. If you drop to send and enable it yourself, you undo that on purpose. The reasoning is on direct CDP.
Next
- REST API: the surface both clients wrap.
- Errors · Events: the two contracts a client has to handle.
- Direct CDP: the driver half, in more detail.