Examples
Copy-paste recipes for common Clearcote workflows. Start with the smallest one that matches your job, then add options only when you need them.
Recipes are shown for the Python, Node and .NET SDKs (pip install clearcote / npm install clearcote / dotnet add package Clearcote). The .NET SDK covers the core workflows — launch, persistent contexts, serve, proxy, geoip (Geoip = true) and canvas bridge, with humanized input as explicit HumanClickAsync / HumanTypeAsync calls rather than a launch flag; saved profiles, Widevine and the in-browser agent are Python & Node only for now.
Every recipe runs the open build unless a licence key is set; with one (clearcote login, CLEARCOTE_LICENSE_KEY or license_key=), the same code runs the latest licensed build. Free with GitHub runs one browser at a time. One thing that surprises people: the engine never forwards console or page-error events, so page.on("console") receives nothing by design — collect output in the page and read it back with page.evaluate().
1. Launch a verified browser from the SDK
The SDK downloads and SHA-256-verifies the browser on first use — the open build by default, the latest licensed build when a key is set. It returns ordinary Playwright objects, so the rest of your automation stays familiar. (Since 0.23, launch() in sync Python and Node runs on a throwaway profile and new_context() hands back that same profile; pass ephemeral_profile=False / ephemeralProfile: false if you need isolated contexts.)
from clearcote import launch
browser = launch(fingerprint="demo:user-1", platform="windows", headless=False)
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close()2. Serve a stealth CDP endpoint for any framework
serve() runs Clearcote as a standing CDP endpoint and returns a cdp_url. It launches the binary directly — no --enable-automation — and any Playwright, Puppeteer, browser-use, Crawl4AI, or Stagehand client attaches over CDP with no code change. Open pages in contexts[0] to use the served profile; new_page() on the browser creates a separate, isolated context. For an AI agent, point Claude / Cursor / Cline at the clearcote-mcp server (pip install clearcote-mcp, or npx -y clearcote-mcp, which needs Python 3.10+). From a shell, clearcote serve runs the same endpoint and can give each connection its own identity — see Deployment.
from clearcote import serve
from playwright.sync_api import sync_playwright
srv = serve(fingerprint="demo:user-1", platform="windows") # same persona options as launch()
print(srv.cdp_url) # http://127.0.0.1:<port>
browser = sync_playwright().start().chromium.connect_over_cdp(srv.cdp_url)
page = browser.contexts[0].new_page(); page.goto("https://example.com"); print(page.title())
srv.close()3. One stable identity per account
Use a deterministic seed and a persistent user-data directory. The seed keeps the browser identity stable; the profile directory keeps cookies, local storage, permissions, and session state.
from clearcote import launch_persistent_context
account_id = "acct_42"
ctx = launch_persistent_context(
rf"C:\clearcote\profiles\{account_id}",
fingerprint=f"acct:{account_id}",
platform="windows",
timezone="America/New_York",
accept_language="en-US,en",
humanize=True,
)
page = ctx.new_page()
page.goto("https://example.com/dashboard")
ctx.close()4. Match timezone, language, location and WebRTC to a proxy
When geoip is enabled, Clearcote finds the proxy's exit IP through the proxy and fills unset timezone, language, location and WebRTC address from a GeoIP database (downloaded on first use, about 50 MB). This avoids hand-matching a timezone to every proxy. If the region can't be resolved within CLEARCOTE_GEOIP_TIMEOUT_SECONDS (default 20), the launch stops with GeoipError rather than starting with this machine's clock and language; set both timezone and accept_language to launch anyway. In .NET, set Geoip = true.
from clearcote import launch
browser = launch(
fingerprint="proxy:nyc:001",
platform="windows",
proxy={"server": "http://host:8080", "username": "user", "password": "pass"},
geoip=True,
)
page = browser.new_page()
page.goto("https://browserleaks.com/webrtc")
browser.close()5. Save and reuse a named profile
A saved Profile is useful when you want a named persona that multiple scripts can share. Keep secrets out of source control: profile files are plaintext.
from clearcote import Profile, launch
Profile("support-agent", {
"fingerprint": "support-agent",
"platform": "windows",
"timezone": "America/Chicago",
"accept_language": "en-US,en",
"storage_quota": 120000,
}).save()
browser = launch(profile="support-agent", headless=False)
page = browser.new_page()
page.goto("https://example.com")
browser.close()6. Use the canvas bridge only where it matters
Bridge mode can be scoped by registrable domain. The example below bridges canvas/WebGL readbacks only on the listed origins and serves local rendering everywhere else. Set the GPU strings to the render GPU's (the renderer the bridge server prints), so the reported GPU matches the bridged pixels. Enabling the bridge runs the renderer without Chromium's sandbox (the SDK adds --no-sandbox for the whole browser, whatever the mode), so enable the bridge only in sessions that need it.
from clearcote import launch
browser = launch(
fingerprint="gpu:nvidia:seat-1",
gpu_vendor="Google Inc. (NVIDIA)", # as printed by the bridge server
gpu_renderer="ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 (0x00002504) Direct3D11 vs_5_0 ps_5_0, D3D11)",
canvas_bridge={
"url": "ws://127.0.0.1:8443",
"auth": "user:secret",
"mode": "allow",
"allow": ["example.com", "browserleaks.com"],
"fallback": "local",
},
)
page = browser.new_page()
page.goto("https://browserleaks.com/canvas")
browser.close()Start the bridge host first. See Canvas bridge for the server command, network guidance, and fallback behavior.
7. Play DRM video with Widevine
Clearcote ships the EME plumbing but never Google's proprietary CDM. widevine fetches the Widevine CDM once from Google's component server, verifies it, seeds it into the profile, and enables it — so requestMediaKeySystemAccess('com.widevine.alpha') resolves and DRM streams play, like a real Chrome.
from clearcote import launch_persistent_context
ctx = launch_persistent_context("C:\\clearcote\\profile-drm", widevine=True)
page = ctx.pages[0] if ctx.pages else ctx.new_page()
page.goto("https://example.com")
# requestMediaKeySystemAccess('com.widevine.alpha') now resolves; DRM playback works
ctx.close()Opt-in by design — the package never distributes Google's CDM; you trigger the one-time fetch (cached under ~/.clearcote/WidevineCdm). Works with launch() and launch_persistent_context() in sync Python; in Node use launchPersistentContext() as above (the TypeScript type of launch() doesn't declare widevine yet). Not with the Python async launch(), which is incognito, or in .NET. Software-secure (L3). A browser that presents as Google Chrome but can't answer the Widevine query is readable by any page, which is the reason to turn it on. See Widevine & DRM.
8. Prefetch the browser in CI
Warm the verified browser cache before your test suite starts. This makes failures happen early, before parallel jobs begin. Without a key this fetches the open build; with CLEARCOTE_LICENSE_KEY set it fetches the licensed one. A Free with GitHub key runs one browser at a time, so serialise browser tests or use Pro.
- name: Install dependencies
run: |
python -m pip install clearcote
- name: Prefetch verified Clearcote
run: |
clearcote install
clearcote info --quick
- name: Run tests
run: |
pytest9. Run an agent task and keep the trace
The in-browser agent is opt-in. Give it a persistent profile directory, an OpenAI-compatible endpoint/key, and a bounded step count so the run is reproducible and reviewable.
import os
from clearcote import launch_agent, run_agent_task
ctx = launch_agent(
os.path.expanduser("~/.clearcote/agent-demo"), # the profile directory
fingerprint="agent-demo",
agent_llm_key="sk-or-...",
agent_model="openai/gpt-4o-mini",
)
page = ctx.new_page()
page.goto("https://example.com")
result = run_agent_task(page, "Find the contact page and summarize the email address", max_steps=12)
print(result["success"])
print(result["finalText"])
print(result["stepsJson"])
ctx.close()10. Raw Playwright when you do not want the SDK
The SDK is the ergonomic path, but the open build is a normal Chromium binary you can launch directly from Playwright or Puppeteer. The licensed build needs the SDK — it holds the licence token the engine checks at startup. The extra arguments below are the defaults the SDK would otherwise add for you.
import { chromium } from "playwright";
const browser = await chromium.launch({
executablePath: "C:\\clearcote\\chrome.exe",
headless: false,
ignoreDefaultArgs: ["--enable-automation", "--enable-unsafe-swiftshader"], // the SDK strips these two by default
args: [
"--fingerprint=raw-playwright-demo",
"--fingerprint-platform=windows",
"--fingerprint-brand=chrome",
"--timezone=America/New_York",
"--accept-lang=en-US,en",
"--lang=en-US",
"--webrtc-ip-handling-policy=disable_non_proxied_udp",
"--ignore-gpu-blocklist", // the SDK pairs this with the SwiftShader strip so WebGL keeps working
],
});
const page = await browser.newPage();
await page.goto("https://example.com");
await browser.close();Keep the recipe small first. Add profile import, canvas bridge, agent mode, or manual GPU overrides only when your target workflow actually needs them.