Switching to Clearcote
Clearcote is a real Chromium that speaks CDP, so existing code usually moves over by changing the lines that start or find the browser. The code after them stays the same.
From Playwright
launch() returns the same Playwright Browser, so only the launch call changes. With playwright-stealth or Patchright, the plugin goes too.
from playwright.sync_api import sync_playwright
browser = sync_playwright().start().chromium.launch()
from clearcote import launch
browser = launch(fingerprint="acct-1")
page = browser.new_page()Migrating from playwright-stealth or Patchright →
From puppeteer-extra-plugin-stealth
Keep Puppeteer and drop the plugin. serve() starts Clearcote with a CDP endpoint, and Puppeteer connects to it.
import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch();
import puppeteer from "puppeteer-core";
import { serve } from "clearcote";
const srv = await serve({ fingerprint: "acct-1" });
const browser = await puppeteer.connect({ browserURL: srv.cdpUrl, defaultViewport: null });
const page = await browser.newPage();Migrating from puppeteer-extra-plugin-stealth →
From undetected-chromedriver
This is the one move that is more than a line: Clearcote is driven over CDP, not WebDriver, so the script moves to Playwright. Most Selenium calls have a one-line counterpart. The same goes for selenium-stealth.
import undetected_chromedriver as uc
driver = uc.Chrome(headless=True)
driver.get("https://example.com")
from clearcote import launch
browser = launch(fingerprint="acct-1")
page = browser.new_page()
page.goto("https://example.com")Migrating from undetected-chromedriver →
From Camoufox
Both hand you a Playwright object from a patched engine, so Camoufox(...) becomes launch(...), mostly with the same option names, on Chromium.
from camoufox.sync_api import Camoufox
with Camoufox(os="windows", humanize=True) as browser:
page = browser.new_page()
from clearcote import launch
browser = launch(platform="windows", humanize=True, fingerprint="acct-1")
page = browser.new_page()From an anti-detect browser app
Multilogin, GoLogin, AdsPower, Dolphin Anty, Kameleo: a profile becomes a seed for the device and a folder for the cookies. There is no desktop app to keep running and no local API to call first.
ws = start_profile("k1abc23") # the app's local API hands out a CDP address
browser = sync_playwright().start().chromium.connect_over_cdp(ws)
page = browser.contexts[0].new_page()
from clearcote import launch_persistent_context
context = launch_persistent_context("profiles/acct-1", fingerprint="acct-1")
page = context.new_page()Migrating from an anti-detect browser app →
From Fortress (BSD releases)
Your client keeps connecting to a CDP endpoint; only what starts the endpoint changes. In Docker, swap the image for teamflatearth/clearcote and keep the port.
with Fortress() as f:
browser = p.chromium.connect_over_cdp(f.cdp_url)
with serve(fingerprint="acct-1", port=9222) as srv:
browser = p.chromium.connect_over_cdp(srv.cdp_url)From Browserless
Your code still connects to a WebSocket address. A Clearcote session gives you one per connection, or the SDK creates the session and connects in one call.
import { chromium } from "playwright-core";
const browser = await chromium.connectOverCDP("wss://production-sfo.browserless.io?token=" + TOKEN);
import { launch } from "clearcote";
const browser = await launch({ cloud: true, identity: "acct-1", country: "us" });
const page = await browser.contexts()[0].newPage();From Browserbase
Same shape: create a session, connect to its address. sessions.create() becomes POST /api/v1/browsers, or launch(cloud=True) does both.
session = Browserbase(api_key=BROWSERBASE_API_KEY).sessions.create()
browser = sync_playwright().start().chromium.connect_over_cdp(session.connect_url)
from clearcote import launch
browser = launch(cloud=True, identity="acct-1", country="us")
page = browser.contexts[0].new_page()What to delete along the way
Clearcote sets the fingerprint inside the engine, from one fingerprint seed. Whatever your old setup changed from the outside now works against it:
- stealth plugins and evasion scripts (
evaluateOnNewDocument,add_init_scriptpatches ofnavigator); - a hand-set user agent, viewport, platform, WebGL vendor or GPU string;
- timezone and language overrides when you use a proxy: pass
geoip=Trueand they follow the exit IP.
Give each account its own seed and keep it: the same seed is the same device every time. Without a licence key the SDK runs the open build (BSD-3, no account); a free key from a GitHub sign-in gets the latest build. See Recommended settings and Installation.