Skip to content

How to URL-Encode a Query String in Python

Learn how to properly URL-encode query strings in Python using urllib.parse.urlencode(), quote_plus(), and quote() to safely construct URLs with special characters.

URL encoding (also called percent-encoding) converts special characters in a query string into a format safe for transmission over the internet. Characters like spaces, ampersands, and question marks are replaced with their percent-encoded equivalents (e.g., space becomes %20 or +). This is essential when constructing URLs with dynamic parameters, sending form data, or building API requests. This guide covers multiple methods to URL-encode query strings in Python.

Why URL Encoding Is Necessary

URLs can only contain a limited set of characters from the ASCII character set. Special characters like spaces, &, =, ?, and non-ASCII characters must be encoded to avoid breaking the URL structure:

Without encoding: https://example.com/search?q=hello world&lang=python
With encoding:    https://example.com/search?q=hello+world&lang=python

Without encoding, the space in "hello world" would break the URL, and the & could be misinterpreted as a parameter separator.

The urllib.parse.urlencode() function is the standard library solution for encoding dictionaries into properly formatted query strings:

from urllib.parse import urlencode

params = {
    "query": "python programming",
    "page": "1",
    "sort": "relevance",
    "filter": "2024 & beyond"
}

encoded = urlencode(params)
print(encoded)

Output:

query=python+programming&page=1&sort=relevance&filter=2024+%26+beyond

Spaces are encoded as +, the & inside the value is encoded as %26, and key-value pairs are joined with &.

Building a Complete URL

from urllib.parse import urlencode

base_url = "https://api.example.com/search"
params = {
    "q": "machine learning",
    "category": "articles",
    "limit": "10"
}

full_url = f"{base_url}?{urlencode(params)}"
print(full_url)

Output:

https://api.example.com/search?q=machine+learning&category=articles&limit=10

Handling Multiple Values for the Same Key

Use the doseq=True parameter when a key has multiple values:

from urllib.parse import urlencode

params = {
    "color": ["red", "blue", "green"],
    "size": "large"
}

encoded = urlencode(params, doseq=True)
print(encoded)

Output:

color=red&color=blue&color=green&size=large

Without doseq=True, the list would be encoded as a string representation:

from urllib.parse import urlencode

encoded_wrong = urlencode(params)
print(encoded_wrong)

Output:

color=%5B%27red%27%2C+%27blue%27%2C+%27green%27%5D&size=large

Using quote_via for Percent-Encoded Spaces

By default, urlencode() encodes spaces as +. To use %20 instead (as required by some APIs), pass quote_via:

from urllib.parse import urlencode, quote

params = {"query": "hello world", "page": "1"}

print(urlencode(params))

print(urlencode(params, quote_via=quote))

Output:

query=hello+world&page=1
query=hello%20world&page=1

Method 2: Using urllib.parse.quote() and quote_plus()

For encoding individual strings rather than entire dictionaries, use quote() or quote_plus():

from urllib.parse import quote, quote_plus

text = "hello world & goodbye!"

print("quote_plus:", quote_plus(text))

print("quote:     ", quote(text))

Output:

quote_plus: hello+world+%26+goodbye%21
quote:      hello%20world%20%26%20goodbye%21
Function Spaces Become Best For
quote_plus() + Query string values (?key=value)
quote() %20 URL path segments (/path/to/resource)

Specifying Safe Characters

Both functions accept a safe parameter to specify characters that should not be encoded:

from urllib.parse import quote

path = "/api/v2/search results/page 1"

print(quote(path))

print(quote(path, safe='/'))

Output:

/api/v2/search%20results/page%201
/api/v2/search%20results/page%201

Method 3: Building Custom Query Strings Manually

For more control over the encoding process, manually encode each key-value pair:

from urllib.parse import quote_plus

params = {
    "name": "John Doe",
    "city": "New York",
    "interest": "AI & ML"
}

encoded = '&'.join(
    f"{quote_plus(key)}={quote_plus(value)}"
    for key, value in params.items()
)
print(encoded)

Output:

name=John+Doe&city=New+York&interest=AI+%26+ML

This produces the same result as urlencode() but gives you the flexibility to customize encoding for individual keys or values.

Decoding URL-Encoded Strings

To reverse the encoding, use unquote() or unquote_plus():

from urllib.parse import unquote, unquote_plus, parse_qs

encoded = "hello+world+%26+goodbye%21"
print("Decoded:", unquote_plus(encoded))

query_string = "name=John+Doe&city=New+York&interest=AI+%26+ML"
parsed = parse_qs(query_string)
print("Parsed:", parsed)

Output:

Decoded: hello world & goodbye!
Parsed: {'name': ['John Doe'], 'city': ['New York'], 'interest': ['AI & ML']}

Common Mistake: Double Encoding

A frequent error is encoding a string that has already been encoded, resulting in % signs being encoded again:

from urllib.parse import quote_plus

original = "hello world"
encoded_once = quote_plus(original)
print("Encoded once:", encoded_once)

encoded_twice = quote_plus(encoded_once)
print("Encoded twice:", encoded_twice)

Output:

Encoded once: hello+world
Encoded twice: hello%2Bworld

The + from the first encoding gets encoded to %2B, producing an incorrect result.

The correct approach**

Encode only once, using the original unencoded string:

from urllib.parse import quote_plus

original = "hello world"
encoded = quote_plus(original)
print("Correctly encoded:", encoded)

Quick Reference

Task Function Example Output
Encode a dictionary urlencode(params) key1=val1&key2=val2
Encode with lists urlencode(params, doseq=True) k=v1&k=v2
Encode a single string (query) quote_plus(text) hello+world
Encode a single string (path) quote(text) hello%20world
Decode a query string unquote_plus(text) hello world
Parse query string to dict parse_qs(query) {'key': ['value']}

The urllib.parse module provides all the tools needed for URL encoding in Python. Use urlencode() for dictionaries, quote_plus() for individual query values, and quote() for URL path components.

Navigation