urllib.parse.quote

Three spellings of percent-encoding. quote is for URL paths — and it keeps '/' unless you pass safe=''. quote_plus is for form-style query values: a space becomes +, and nothing is safe by default. quote_from_bytes does the same job on bytes you already have.

urllib.parse functionPython 3.0+Live demo
Common call
quote(name, safe='')
Returns
'a%20b%2Fc' for 'a b/c'
Replaces
hand-written .replace(' ', '%20') chains
Watch out
The default safe='/' leaves slashes unencoded — wrong for a single path segment or a query value
urllib.parse.quote(stringstring — The text to encode. A str is first encoded to bytes (UTF-8 unless encoding says otherwise); bytes are used as they are.type: str | bytes · required, safesafe — Extra characters to leave unencoded, on top of letters, digits and _ . - ~. quote and quote_from_bytes default to '/', quote_plus to ''. Non-ASCII characters in safe are ignored.type: str | bytes · default: '/'='/', encodingencoding — Codec used to turn a str into bytes (None means 'utf-8'). Must not be given when string is bytes — TypeError.type: str · default: None=None, errorserrors — What to do with characters the codec cannot encode (None means 'strict', which raises UnicodeEncodeError).type: str · default: None=None)
→ str

Demo

Live evaluation
The same text three ways: quote with its default safe, quote with nothing safe, and quote_plus.
Try:
Inputs
textstrany text
Code
from urllib.parse import quote, quote_plus
(quote('rock & roll'), quote('rock & roll', safe=''), quote_plus('rock & roll'))
Result
('rock%20%26%20roll', 'rock%20%26%20roll', 'rock+%26+roll')

quote keeps / because its default is safe='/', so 'AC/DC live' becomes 'AC/DC%20live' — fine inside a path, wrong for one path segment or a query value. quote_plus writes the space as + and therefore must encode a literal + as %2B. Non-ASCII text is encoded to UTF-8 first: é is the two bytes %C3%A9, € the three bytes %E2%82%AC.

Parameters

NameTypeRequiredDescription
stringstr | bytesyesThe text to encode. A str is first encoded to bytes (UTF-8 unless encoding says otherwise); bytes are used as they are.
safestr | bytesno ('/')Extra characters to leave unencoded, on top of letters, digits and _ . - ~. quote and quote_from_bytes default to '/', quote_plus to ''. Non-ASCII characters in safe are ignored.
encodingstrno (None)Codec used to turn a str into bytes (None means 'utf-8'). Must not be given when string is bytes — TypeError.
errorsstrno (None)What to do with characters the codec cannot encode (None means 'strict', which raises UnicodeEncodeError).

Return value

str — The percent-encoded text: ASCII letters, digits, _ . - ~ and the characters in safe stay as they are; every other byte becomes %XX (uppercase hex).

Common patterns

One path segment
safe='' so a slash inside the value cannot create a new path segment.
from urllib.parse import quote
url = f'https://example.com/files/{quote(filename, safe="")}'
A whole path
The default safe keeps the separators and encodes everything else.
from urllib.parse import quote
url = 'https://example.com' + quote('/docs/my file.txt')
Match JavaScript encodeURIComponent
encodeURIComponent also leaves ! * ' ( ) alone.
from urllib.parse import quote
quote(text, safe="!*'()")
A query string from a dict
Do not quote and join by hand — urlencode applies quote_plus to every key and value.
from urllib.parse import urlencode
query = urlencode({'q': search, 'page': page})

Examples

1. quote keeps the slash
from urllib.parse import quote quote('a b&c/d')
Returns
'a%20b%26c/d'
2. safe='' encodes it too
from urllib.parse import quote quote('a b&c/d', safe='')
Returns
'a%20b%26c%2Fd'
3. quote_plus: space → +
from urllib.parse import quote_plus quote_plus('a b&c/d')
Returns
'a+b%26c%2Fd'
4. A literal + becomes %2B
from urllib.parse import quote_plus quote_plus('1+1=2')
Returns
'1%2B1%3D2'
5. Non-ASCII → UTF-8 bytes
from urllib.parse import quote quote('café €')
Returns
'caf%C3%A9%20%E2%82%AC'
6. A different encoding
from urllib.parse import quote quote('café', encoding='latin-1')
Returns
'caf%E9'
7. quote_from_bytes
from urllib.parse import quote_from_bytes quote_from_bytes(b'\x00\xff/')
Returns
'%00%FF/'
8. Numbers are not text
from urllib.parse import quote quote(42)
Returns
TypeError: quote_from_bytes() expected bytes

Pitfalls

1. The default safe="/" in a path segment
A user-supplied name with a slash silently becomes two path segments, pointing at a different resource.
default safe
from urllib.parse import quote
'/files/' + quote('2026/report.pdf')
'/files/2026/report.pdf'
safe=''
from urllib.parse import quote
'/files/' + quote('2026/report.pdf', safe='')
'/files/2026%2Freport.pdf'
2. Quoting text that is already encoded
quote encodes % like any other character, so an already-encoded value is encoded twice and the server sees the literal text %20.
quote twice
from urllib.parse import quote
quote('my%20file.txt')
'my%2520file.txt'
decode, then encode
from urllib.parse import quote, unquote
quote(unquote('my%20file.txt'))
'my%20file.txt'
3. An encoding that cannot hold the text
errors defaults to 'strict', so a character outside the codec raises. Prefer UTF-8; use errors= only if the server really wants another codec.
ascii
from urllib.parse import quote
quote('€', encoding='ascii')
UnicodeEncodeError: 'ascii' codec can't encode character '\u20ac' in position 0: ordinal not in range(128)
utf-8 (the default)
from urllib.parse import quote
quote('€')
'%E2%82%AC'

When to use

Use it
  • Putting a value into a URL path (safe="" for a single segment)
  • Building a query value by hand: quote_plus
  • Encoding raw bytes for a URL: quote_from_bytes
Reach for something else
  • A whole query string from a dict → urlencode
  • Already-encoded text → it gets encoded twice (% becomes %25)
  • HTML escaping → html.escape

Notes

CPython impl
quote encodes str with str.encode(encoding, errors) and hands the bytes to quote_from_bytes, which maps every byte through a cached table: kept as-is when it is unreserved or in safe, else '%XX'
Unreserved
A-Z a-z 0-9 _ . - ~ are never encoded (RFC 3986). ~ joined the set in Python 3.7
Defaults
quote: safe='/'. quote_plus: safe=''. quote_from_bytes: safe='/'
Exceptions
TypeError for encoding/errors with bytes input and for non-str/bytes values; UnicodeEncodeError with errors='strict'

FAQ

quote writes a space as %20 and keeps '/' by default (safe='/'); it is meant for URL paths. quote_plus writes a space as + and has safe='', the form-encoding style used in query strings — so a literal + becomes %2B. unquote_plus reverses quote_plus.