XBrowser
Stealth Chromium that passes every bot detection test.
Drop-in Playwright/Puppeteer replacement. Same API, same code — just swap the import.
- 73 source-level C++ patches — canvas, WebGL, audio, fonts, GPU, screen, WebRTC, network timing, automation signals
- Passes Cloudflare Turnstile, FingerprintJS, BrowserScan — tested against 30+ detection sites
humanize=True— human-like mouse curves, keyboard timing, scroll patternspip install xbrowser— binary auto-downloads, zero config
Install
Python:
pip install xbrowser
JavaScript / Node.js:
# With Playwright
npm install xbrowser playwright-core
# With Puppeteer
npm install xbrowser puppeteer-core
Optional — auto-detect timezone/locale from proxy IP:
pip install 'xbrowser[geoip]'
Migrating from Playwright?
- from playwright.sync_api import sync_playwright
- browser = sync_playwright().start().chromium.launch()
+ from xbrowser import launch
+ browser = launch()
page = browser.new_page()
page.goto("https://example.com")
# rest of your code works unchanged
Quick Start
Python:
from xbrowser import launch
browser = launch()
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close()
JavaScript:
import { launch } from 'xbrowser';
const browser = await launch();
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
await browser.close();
For sites with anti-bot protection:
browser = launch(
proxy="http://user:pass@residential-proxy:port",
geoip=True, # match timezone + locale to proxy IP
headless=False, # some sites detect headless
humanize=True, # human-like mouse, keyboard, scroll
)
Environment Variables
export XBROWSER_LICENSE_KEY=cb_yourkey
export XBROWSER_BINARY_PATH=~/.xbrowser/chromium-151.0.7922.108.4-pro/chrome
export XBROWSER_AUTO_UPDATE=false
| Variable | Default | Description |
|---|---|---|
XBROWSER_LICENSE_KEY |
— | License key (cb_...) |
XBROWSER_BINARY_PATH |
— | Use a local binary, skip download |
XBROWSER_CACHE_DIR |
~/.xbrowser |
Binary cache directory |
XBROWSER_AUTO_UPDATE |
true |
Set false to disable background updates |
XBROWSER_VERSION |
— | Pin exact Chromium version (e.g. 151.0.7922.108.4) |
XBROWSER_RELEASE_CHANNEL |
stable |
Set preview to opt into preview builds |
XBROWSER_GEOIP_TIMEOUT_SECONDS |
5 |
Max seconds for GeoIP resolution |
XBROWSER_WIDEVINE |
1 |
Set 0 to disable Widevine hint-file seeding |
XBROWSER_WIDEVINE_CDM |
— | Path to a sideloaded WidevineCdm directory |
API
launch()
from xbrowser import launch
# Basic — headless, default stealth config
browser = launch()
# Headed mode
browser = launch(headless=False)
# With proxy (HTTP or SOCKS5)
browser = launch(proxy="http://user:pass@proxy:8080")
browser = launch(proxy="socks5://user:pass@proxy:1080")
# Auto-detect timezone/locale from proxy IP
browser = launch(proxy="http://proxy:8080", geoip=True)
# Human-like mouse, keyboard, scroll
browser = launch(humanize=True)
# Slower, more deliberate movements
browser = launch(humanize=True, human_preset="careful")
# Fixed fingerprint seed — consistent identity across sessions
browser = launch(args=["--fingerprint=12345"])
# Disable default stealth args — full control
browser = launch(stealth_args=False, args=["--fingerprint=12345", "--fingerprint-platform=windows"])
# Full anti-detect setup
browser = launch(
headless=False,
proxy="http://user:pass@residential-proxy:port",
geoip=True,
humanize=True,
args=["--fingerprint=12345"],
)
Returns a standard Playwright Browser object.
launch_async()
import asyncio
from xbrowser import launch_async
async def main():
browser = await launch_async()
page = await browser.new_page()
await page.goto("https://example.com")
print(await page.title())
await browser.close()
asyncio.run(main())
launch_context()
Browser + context in one call. Sets locale and timezone via binary flags — no detectable CDP emulation:
from xbrowser import launch_context
ctx = launch_context(
user_agent="Mozilla/5.0 ...",
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone="America/New_York",
)
page = ctx.new_page()
page.goto("https://example.com")
ctx.close()
# Restore a saved session
ctx = launch_context(storage_state="state.json")
page = ctx.new_page()
ctx.storage_state(path="state.json")
ctx.close()
Async version: launch_context_async()
launch_persistent_context()
Persistent profile — cookies, localStorage, cache survive across sessions. Bypasses incognito detection.
from xbrowser import launch_persistent_context
# First run — creates profile
ctx = launch_persistent_context("./my-profile", headless=False)
page = ctx.new_page()
page.goto("https://example.com")
ctx.close()
# Next run — restored automatically
ctx = launch_persistent_context("./my-profile")
# Load Chrome extensions
ctx = launch_persistent_context(
"./my-profile",
headless=False,
extension_paths=["./my-extension"],
)
# Bypass storage quota incognito detection
ctx = launch_persistent_context("./my-profile", args=["--fingerprint-storage-quota=5000"])
Async version: launch_persistent_context_async()
JavaScript (Playwright)
import { launch, launchContext, launchPersistentContext } from 'xbrowser';
// Basic
const browser = await launch();
// Full options
const browser = await launch({
headless: false,
proxy: 'http://user:pass@proxy:8080',
geoip: true,
humanize: true,
args: ['--fingerprint=12345'],
timezone: 'America/New_York',
locale: 'en-US',
});
// Browser + context
const ctx = await launchContext({
userAgent: 'Custom UA',
viewport: { width: 1920, height: 1080 },
locale: 'en-US',
timezone: 'America/New_York',
});
// Persistent profile
const ctx = await launchPersistentContext({
userDataDir: './my-profile',
headless: false,
proxy: 'http://user:pass@proxy:8080',
});
JavaScript (Puppeteer)
Playwright is recommended for reCAPTCHA Enterprise sites. Puppeteer's CDP protocol leaks automation signals.
import { launch } from 'xbrowser/puppeteer';
const browser = await launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
Utility Functions
from xbrowser import binary_info, clear_cache, ensure_binary
# Check binary installation
print(binary_info())
# Pre-download binary (e.g. Docker build)
ensure_binary()
# Force re-download
clear_cache()
import { binaryInfo, ensureBinary, clearCache } from 'xbrowser';
await ensureBinary();
console.log(binaryInfo());
clearCache();
CLI
python -m xbrowser info # diagnostics: binary, license, env
python -m xbrowser install # download binary
python -m xbrowser update # check + download newer binary
python -m xbrowser clear-cache # remove cached binaries
python -m xbrowser login # save license key
python -m xbrowser logout # remove saved key
Human Behavior
Pass humanize=True — all Playwright calls automatically use human-like behavior:
browser = launch(humanize=True)
page = browser.new_page()
page.locator("#email").fill("user@example.com") # per-character, thinking pauses
page.locator("button[type=submit]").click() # Bézier curve, realistic aim
| Interaction | Default | humanize=True |
|---|---|---|
| Mouse movement | Instant teleport | Bézier curve + overshoot |
| Clicks | Instant | Realistic aim + hold duration |
| Keyboard | Instant fill | Per-character, pauses, typos + correction |
| Scroll | Jump | Accelerate → cruise → decelerate |
Presets:
browser = launch(humanize=True, human_preset="careful") # slower
Custom config:
browser = launch(humanize=True, human_config={
"mistype_chance": 0.05,
"typing_delay": 100,
"idle_between_actions": True,
"idle_between_duration": [0.3, 0.8],
})
Fingerprint Flags
The binary is stealthy by default — no flags needed. Customize via args=:
| Flag | Controls |
|---|---|
--fingerprint=<seed> |
Deterministic identity — same seed = same fingerprint |
--fingerprint-platform=windows |
Spoof as Windows (default on Linux) |
--fingerprint-gpu-vendor |
WebGL vendor string |
--fingerprint-gpu-renderer |
WebGL renderer string |
--fingerprint-hardware-concurrency |
navigator.hardwareConcurrency |
--fingerprint-device-memory |
navigator.deviceMemory in GB |
--fingerprint-screen-width/height |
Screen dimensions |
--fingerprint-timezone |
Timezone (e.g. America/New_York) |
--fingerprint-locale |
Locale (e.g. en-US) |
--fingerprint-noise=false |
Disable noise injection |
--fingerprint-webrtc-ip=auto |
Spoof WebRTC IP from proxy exit |
--fingerprint-storage-quota=5000 |
Override storage quota in MB |
--fingerprint-windows-font-metrics |
Align font metrics with Windows (148+) |
# Fixed seed for returning-visitor identity
browser = launch(args=["--fingerprint=12345"])
# Full manual control
browser = launch(stealth_args=False, args=[
"--fingerprint=12345",
"--fingerprint-platform=windows",
"--fingerprint-gpu-vendor=Intel Inc.",
])
Troubleshooting
Site detects incognito
Use launch_persistent_context() — runs with a real user profile:
ctx = launch_persistent_context("./my-profile", headless=False)
reCAPTCHA v3 scores low
Avoid page.wait_for_timeout() — sends CDP commands reCAPTCHA detects:
# Bad
page.wait_for_timeout(3000)
# Good
import time; time.sleep(3)
Other tips:
- Use residential proxies — datacenter IPs flagged by IP reputation
- Spend 15+ seconds on page before triggering reCAPTCHA
- Use
page.type()instead ofpage.fill() - Use fixed fingerprint seed
--fingerprint=12345
Still getting blocked (DataDome, Kasada)?
browser = launch(
proxy="http://your-residential-proxy:port",
geoip=True,
headless=False,
humanize=True,
)
Install font packages on Linux:
sudo apt install -y fonts-noto-color-emoji fonts-freefont-ttf \
fonts-ipafont-gothic fonts-wqy-zenhei
Platforms
| Platform | Status |
|---|---|
| Linux x86_64 | ✅ |
| Linux arm64 | ✅ |
| macOS arm64 (Apple Silicon) | ✅ |
| macOS x86_64 (Intel) | ✅ |
| Windows x86_64 | ✅ |
License
Wrapper code — MIT.
Metadata
Release files for xbrowser 0.5.14
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| xbrowser-0.5.14.tar.gz | 98.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xbrowser-0.5.14-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 213.3 kB
Release files / xbrowser-0.5.14.tar.gz
| Download URL | xbrowser-0.5.14.tar.gz |
|---|---|
| Size | 98.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ce97814c6f0e94d2b7b131b7ceb1adcdba27b5bf8370f23877b6094edcdb1a74
|
|
BLAKE2b-256 checksum How to use checksums |
d754095093711d88afcb889ef60496de3ffbb80ba37b2efef76a4301510e0db2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / xbrowser-0.5.14-py3-none-any.whl
| Download URL | xbrowser-0.5.14-py3-none-any.whl |
|---|---|
| Size | 114.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7e14f0d1a41a4c311ef4482173514ca2841f77487bee0731ad68e10ec2d4f91d
|
|
BLAKE2b-256 checksum How to use checksums |
86cef9d46ee0ae5f1f04df76dfcb77b75e402549dd7dcb01e63cad956a0ffef8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Security for xbrowser 0.5.14
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.