urllib.parse.urlencode
The way to build a query string. Values that are not str or bytes are passed through str() — so a list becomes "['a', 'b']" unless you pass doseq=True. Spaces become + (quote_plus); pass quote_via=quote for %20.
Demo
from urllib.parse import urlencode urlencode({'q': 'rock & roll', 'page': 2})
Every key and value is quoted with quote_plus and safe='': a space becomes +, and & = + / ? are all percent-encoded, so they cannot break the query apart. With doseq=False a list is first turned into its str(), "['python', 'web']", and that text is encoded; doseq=True repeats the key once per item — and an empty list then produces nothing at all.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| query | Mapping | sequence of pairs | yes | A dict (anything with .items()) or a list of (key, value) tuples — the list form allows repeated keys in any order. A str or a list of lists raises TypeError. |
| doseq | bool | no (False) | True: a sequence value produces one key=item pair per item. False: every value goes through str(). |
| safe | str | no ('') | Characters quote_via must not encode, e.g. "/" or ":". |
| encoding | str | no (None) | Passed to quote_via for str keys and values (None means UTF-8). |
| errors | str | no (None) | Passed to quote_via for str keys and values. |
| quote_via | callable | no (quote_plus) | The quoting function (3.5+). quote gives %20 for spaces instead of +. |
Return value
str — 'key=value&key2=value2' with every key and value percent-encoded. No leading '?'.
Common patterns
from urllib.parse import urlencode url = 'https://api.example.com/search?' + urlencode({'q': term, 'limit': 50})
from urllib.parse import urlencode urlencode([('tag', 'a'), ('tag', 'b')]) urlencode({'tag': ['a', 'b']}, doseq=True)
from urllib.parse import urlencode query = urlencode({k: v for k, v in params.items() if v is not None})
from urllib.parse import urlsplit, parse_qs, urlencode parts = urlsplit(url) params = parse_qs(parts.query) params['page'] = [str(page)] url = parts._replace(query=urlencode(params, doseq=True)).geturl()
Examples
Pitfalls
from urllib.parse import urlencode urlencode({'tag': ['a', 'b']})
from urllib.parse import urlencode urlencode({'tag': ['a', 'b']}, doseq=True)
from urllib.parse import urlencode urlencode([['a', 1], ['b', 2]])
from urllib.parse import urlencode urlencode([tuple(p) for p in [['a', 1], ['b', 2]]])
from urllib.parse import urlencode urlencode({'q': 'x', 'lang': None})
from urllib.parse import urlencode params = {'q': 'x', 'lang': None} urlencode({k: v for k, v in params.items() if v is not None})
When to use
- Building any query string or form-encoded POST body from data
- Values containing & = + / spaces or non-ASCII text
- Encoding one value → quote / quote_plus
- JSON APIs → send json.dumps(...) as the body instead
- requests / httpx → pass params= and they call urlencode for you
Notes
FAQ
urllib.parse.urlencode({'q': 'x y', 'page': 2}) returns 'q=x+y&page=2'. Add '?' yourself when appending it to a URL.