yarl — Yet Another URL Library

raw JSON →
1.23.0 verified Tue May 12 auth: no python install: verified quickstart: verified

yarl is an immutable, RFC 3986-compliant URL parsing and manipulation library for Python 3. It provides the URL class with automatic percent-encoding/decoding, pathlib-style path operations, query-string helpers, and human-readable representations. The current release is 1.23.0, published March 2026. Releases ship frequently (multiple times per minor version) as part of the aio-libs ecosystem, closely tracking aiohttp.

pip install yarl
error ModuleNotFoundError: No module named 'yarl'
cause The yarl library is not installed in the current Python environment.
fix
pip install yarl
error ModuleNotFoundError: No module named 'yarl.url'
cause The URL class is exposed directly from the top-level yarl package, not from a nested yarl.url submodule.
fix
from yarl import URL
error AttributeError: 'URL' object has no attribute 'add_query_param'
cause yarl.URL objects are immutable. To modify query parameters, use methods like with_query() or update_query(), which return a new URL object.
fix
new_url = url.with_query(param1='value1', param2='value2')
error TypeError: argument should be str or URL, got <class 'int'>
cause Methods like joinpath() or the / operator for URL objects expect string or other URL objects as arguments for path segments, not other types like integers.
fix
Ensure the argument passed is a string representing a path segment or another yarl.URL object. Example: url.joinpath(str(segment_id)) or url / 'segment_name'
breaking URL.build() and URL.with_host() raise TypeError when port is passed as a string. Previously a string port silently produced a malformed URL.
fix Always pass port as int: URL.build(scheme='https', host='example.com', port=443)
breaking propcache is now a required hard dependency (split out ~v1.17). Environments that vendor or vendor-pin transitive deps without propcache will fail to import yarl.
fix Ensure propcache is present in all deployment targets. pip install yarl handles this automatically.
deprecated cache_configure() parameters ip_address_size and host_validate_size are deprecated in favour of encode_host_size and will be removed in a future release.
fix Replace cache_configure(ip_address_size=N, host_validate_size=N) with cache_configure(encode_host_size=N)
gotcha URL properties (e.g. .path, .host, .query_string) return percent-DECODED values. Pass these to other URLs or HTTP wire formats and you risk double-encoding. Use the raw_ prefixed variants (.raw_path, .raw_host, .raw_query_string) for wire-safe strings.
fix Use str(url) or url.raw_path / url.raw_query_string when constructing HTTP request lines.
gotcha Passing boolean values to with_query() or URL.build(query=...) raises TypeError. yarl refuses to guess how to serialize True/False.
fix Convert bools to strings before passing: {'flag': 'true'} not {'flag': True}
gotcha encoded=True skips all auto-encoding but any subsequent mutation method (.with_query(), /, etc.) may re-quote parts, silently corrupting pre-encoded values. The docs explicitly warn against relying on this.
fix Use encoded=True only as a last resort and avoid chaining mutation methods on encoded URLs. Prefer URL.build() or proper string inputs instead.
gotcha The / operator (URL.__truediv__) and URL.joinpath() strip a trailing slash from the base path before appending the segment. URL('https://example.com/api/') / 'v1' gives /api/v1, not /api//v1. This matches pathlib semantics but surprises users expecting query-string-style appending.
fix Use URL.joinpath(*parts) with explicit trailing slash control, or URL.with_path() for full path replacement.
YARL_NO_EXTENSIONS=1 pip install yarl
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.07s 20.2M
3.10 slim (glibc) - - 0.06s 21M
3.11 alpine (musl) - - 0.09s 21.7M
3.11 slim (glibc) - - 0.07s 22M
3.12 alpine (musl) - - 0.06s 13.6M
3.12 slim (glibc) - - 0.06s 14M
3.13 alpine (musl) - - 0.06s 13.3M
3.13 slim (glibc) - - 0.07s 14M
3.9 alpine (musl) - - 0.05s 20.4M
3.9 slim (glibc) - - 0.04s 22M

Parse, inspect, and mutate URLs; build from parts; path-join with /; apply query with %.

from yarl import URL

# Parse an existing URL
url = URL('https://user:pw@api.example.com:8080/v1/items?page=1&q=foo#section')
print(url.scheme)        # 'https'
print(url.host)          # 'api.example.com'  (decoded)
print(url.raw_host)      # IDNA-encoded form
print(url.port)          # 8080 (explicit); None falls back to scheme default
print(url.path)          # '/v1/items'  (percent-decoded)
print(url.raw_path)      # '/v1/items'  (percent-encoded, wire form)
print(url.query)         # MultiDictProxy with parsed key/value pairs
print(url.query_string)  # 'page=1&q=foo'
print(url.fragment)      # 'section'

# Build from components — port must be int, not str
base = URL.build(scheme='https', host='api.example.com', port=8080, path='/v1')
print(base)  # https://api.example.com:8080/v1

# Path-join (like pathlib)
endpoint = base / 'items' / '42'
print(endpoint)  # https://api.example.com:8080/v1/items/42

# Apply a query string with %
with_query = endpoint % {'expand': 'true', 'format': 'json'}
print(with_query)

# Mutation methods return new URL objects (immutable)
updated = endpoint.with_query({'page': '2'}).with_fragment('results')
print(str(updated))      # wire-safe string for HTTP clients
print(updated.human_repr())  # human-readable (non-ASCII decoded)

# Non-ASCII is encoded automatically
unicode_url = URL('https://example.com/пошук?q=кіт')
print(str(unicode_url))      # percent-encoded
print(unicode_url.human_repr())  # readable