This is the Python wrapper for the high-performance Universal User-Agent Parser. It uses ctypes to interface with the core Go-based shared library.
⚠️ Not on PyPI. Do not runpip install ua-parserorpip install ua-parser-core—ua-parseron PyPI is an unrelated project and will install the wrong package (after whichfrom uaparser import UaParserfails). This parser ships only as a wheel attached to GitHub Releases; install it as shown below.
This package is distributed via GitHub Releases.
- Go to the Releases Page and note the latest version.
- Download the wheel — it is named
ua_parser_core-<VERSION>-py3-none-any.whl. - Install it using pip with the exact filename you downloaded (don't rely on a
*wildcard — it isn't expanded on Windows cmd/PowerShell):pip install ./ua_parser_core-0.0.51-py3-none-any.whl
Note: Install the released wheel, not a source checkout — only release wheels bundle the native driver.
pip install .on a clone produces a driver-less package andUaParser()then raisesFileNotFoundError. The release wheel automatically includes the native binaries for Windows, Linux, and macOS (amd64 and arm64). The distribution name isua-parser-core, but the import name isuaparser(i.e.from uaparser import UaParser).
The Python client does not currently work on Alpine Linux or other musl-based systems. Go shared libraries (-buildmode=c-shared) cannot be loaded by musl's dynamic linker due to a Go toolchain limitation (golang/go#54805); loading fails with an error like initial-exec TLS resolves to dynamic definition. On musl systems the client detects this and raises a RuntimeError with guidance.
Working alternatives:
- Run the ua-parser REST server container (
ghcr.io/octanium91/ua-parser) next to your application and call it over HTTP. - Use a glibc-based image instead (e.g.
python:3.12-slim). - Use the Node.js or Java clients, which fall back to WebAssembly automatically on musl.
Once the Go toolchain fix lands and the libraries are rebuilt, the bundled libua-parser-linux-{arch}-musl.so binaries will be picked up automatically on musl systems.
from uaparser import UaParser
# Initialize the parser
parser = UaParser()
# Initialize the core (starts updater if not disabled)
parser.init({"disable_auto_update": False, "lru_cache_size": 1000})
# Parse a User-Agent
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
headers = {
"Sec-CH-UA-Platform": '"Windows"',
"Sec-CH-UA-Platform-Version": '"13.0.0"'
}
result = parser.parse(ua, headers)
print(f"OS: {result['os']['name']} {result['os']['version']} ({result['os']['platform']})")
print(f"Browser: {result['browser']['name']} {result['browser']['version']}")
print(f"Device: {result['device']['vendor']} / {result['device']['form_factor']}")
print(f"CPU: {result['cpu']['architecture']} {result['cpu']['bitness']}, frozen UA: {result['is_frozen_ua']}")
if result.get("bot"):
print(f"Bot: {result['bot']['name']} ({result['bot']['category']}, {result['bot']['vendor']})")parse(ua, headers, signals) accepts a dict of browser-side evidence that UA and Client Hints can't provide (Safari/Firefox send no Client Hints):
result = parser.parse(ua, headers, signals={
"max_touch_points": 5, # unmasks iPads reporting a desktop (Mac) UA
"webgl_renderer": "Apple M2", # Apple Silicon / Android SoC
"screen": {"w": 1180, "h": 820, "dpr": 2},
})Priority inside the engine: Client Hints > signals > UA string.
The parse result is a plain dict, so every field is available directly —
including result_version, os.platform / os.version_name / os.version_raw,
device.form_factor, cpu.bitness, is_frozen_ua, the convenience flags
(is_mobile / is_desktop / is_touch_capable / is_chrome_family /
is_apple_silicon), automation, integrity, security, detection,
class_hash, bot ({name, category, vendor}, absent/None for humans) and
gpu. Full field semantics: root README.
For maximum accuracy don't enumerate headers — copy the User-Agent plus every request header starting with Sec-CH- (and X-Requested-With if present). Flask example (same idea for Django/FastAPI):
headers = {
k.lower(): v
for k, v in request.headers
if k.lower().startswith("sec-ch-") or k.lower() == "x-requested-with"
}
result = parser.parse(request.headers.get("User-Agent", ""), headers)Pass values raw, quotes included ('"Windows"'). See the backend forwarding guide and Requesting Client Hints (Accept-CH) in the root README.
The init() method accepts a dictionary:
disable_auto_update(bool): Master switch — if true, no background fetching (regexes or corrections).update_url(string): Custom URL for regex updates.update_interval(string): Update interval (e.g., "24h").lru_cache_size(int): Number of entries to keep in the LRU cache.corrections_url(string): Custom URL for the hot-updated correction layer (corrections.yaml).disable_corrections_update(bool): Suppress only correction updates while regex updates continue (the embedded snapshot still applies).