uap-python parses user agent strings into structured data
Python implementation of ua-parser
At a glance
- What is it?
- uap-python is the official Python implementation of the ua-parser project, turning a user agent string into a browser, operating system and device result, with a pure Python mode and an optional regex accelerator. It is Apache-2.0 licensed, requires Python 3.10 or newer, and the 1.x releases are a rewrite with a different API from the 0.x line.
- Who is it for?
- uap-python suits a Python service that needs to segment traffic by browser, operating system and device, since it is the reference implementation of the ua-parser project and returns a typed result object rather than a string. It is the wrong tool if you need to keep up with bleeding-edge browsers, because the rules come from a shared cross-language regex set that is updated on its own schedule, and it is the wrong tool if you are on Python 3.9 or older.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 26 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the library returns
uap-python is the official Python implementation of the ua-parser project, a port of Browserscope's original user agent parser, and the description in the package metadata says exactly that.
The API is four functions, and the shape of the result is the thing to understand first. The full parse returns a Result with four fields: user_agent, os, device and string.
>>> from ua_parser import parse
>>> ua_string = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36'
>>> parse(ua_string) # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
Result(user_agent=UserAgent(family='Chrome',
major='41',
minor='0',
patch='2272',
patch_minor='104'),
os=OS(family='Mac OS X',
major='10',
minor='9',
patch='4',
patch_minor=None),
device=Device(family='Mac',
brand='Apple',
model='Mac'),
string='Mozilla/5.0 (Macintosh; Intel Mac OS...Each of the three sub-results is a named tuple with a family string and the version split into major, minor, patch and patch_minor. The family is the coarse device or browser name, and the version parts are strings rather than integers, because a user agent string can carry a non-numeric version component and coercing it would throw away information.
Missing data is represented as None rather than as an empty string, and the README is explicit about it:
>>> parse("")
Result(user_agent=None, os=None, device=None, string='')That distinction matters when you aggregate. An empty family and a None family mean different things when you are counting browser share, and only one of them is a value you can rely on.
The three specialised functions are parse_user_agent, parse_os and parse_device, each returning just its own sub-result or None. For a specific domain, a match failure just returns None.
The three parsers and the regex extra
The installation instructions are the place where the most important design decision is documented.
Add ua-parser[regex] to your project's dependencies, or run:
$ pip install 'ua-parser[regex]'to install in the current environment. And then the note, which is the clearest statement in the README: the [regex] feature is strongly recommended, the Pure Python (no feature) is significantly slower, especially on non-cpython runtimes, though it is the most memory efficient.
So there is a speed versus memory trade-off, and the recommendation is unambiguous. The regex implementation is faster; the pure Python one uses less memory. The gap widens on non-CPython runtimes, and the supported runtimes include recent pypy supporting 3.10 and GraalPy 25.
The reason is that the two modes differ in how they apply the matching rules. The pure Python path evaluates the rules with the standard library's own regular expression engine, which is available everywhere and has no native dependency, while the regex path uses a faster engine. The documentation points at a section on builtin resolvers for a fuller explanation of the tradeoffs between the different options.
The pyproject.toml shows the packaging shape. The base install has one dependency, ua-parser-builtins, and the optional extras include yaml = ["PyYaml"] for the YAML form of the rules and a re2 extra pointing at a Go implementation. The build system requires setuptools and PyYaml, and the version is 1.0.2.
The Python requirement is 3.10 or newer, and the classifiers list CPython from 3.10 through 3.14, plus PyPy and GraalPy, with the development status marked as Production/Stable.
What changed in the 1.0 rewrite
The Upgrading section of the README is short, and it points at a migration guide, which tells you that the API changed shape.
Upgrading from 0.x? See the upgrade guide.
That one line carries more weight than it looks. The 0.x series of uap-python was a direct port of the original uap-core regex set, and it exposed a module-level parser object with a single parse method and a request-level user_agent attribute on the result. The 1.0 line is a rewrite with a different API: the module-level functions, the named tuple results, and the split between the pure Python and regex backends.
The release history explains why this matters for version pinning. The 1.0 release was tagged on 2024-11-28, and 1.0.1 on 2025-02-01 with the note that the global parser is re-created on every access, which is a performance fix for the new function-based API since there is no longer a single long-lived parser instance. The 1.0.2 release is from 2026-04-05.
The repository layout also shows the rewrite. The top level has src/, doc/, tests/, samples/, scripts/, a tox.ini and a pyproject.toml, and three directories that are not Python packages at all: ua-parser-builtins/, ua-parser-rs/ and uap-core. Those are submodules, signalled by the .gitmodules file, and they are where the rules and the faster matching implementation live.
That is the key architectural point. The rules are not written in Python and they are not Python-specific. They come from the shared ua-parser project, which exists precisely so that the regex definitions can be shared across implementations in different languages, so a user agent string is classified the same way whether it is parsed by the Python package, the Rust one or another port.
The project layout and what it implies for rules coverage
The repository structure says more about the project than the feature list does.
uap-core is the shared rules repository, and the fact that it is a submodule rather than a vendored copy means the rules are pulled at a specific commit and updated when the project decides to update it, not continuously. That is a deliberate design for a cross-language project: the same regex definitions have to satisfy implementations in several languages, so a rule that uses a feature only one language supports cannot land.
ua-parser-builtins/ is the Python package of the built-in rules, and it is the base dependency of the released package, which is why the pyproject lists it as the only runtime dependency. ua-parser-rs/ is the Rust implementation, and the optional re2 extra in the pyproject points at a Go implementation of the same idea. So there are at least three matching engines in play, all consuming the same rules.
The consequence for a user is the important part. The rule set is shared, so the classification is consistent across languages, and so a user agent string that one implementation recognises will be recognised by the others. The cost is that the rules move on the schedule of the shared project rather than the schedule of the Python package, and the Python release is where those updates land. A brand new browser or a new bot fingerprint is not parsed until a rule for it exists upstream.
That is a general property of rule-based user agent parsing and uap-python does not try to hide it. The alternative approaches do not have it, which is the comparison below.
The remaining directories are ordinary: doc/ for the documentation sources, tests/ and tox.ini for the test suite, samples/ with a useragents.txt file of sample strings, and scripts/.
Where uap-python fits, and where it stops
The comparison worth making is with a regular expression written in house, with a commercial device detection service, and with a newer detection library.
Against a regular expression written in house, the difference is the maintenance of the rules rather than the parsing. A hand written pattern that identifies Chrome works until a new Chrome version changes its token, and then it silently misclassifies a slice of your traffic. ua-parser's rules are maintained centrally and applied consistently, and the classification of a string is a documented behaviour rather than a side effect of a pattern someone wrote. The cost is the dependency and the rule update lag, which is the same lag you would have had anyway, just someone else carrying it.
Against a commercial device detection service, the difference is where the parsing happens and what you learn. A service gives you a much larger signal, browser versions, device models, whether it is a bot, and a maintained catalogue, and it will know about devices that appeared last week. uap-python gives you three fields and no network call, which is the right answer when the user agent string is already in your logs, when you are processing it at volume, or when a network round trip per request is not acceptable.
Against a newer detection library, the honest difference is philosophical. uap-parser is regex-based and rule-driven, so it either matches a pattern or it does not, and an unmatched string yields None. Libraries that use a classifier or a set of heuristics can produce a best guess for something they have never seen. If you need a guess rather than a known value, a different approach fits better. If you need a value that is stable and reproducible, the rules approach fits better.
The practical limits are these. The rules lag new browsers, so a dashboard built on it will slowly drift out of date unless you track the upstream updates. It returns a browser, an OS and a device and nothing else, so it will not tell you whether the request is a bot, whether it is a tablet or a phone in a specific way, or what the device's price band is. The 1.0 API break means existing 0.x code has to be migrated rather than upgraded, and the migration guide exists for that reason. And the pure Python mode, which is what you get if you skip the extra, is significantly slower, so the extras are not optional in practice.
The licence is Apache-2.0, which is permissive and carries no obligation beyond attribution, and the maintainer is listed in the pyproject as masklinn with the uap contact address.
Editorial conclusion
uap-python suits a Python service that needs to segment traffic by browser, operating system and device, since it is the reference implementation of the ua-parser project and returns a typed result object rather than a string. It is the wrong tool if you need to keep up with bleeding-edge browsers, because the rules come from a shared cross-language regex set that is updated on its own schedule, and it is the wrong tool if you are on Python 3.9 or older. Before upgrading from the 0.x line, read the migration guide linked from the README, because the 1.0 release is a rewrite rather than a patch, and install the regex extra so you are not running the slow path in production.
Frequently asked questions
How do I install uap-python?
Run pip install 'ua-parser[regex]'. The regex extra is strongly recommended because the pure Python mode without the feature is significantly slower, especially on non-CPython runtimes, though it is the most memory efficient. uap-python supports CPython 3.10 and newer, recent pypy supporting 3.10, and GraalPy 25.
What does parse return in uap-python?
A Result with user_agent, os, device and string. The first three are named tuples holding a family and the version split into major, minor, patch and patch_minor. Any datum not found in the user agent string is set to None, so an empty string gives Result(user_agent=None, os=None, device=None, string='').
How do I get only the browser or only the device from uap-python?
Use parse_user_agent, parse_os and parse_device, which each return only their own sub-result. For specific domains a match failure just returns None, so the functions are safe to call on arbitrary strings.
What changed between uap-python 0.x and 1.0?
Version 1.0 is a rewrite with a different API, exposing module level functions and named tuple results instead of the old parser object interface, and it offers a pure Python mode alongside the regex accelerator. The README links a migration guide for upgraders, and the 1.0.1 release notes mention that the global parser is re-created on every access.
Where do uap-python's parsing rules come from?
They come from the shared ua-parser project rather than from the Python code. The repository carries uap-core as a submodule alongside ua-parser-builtins and ua-parser-rs, so the same regex definitions are shared across implementations in different languages and the classification stays consistent between them.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ua-parser-uap-python)Was this useful?