undetected-chromedriver: setup, errors and hCaptcha.

undetected-chromedriver is a Python package that makes a Selenium-driven Chrome look less automated. How to install it, fix the version-mismatch and distutils errors, use a proxy, and what to do when a site shows hCaptcha.

What undetected-chromedriver is

Selenium drives Chrome through ChromeDriver, and a Chrome started that way reports itself: navigator.webdriver reads true, and ChromeDriver adds variables of its own to the page that a script can look for. undetected-chromedriver downloads a ChromeDriver, patches it, and starts Chrome so those values read like an ordinary browser’s. Its README describes it as an “Optimized Selenium Chromedriver patch which does not trigger anti-bot services like Distill Network / Imperva / DataDome / Botprotect.io” that “Automatically downloads the driver binary and patches it.”

undetected-chromedriver at a glance (read from PyPI and GitHub on 2 November 2026)
PackageMaintainerLatest versionLicence and requirements
undetected-chromedriver (PyPI), imported as undetected_chromedriverUltrafunkAmsterdam, ultrafunkamsterdam on GitHub3.5.5, released 17 February 2024GPL-3.0. Python only. Needs Selenium 4.9 or newer, requests and websockets, plus a Chrome or Chromium you install yourself

uc.Chrome is a subclass of Selenium’s Chrome driver, so find_element, execute_script and the rest of the Selenium API work unchanged.

Is it still maintained?

Not actively. Version 3.5.5 on PyPI, released on 17 February 2024, is still the latest, and it is what pip install undetected-chromedriver gives you. The GitHub repository’s last commit, on 5 July 2025, is a Python 3.13 compatibility fix that has not been released to PyPI. The issue tracker is turned off; the README sends questions to GitHub Discussions.

The author’s newer project is nodriver, which its PyPI page calls “the official successor of the Undetected-Chromedriver python package”. It drops Selenium and ChromeDriver altogether and has a different, async API, so moving to it means rewriting your script, not changing an import. Other developers have also published forks of undetected-chromedriver; those are not the original author’s. The nodriver guide covers installing nodriver, proxies and its own fork, zendriver.

Install it

Shell · pip
pip install undetected-chromedriver
pip install setuptools   # Python 3.12 and newer, see below

The package does not bring a browser: it finds the Chrome already installed on your machine, or the one you point it to with browser_executable_path. On first run it downloads a ChromeDriver into a folder of its own (~/.local/share/undetected_chromedriver on Linux, ~/Library/Application Support/undetected_chromedriver on macOS, %APPDATA%\undetected_chromedriver on Windows).

“No module named ‘distutils’”

On Python 3.12 and newer, a fresh install of 3.5.5 fails on import:

Python 3.12+ · error
File ".../undetected_chromedriver/patcher.py", line 4, in <module>
    from distutils.version import LooseVersion
ModuleNotFoundError: No module named 'distutils'

Python 3.12 removed distutils, and the released version still imports it. Installing setuptools into the same environment puts it back; we reproduced the error on Python 3.12 and 3.13 and the import worked after pip install setuptools. The GitHub version no longer needs it, but it is not on PyPI.

Quick start with Selenium

Python · undetected-chromedriver 3.5.5
import undetected_chromedriver as uc

driver = uc.Chrome(headless=True)   # uc.Chrome() opens a visible window
driver.get("https://your-site.example/")
print(driver.execute_script("return navigator.webdriver"))   # False
print(driver.title)
driver.quit()

We ran this on 2 November 2026 with undetected-chromedriver 3.5.5, Selenium 4.52.0 and Chrome 156, headless. Plain Selenium’s webdriver.Chrome() read navigator.webdriver as true; uc.Chrome() read it as false.

Options go through uc.ChromeOptions(), which takes the same arguments as Selenium’s. Two settings of its own are worth knowing: a profile folder that survives the run, and the Chrome version to fetch a driver for:

Python · profile and version
import undetected_chromedriver as uc

driver = uc.Chrome(
    user_data_dir="/path/to/profile",   # created if missing, kept after quit
    version_main=156,                   # fetch the driver for Chrome 156
)

Headless mode

The README calls headless “unsupported officially”, and the headless argument’s own docstring warns that it “lowers undetectability and not fully supported”. In our run, uc.Chrome(headless=True) fixed navigator.webdriver but left HeadlessChrome in the user agent. Passing a normal user agent yourself removed it:

Python · headless with your own user agent
import undetected_chromedriver as uc

ua = (
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
    "(KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36"
)

options = uc.ChromeOptions()
options.add_argument(f"--user-agent={ua}")
driver = uc.Chrome(options=options, headless=True)

Fix the Chrome version mismatch

The project’s Discussions hold 15 threads with this message:

SessionNotCreatedException
selenium.common.exceptions.SessionNotCreatedException: Message: session not created:
cannot connect to chrome at 127.0.0.1:62047
from session not created: This version of ChromeDriver only supports Chrome version 157
Current browser version is 156.0.8078.79

Unless you tell it otherwise, 3.5.5 downloads the ChromeDriver for the newest stable Chrome, not the Chrome you have installed. Whenever your browser is behind the newest stable release, because an update has not been applied yet or you keep an older Chrome, the session fails. We reproduced the message above by asking for a driver one version ahead of the installed Chrome 156. Two fixes:

  • Update Chrome and restart it, so it matches the newest driver.
  • Pin the driver to your browser with version_main:
Python · version_main
import undetected_chromedriver as uc

# Your Chrome's major version: chrome://version, or
# google-chrome --version / "Google Chrome.app/Contents/MacOS/Google Chrome" --version
driver = uc.Chrome(version_main=156)

version_main is the major version only. In our test, version_main=156 fetched ChromeDriver 156.0.8078.80 for Chrome 156.0.8078.79 and started cleanly. To keep the package itself from moving under you, pin it in requirements.txt or at install time:

Shell · pin 3.5.5
pip install "undetected-chromedriver==3.5.5" setuptools

Use a proxy

The README is blunt about IP addresses: “THIS PACKAGE DOES NOT, and i repeat DOES NOT hide your IP address, so when running from a datacenter (even smaller ones), chances are large you will not pass!” A proxy is set with Chrome’s own flag:

Python · proxy
import undetected_chromedriver as uc

options = uc.ChromeOptions()
options.add_argument("--proxy-server=http://203.0.113.10:8080")   # or socks5://host:port

driver = uc.Chrome(options=options)
driver.get("https://your-site.example/")

We checked this against a local test proxy: every page request went through it. The flag takes a scheme, host and port. Putting user:pass@ in it does not work: in our test Chrome never reached the proxy and showed “This site can’t be reached”. For a proxy that needs a login, allow your IP address in the provider’s dashboard, or run a local proxy without a login that forwards to it.

Is there an undetected-chromedriver for C# or Java?

No, not from its author. undetected-chromedriver is Python only, and so is nodriver. Other people have ported the idea:

  • C#: the NuGet package Selenium.UndetectedChromeDriver by fysh711426 calls itself a “C# implementation of undetected_chromedriver”. It was the most downloaded result in a NuGet search when we looked.
  • Java: we found only small community ports on GitHub, the largest with 22 stars when we looked.

Ports are separate code with their own maintainers, so read what they patch before you rely on them. For hCaptcha in .NET, the C# guide works with plain Selenium WebDriver.

What it does when a site shows hCaptcha

The package’s PyPI summary says it is “Not triggered by CloudFlare/Imperva/hCaptcha and such”, followed by “NOTE: results may vary due to many factors. No guarantees are given, except for ongoing efforts in understanding detection algorithms.” Fewer automation tells are one of several things that decide whether hCaptcha shows a challenge at all; why am I getting hCaptcha covers the others, starting with the reputation of your IP address.

Once a challenge does appear, undetected-chromedriver has nothing more to offer. The site accepts the form only with a valid token in the h-captcha-response field, a P1_ string its server checks with hCaptcha before it lets the request through. Invisible hCaptcha needs that token too, even when no puzzle is shown. A patched driver does not produce one. When we opened hCaptcha’s own demo page with uc.Chrome(), the sitekey and the empty h-captcha-response field were there to read; filling that field is the part the package leaves to you.

Where NoneCap fits

NoneCap is an hCaptcha solving API. Keep undetected-chromedriver for the rest of your Selenium run, and when a page has hCaptcha, send its sitekey and url to NoneCap, get a P1_ token back, put it in the form and submit. With the Python SDK (pip install nonecap), nc.solve() waits for the result, so there is no polling loop:

Python · get an hCaptcha token
import os

import undetected_chromedriver as uc
from nonecap import NoneCap
from selenium.webdriver.common.by import By

nc = NoneCap(api_key=os.environ["NONECAP_KEY"])

driver = uc.Chrome()
driver.get("https://your-site.example/signup")

sitekey = driver.find_element(By.CSS_SELECTOR, "[data-sitekey]").get_attribute("data-sitekey")
solve = nc.solve(
    type="hcaptcha",
    sitekey=sitekey,
    url=driver.current_url,
    user_agent=driver.execute_script("return navigator.userAgent"),
)

Then put the token in the form, call the widget’s callback if the page set one, and submit:

Python · fill the token, submit
driver.execute_script("""
    const token = arguments[0];
    for (const name of ["h-captcha-response", "g-recaptcha-response"]) {
        const el = document.querySelector(`textarea[name="${name}"]`);
        if (el) el.value = token;
    }
    const cb = document.querySelector("[data-callback]")?.getAttribute("data-callback");
    if (cb && typeof window[cb] === "function") window[cb](token);
""", solve.token)

driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
driver.quit()

Sending the browser’s own user_agent is optional; some sites need it for the token to be accepted. If your Chrome goes through a proxy, you can pass the same one as proxy (http://user:pass@host:port) so the token and the form come from the same IP address. Failed, cancelled and expired solves are never charged.

The Selenium guide covers sitekeys that are not in the DOM, invisible and enterprise (rqdata) sitekeys, and the error types the SDK raises. Every field and status is in the API reference. NoneCap solves hCaptcha only; it does not help with reCAPTCHA, Turnstile or other CAPTCHAs.

Solve hCaptcha in Selenium →

Last updated November 2026.

Frequently asked

What is undetected-chromedriver?
A Python package that wraps Selenium’s Chrome driver. It downloads a ChromeDriver, patches it, and starts Chrome so that the usual automation tells, such as navigator.webdriver, read like an ordinary browser’s. You use it in place of webdriver.Chrome(); everything else is normal Selenium. It is written by UltrafunkAmsterdam and licensed GPL-3.0.
How do I install undetected-chromedriver?
pip install undetected-chromedriver, plus pip install setuptools on Python 3.12 or newer, because version 3.5.5 still imports distutils, which Python 3.12 removed. Chrome itself is not included: install Chrome or Chromium first.
Is undetected-chromedriver still maintained?
Not actively. The latest PyPI release is 3.5.5 from 17 February 2024. The last commit to the GitHub repository, a Python 3.13 compatibility fix, is from 5 July 2025 and has not been released to PyPI. The issue tracker is closed, and questions go to GitHub Discussions. The same author now works on nodriver, which its PyPI page calls “the official successor of the Undetected-Chromedriver python package”.
How do I fix “This version of ChromeDriver only supports Chrome version”?
By default, undetected-chromedriver 3.5.5 downloads the ChromeDriver for the newest stable Chrome, not the Chrome on your machine. When your browser is a version behind, the session fails with that error. Update Chrome, or pass your browser’s major version: uc.Chrome(version_main=156).
How do I use a proxy with undetected-chromedriver?
Add Chrome’s own flag to the options: options.add_argument("--proxy-server=http://host:port"). The flag has no place for a username and password; in our test, a proxy URL with user:pass@ in it never reached the proxy at all. For a proxy that needs a login, allow your IP address in the provider’s dashboard, or run a local proxy without a login that forwards to it.
Is there an undetected-chromedriver for C# or Java?
Not from the original author: undetected-chromedriver is a Python package. Other people have ported it. For C#, the NuGet package Selenium.UndetectedChromeDriver by fysh711426 describes itself as a “C# implementation of undetected_chromedriver”. For Java we found only small community ports on GitHub. Check who maintains a port before you depend on it.
Does undetected-chromedriver bypass hCaptcha?
It does not solve it. The PyPI summary says it is “Not triggered by CloudFlare/Imperva/hCaptcha and such” and adds “No guarantees are given”. Fewer automation tells can change how often a site shows a challenge, but when hCaptcha does appear, the form still needs a valid token in h-captcha-response, and a patched driver does not produce one. NoneCap returns that token for hCaptcha.
Is it allowed to use undetected-chromedriver and a solver on a site?
That depends on the site and on what you do there. Automate where you are allowed to: your own sites, your tests, and services whose terms permit it. NoneCap’s terms rule out fraud, credential stuffing, spam and unauthorised access. This is not legal advice.

Start solving hCaptcha in minutes.

1,300 free credits on signup. Pay per solve, credits never expire, failed solves never charged.