Local or cloud: one SDK
The same launch() that starts Clearcote on your machine starts it on our servers. Pass cloud=True (Python) or cloud: true (Node), or set CLEARCOTE_CLOUD=1, and you get the same Playwright Browser back, connected to a hosted browser. The rest of your script stays as it is.
Run a script in the cloud
You need SDK 0.34.0 or newer and an API key from the API keys page. Put the key in CLEARCOTE_API_KEY, then:
from clearcote import launch
browser = launch(cloud=True, fingerprint="acct-1", country="de")
page = browser.contexts[0].new_page()
page.goto("https://example.com")
print(page.title())
browser.close() # ends the cloud sessionBehind the call, the SDK creates a session with POST /api/v1/browsers, connects to it over CDP and sets up humanize the way a local launch does. Open pages in browser.contexts[0]: it carries the session's profile. browser.new_page() works too, but opens a separate context without the profile's cookies (the SDK still turns off Playwright's emulated viewport for it, as it does locally).
The hosted session behind the browser is on browser.cloud_session in Python and cloudSessionOf(browser) in Node (also browser.cloudSession): the create answer with its id, worker and expiresAt, minus the single-use connectUrl. Use the id with the Cloud client for hand-off, recordings and events.
Switch with environment variables
Leave cloud out of the call and the environment decides, so one script runs locally on your laptop and in the cloud in production without a code change.
| Variable | Meaning |
|---|---|
CLEARCOTE_CLOUD | 1, true or yes: a launch() without a cloud argument starts a cloud browser. Anything else, or unset: a local one. |
CLEARCOTE_API_KEY | Your cc_live_… key. Needed for every cloud call. |
CLEARCOTE_API_URL | The API to talk to. Default https://www.clearcotelabs.com; change it only for a development control plane. It must be https://; plain http:// is only accepted for this machine (localhost, 127.0.0.1, ::1), so the key never crosses a network unencrypted. |
export CLEARCOTE_API_KEY=cc_live_...
python scrape.py # a local browser
CLEARCOTE_CLOUD=1 python scrape.py # the same script on a hosted browserAn explicit argument always wins: cloud=False / cloud: false launches locally whatever CLEARCOTE_CLOUD says.
Options
In cloud mode, launch() takes the options of POST /api/v1/browsers. Node uses the API's names as they are; Python uses snake_case, and the SDK renames them. The full rules for each option are in the hosted browsers reference.
| Python | Node and API | Meaning |
|---|---|---|
identity | identity | One label per account: the same device and the same residential IP while that IP stays online. |
fingerprint | fingerprint | Device seed. The same seed gives the same device profile. |
light_stealth | lightStealth | Default true. |
platform | platform | windows, macos, linux or android. |
brand | brand | Chrome, Edge, Opera or Vivaldi. |
timezone | timezone | IANA name, for example Europe/Berlin. |
locale, accept_language | locale | Accept-Language, for example de-DE,de. accept_language (Node: acceptLanguage) is an alias. |
geoip | geoip | Timezone and language follow the exit IP. |
proxy | proxy | "managed" (the default), { server, username, password }, or a URL such as http://user:pass@host:8080, which the SDK splits into that object for you. |
country | country | Exit country in the managed residential pool. |
state | state | Exit state or region in the managed pool. Needs country. |
city | city | Exit city in the managed pool. Needs state. |
proxy_session | proxySession | Sticky label: the same label gets the same exit IP back later. |
timeout_sec | timeoutSec | Hard limit on the session length, in seconds. |
idle_timeout_sec | idleTimeoutSec | End the session after this long without a CDP command. |
max_gb | maxGb | Stop the session after this much traffic. |
headless | headless | Default true. |
keep_alive | keepAlive | Keep the browser running after you disconnect. |
version | version | Run a specific Clearcote release. |
profile | profile | A cloud profile: "name" to load it, { name, persist: true } to also save it back. |
url | url | Opened in the first tab before you get the browser. |
adblock | adblock | Refuse known ad and tracker hosts before they load. |
record | record | Record an MP4 of the session. |
note | note | Your label for the session. |
worker | worker | Start on the same server as an earlier session. |
humanize | humanize | Human mouse and keyboard, applied by the SDK on your side. Never sent to the API. |
show_cursor | showCursor | Show a red dot that follows the mouse, for watching or debugging. SDK side, like humanize. |
api_key, api_url | apiKey, apiUrl | The account and API server for this launch. Default CLEARCOTE_API_KEY and CLEARCOTE_API_URL. Never sent as options. |
timeout, slow_mo | timeout, slowMo | Playwright's own options for the CDP connection, in milliseconds. Not sent to the API. |
What stays the same, what differs
The same:
- You get a Playwright object.
launch()returns aBrowserandlaunch_persistent_context()aBrowserContext, sync or async, exactly as locally. - Humanize runs on your side either way. The SDK installs the same curved mouse paths and key-by-key typing on the cloud browser as on a local one, so
humanizebehaves identically. It is never sent to the API. - The same engine. Hosted sessions run the same Clearcote releases the SDK downloads, so the persona options mean the same thing in both places, and
versionpins the same build.
Different:
- Local-only options are refused.
executable_path,args,user_data_dir,extensions,ignore_default_argsand the other options that only make sense for a binary on your machine raiseValueError("<name> is not available for cloud browsers")in Python (an error with the same message in Node) rather than being ignored. - Profiles instead of folders. A cloud browser has no folder on your disk. To keep cookies and site storage between runs, use a named cloud profile (below).
profilemeans a cloud profile. Locally,profilepicks a captured device profile ("auto"). In cloud mode it names a saved cloud profile: cookies, localStorage and IndexedDB.- Hosted defaults. A cloud session starts from the hosted defaults:
lightStealthon, a seed, timezone and language that follow the exit IP, and a residential exit IP unless you pass your ownproxy. - Closing.
browser.close()disconnects, and the session ends when its client goes; the SDK also stops it through the API to be sure. Withkeep_alive/keepAlivethe browser keeps running instead, until you stop it or a limit does. - Hosted limits. No
file://, no extensions, no private networks, downloads stay on the server, andpage.on("console")stays silent. See the limits. - Billing. A cloud browser is billed like any hosted session, per GB of traffic from your prepaid balance. A local browser costs nothing beyond your licence.
A persistent context on a cloud profile
launch_persistent_context() with cloud=True takes a profile name instead of a folder. It returns the first context of a cloud session started with profile: { name, persist: true }: the session loads the profile, and closing the context ends the session and saves cookies and site storage back to it.
from clearcote import launch_persistent_context
ctx = launch_persistent_context(cloud=True, profile="shop-account", country="de")
page = ctx.pages[0] if ctx.pages else ctx.new_page()
page.goto("https://shop.example.com/account")
ctx.close() # ends the session and saves "shop-account"Passing a user_data_dir together with cloud=True raises a ValueError that points you at profile=. Only one session at a time can save to a profile. To start a profile from cookies you already have, use cookie sync.
The Cloud client
Everything else the API does has a client: sessions, agent runs, profiles and webhooks. It reads CLEARCOTE_API_KEY and CLEARCOTE_API_URL, or takes them as arguments. The Python client uses only the standard library; AsyncCloud has the same methods for asyncio.
from clearcote.cloud import Cloud, CloudError
cloud = Cloud() # or Cloud(api_key="cc_live_...", base_url="https://www.clearcotelabs.com")
session = cloud.browsers.create(country="de", record=True)
print(session["id"], session["connectUrl"])
try:
cloud.browsers.stop(session["id"])
except CloudError as e:
print(e.status, e.code, e.message)| Namespace | Methods |
|---|---|
browsers | create(**options), get(id), list(status=None, note=None, limit=None, before=None), stop(id), live(id, control=False), share(id, control=None, minutes=None, recording=None), handoff(id, reason=None, timeout_sec=None), handoff_done(id), wait_handoff(id, timeout=None, poll=2.0), events(id, after=0, limit=None), recording_url(id), download_recording(id, path) |
runs | create(task, url=None, schema=None, secrets=None, wait=True, timeout=None, poll=1.5, on_update=None, **options), get(id), list(limit=None, before=None), cancel(id), wait(id, timeout=None, poll=1.5, on_update=None) |
profiles | list(), get(name), delete(name), import_cookies(name, cookies, mode="merge"), sync(name, …) |
webhooks | create(url, events=None, description=None), list(), delete(id), test(id) |
Every method returns the endpoint's JSON as it is: a dict in Python, a plain object in Node. List calls return the API's object, for example { balanceEur, sessions } or { runs }, not a bare list. Durations the SDK waits itself (timeout, poll) are in seconds in both languages.
Node has the same methods in camelCase (handoffDone, waitHandoff, importCookies, recordingUrl, downloadRecording), with the optional arguments in one object: live(id, { control }), events(id, { after, limit }), runs.create(task, { url, schema, country, … }), importCookies(name, cookies, { mode }), webhooks.create(url, { events }). A failed call raises CloudError with the HTTP status (0 when the API could not be reached), the API's code and its message; the codes are listed under Errors. A wait that runs past its timeout raises CloudTimeoutError instead, whose last holds the last state the SDK read; the run or hand-off carries on on the server. A bad option raises ValueError in Python (an Error in Node) before anything is sent. The SDK also exports verify_webhook / verifyWebhook for webhook signatures.
The clearcote cloud command
The clearcote command that comes with the SDK (pip install clearcote, or npx clearcote) has a cloud group for the same API:
# an agent run: prints the result when it finishes
clearcote cloud run "Find the price of the cheapest plan" --url https://example.com/pricing \
[--schema schema.json] [--secret name=value]... [--secret-domain name=host]... [--handoff] [--record] [--json]
# sessions
clearcote cloud sessions
clearcote cloud stop <id>
clearcote cloud events <id>
clearcote cloud recording <id> -o session.mp4
# copy cookies into a cloud profile (--domain or --all-domains is required)
clearcote cloud profile sync <name> (--from-profile DIR | --from-cdp URL | --from-file state.json | --login URL) \
(--domain example.com... | --all-domains) [--replace]
# webhooks
clearcote cloud webhooks add https://example.com/hooks/clearcote [--event run.finished]...
clearcote cloud webhooks list
clearcote cloud webhooks rm <id>
clearcote cloud webhooks test <id>Every command takes --json to print the API's answer instead of a summary. run waits for the result and exits with 0 only when the run succeeded (1 otherwise, 2 for a usage error), so it works in a shell script. recording saves to <id>.mp4 without -o. Run clearcote cloud --help for every flag.
Each command is covered on its own page: runs, recordings and events, profile sync and webhooks.
MCP: a cloud browser for your AI assistant
The MCP server has the same switch. With CLEARCOTE_CLOUD=1 and CLEARCOTE_API_KEY in its env, the shared browser its tools drive is a cloud browser. With an API key set, it also gets a run_task tool that hands a whole task to an agent run and returns the result JSON.
{
"mcpServers": {
"clearcote": {
"command": "npx",
"args": ["-y", "clearcote-mcp"],
"env": { "CLEARCOTE_CLOUD": "1", "CLEARCOTE_API_KEY": "cc_live_..." }
}
}
}