Query parameters and headers

Requests · Essentials · 8 min

Almost every API call needs query parameters, an Accept header or an API key, and hand-building URLs causes bugs.

Never build query strings by concatenating text. Pass a dict to params= and Requests URL-encodes keys and values for you, including spaces, & and non-ASCII characters:

requests.get(url, params={"q": "rock & roll", "page": 2})

A value that is a list produces a repeated key (tag=a&tag=b), and keys whose value is None are dropped, which is convenient for optional filters. Parameters already in the URL are kept and the new ones appended.

Custom headers go in headers= as a dict. Typical uses:

  • Accept: application/json to ask for JSON,
  • Authorization: Bearer <token> for API tokens (there is also an auth= mechanism, covered later),
  • User-Agent to identify your client; some APIs reject the default python-requests/x.y agent.

Header values must be strings (or bytes); passing an int raises an error. Requests adds its own defaults (User-Agent, Accept-Encoding, Accept, Connection), and your values override them. Setting a header to None removes a default.

Some headers are better left to Requests: it sets Content-Type and Content-Length when you use json=, data= or files=. Setting Content-Type manually for a multipart upload breaks it, because the boundary parameter would be missing.

Inspect r.url or r.request.headers to see exactly what was sent.

Example

import requests

r = requests.get(
    "https://httpbin.org/get",
    params={"q": "rock & roll", "tag": ["a", "b"], "page": 2, "skip": None},
    headers={"Accept": "application/json", "User-Agent": "my-script/1.0"},
    timeout=10,
)
print(r.url)
# https://httpbin.org/get?q=rock+%26+roll&tag=a&tag=b&page=2
echo = r.json()
print(echo["args"])              # {'page': '2', 'q': 'rock & roll', 'tag': ['a', 'b']}
print(echo["headers"]["User-Agent"])    # my-script/1.0
print(r.request.headers["Accept-Encoding"])  # gzip, deflate (default; may add br, zstd)

Remember

  • params= builds and encodes the query string; None values are dropped
  • headers= adds or overrides headers; values must be strings
  • Let Requests set Content-Type for json=, data= and files=

Watch out for

  • Formatting URLs with f-strings like f'{url}?q={term}', which breaks on spaces and &.

Check yourself

  1. What URL does requests.get('https://x.org/s', params={'q': 'a b', 'n': None}) request?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: requests.readthedocs.io/en/latest/user/quickstart/#passing-parameters-in-urls