A stealth browser for Playwright & Puppeteer
Clearcote is Chromium, so the automation you already have keeps working. Swap the launch call for the SDK's launch() and you get back a Playwright handle that drives like a Browser (on a throwaway profile, so newContext() returns that same context), running a build whose identity controls are compiled into the engine instead of injected as JavaScript.
The open build needs no account. The latest build is free with GitHub for one browser at a time.
Change the launch line, keep everything else
# pip install clearcote
from clearcote import launch
browser = launch(fingerprint="account-42", light_stealth=True)
page = browser.new_page()
page.goto("https://example.com")
browser.close()Highlights
A one-line migration
launch() hands back a Playwright handle on a throwaway profile, so selectors and page objects stay as they are; its newContext() returns that same context, so pass ephemeralProfile: false where you need isolated contexts. Puppeteer attaches to serve()'s CDP endpoint with puppeteer.connect.
A verified binary, fetched for you
On first use the SDK downloads the matching build and checks its SHA-256: the open build against hashes baked into the SDK, the licensed build against the hash its download returns. Opt-in auto-update (open build only) checks the newest release against its published checksums, and their GPG signature when gpg is installed.
Identity as named options
fingerprint, platform, brand, timezone, acceptLanguage, proxy and geoip are arguments, not switches to remember. One seed keeps an identity stable across runs.
Coherent defaults
Headed launches skip the emulated viewport so the page size matches the real window, WebRTC keeps your machine's address private, and QUIC is off behind a proxy so no UDP goes around it.
Humanized input, one flag
humanize: true sends clicks, typing, drags and scrolls as trusted native input on curved paths, with navigator.webdriver left false.
Three languages
Python (sync and async), Node and .NET, on PyPI, npm and NuGet. .NET has geoip too, and humanized input as explicit HumanClickAsync-style calls; saved profiles, render-coherence checks, Widevine and the agent helpers are Python and Node for now.
What the SDK does for you
The package wraps the Clearcote binary so identity options become named arguments. It resolves the browser in a fixed order (an explicit path, the CLEARCOTE_BINARY environment variable, a version you asked for, the latest licensed build when it finds a licence key, then the open build pinned in the SDK), verifies and caches it, launches it through Playwright, and hands back the objects your code already uses.
Because the controls live in the C++ engine, there's no stealth plugin to load and nothing injected into the page. A plugin patches properties from JavaScript, and a patched getter can be told apart from a native one. Here the getter is the native one, in the main frame, in iframes and in workers. How detection works explains why that matters.
Two things behave differently from stock Chromium, both on purpose. Console and page-error events aren't forwarded to the automation client, because forwarding them is what an automation probe measures; collect them in the page and read them back with page.evaluate(). And on the licensed build (free with GitHub), a SOCKS5 proxy with a username and password works directly, with the credentials passed as separate username and password fields, because the engine implements the authentication stock Chromium lacks. The open build can't authenticate to a SOCKS5 proxy.
A stealth plugin next to an engine-level browser
Open-source stealth plugins are a reasonable first step, and many projects start there. See the comparison with puppeteer-extra-plugin-stealth for a fuller picture.
What people use it for
Scraping and data collection
Keep your existing Playwright or Puppeteer crawler and change what's underneath it.
End-to-end tests on real pages
Test flows on sites that treat stock automated Chromium differently from a normal browser.
Migrating an existing suite
Selectors and page objects stay put and only the launch call changes. Fixtures that open a fresh context per test need ephemeralProfile: false, because launch()'s newContext() returns its one profile context.
Fingerprinting research
Run the same scripts with and without a persona and compare what a page measures.
Clearcote is built for privacy, testing, research and lawful automation. You're responsible for how you use it and for following the terms of the sites you visit.
Which plan includes it
The SDK is in all three tiers, and so are nearly all identity options (the few that need the licensed build are marked in the fingerprint flags list). Without a key it runs the open build. With a licence key (free with a GitHub account) it runs the latest build, with the licensed-only extras on the pricing page.
Compare plansFAQ
Is it really a drop-in replacement?
Very nearly. The sync Python and Node launch() hand back a browser-like handle on a throwaway profile: newPage() and everything after it work as before, but newContext() returns that same context, so code that needs isolated contexts should pass ephemeralProfile: false or launch one browser per context. launchPersistentContext() returns a standard BrowserContext. Puppeteer attaches to serve()'s CDP endpoint with puppeteer.connect, and the .NET SDK returns standard Microsoft.Playwright objects (IBrowserContext from LaunchEphemeralProfileAsync, IBrowser from LaunchAsync).
Do I still need puppeteer-extra-plugin-stealth or playwright-stealth?
No, and it's better not to add one. Those plugins patch values from injected JavaScript, and the patching can itself be noticed. Clearcote sets the values in the engine, so there's nothing left for a plugin to patch.
Does it work headless?
Yes. Headed is still the safer default where you can run it, because headless mode has tells of its own. In containers, the official Docker image runs headed under a virtual display by default.
Which languages are supported?
Python (sync and async APIs), Node.js and .NET. All three have geoip matching and humanized input (a launch flag in Python and Node, explicit HumanClickAsync-style calls in .NET). Saved profiles, render-coherence checks, Widevine and the in-browser agent are Python and Node only for now.
How do I keep the same identity between runs?
Reuse the same fingerprint seed, and use launchPersistentContext with a user-data directory so cookies and storage persist. Deriving the seed from your own account or tenant id keeps it reproducible. See persistent identities.
Go deeper in the docs
Related features
One option gives Playwright's clicks, typing, drags and scrolls curved paths and human timing, still as trusted native input.
Canvas, WebGL, WebGPU, audio, fonts, screen and hardware derived in the C++ engine from one seed, with locale and timezone set to match, so every surface agrees with every other.
Set a proxy with geoip on and the timezone, languages and WebRTC follow the exit IP. SOCKS5 with a username and password works directly on the licensed build.
Run Clearcote as a standing CDP endpoint from the official Docker image, serve() or a CLI, and attach any framework with no code change.
Related reading
Try Clearcote
A drop-in for Playwright & Puppeteer, coherent down to the TLS handshake, with an open-source build to start from.
Free for one browser with GitHub. No card.