8. Query parameters
Most real-world APIs need extra details in your request: a city for weather data, a search term for videos, a filter for results. These extra pieces of information are sent as query parameters.
Query parameters are added to URLs after a ?, written as key=value pairs, and separated by &. They customise the API's response. Here's the shape of a URL with three of them:
https://httpbin.org/get?name=Alice&age=25&city=Boston
└──────────┬──────────┘ └──────────────┬──────────────┘
Base URL Query Parameters
Breaking it down:
?name=Alice ← First parameter (starts with ?)
&age=25 ← Second parameter (starts with &)
&city=Boston ← Third parameter (starts with &)
Sending parameters as part of the URL
We'll start by hard-coding parameters directly in the URL and sending them to httpbin's /get endpoint, which echoes back whatever you send.
Save this as query_params.py:
import requests
url = "https://httpbin.org/get?name=Alice&age=25&city=Boston"
response = requests.get(url, timeout=5)
print("Status:", response.status_code)
print("\nWhat the server received:")
data = response.json()
print("Parameters:", data["args"])
Run it:
$ python query_params.py
Status: 200
What the server received:
Parameters: {'name': 'Alice', 'age': '25', 'city': 'Boston'}
The server parsed the query string and extracted each parameter. Notice that age came back as the string "25" even though we might think of it as a number. Query parameters are always transmitted as text; it's up to the server to decide whether to treat them as numbers, dates, or other types.
Five things to remember about query parameters:
- Base URL. The main endpoint (here,
https://httpbin.org/get). - Question mark. Marks the start of the query string.
- Key-value pairs. Written as
key=value. - Ampersand. Separates multiple parameters.
- All values are strings. Even numbers and booleans are transmitted as text.
Using the params argument instead
Manually building parameter strings is error-prone. What if a value contains spaces? Special characters? Symbols like & or =? The params argument in requests solves these problems by automatically handling URL encoding, type conversion, and formatting. It feels like a small convenience until you hit a subtle bug that manual string building would have caused.
Passing parameters as a dictionary is cleaner, safer, and more maintainable than string concatenation:
- Automatic URL encoding. Handles spaces, special characters, and symbols correctly.
- Type conversion. Converts Python types (int, float, bool) to strings automatically.
- Cleaner code. No manual string building or ampersand juggling.
- Error prevention. Much harder to accidentally break the URL with formatting mistakes.
Side by side, here's the difference between the fragile manual approach and the robust one:
import requests
# Manual string building - fragile and error-prone
name = "Alice Smith" # What if there's a space?
city = "New York" # What about special characters?
url = f"https://httpbin.org/get?name={name}&city={city}" # Easy to get wrong
response = requests.get(url, timeout=5)
import requests
# Clean, safe parameter passing
params = {
"name": "Alice Smith",
"city": "New York",
"age": 25
}
response = requests.get("https://httpbin.org/get", params=params, timeout=5)
The params version automatically handles the spaces in "Alice Smith" and "New York" by encoding them safely in the URL. You don't have to think about the low-level details; requests takes care of it.
A complete example with type conversion
Now we'll see params handle multiple data types at once (a string, an integer, a boolean) and print the final URL it actually constructed. Pay attention to how each Python type becomes part of the URL string.
Replace the contents of query_params.py with this:
import requests
url = "https://httpbin.org/get"
# Use a dictionary for parameters
params = {
"name": "Alice Smith",
"age": 25,
"city": "New York",
"verified": True
}
response = requests.get(url, params=params, timeout=5)
print("Status:", response.status_code)
print("Final URL:", response.url)
print("\nWhat the server received:")
data = response.json()
for key, value in data["args"].items():
print(f" {key}: {value}")
Run it:
$ python query_params.py
Status: 200
Final URL: https://httpbin.org/get?name=Alice+Smith&age=25&city=New+York&verified=True
What the server received:
name: Alice Smith
age: 25
city: New York
verified: True
Four things to notice:
- Automatic encoding. "Alice Smith" became
Alice+Smithin the URL (+is one of the valid ways to encode a space in a URL). - Type conversion. The integer
25and booleanTruewere converted to strings automatically. - response.url shows the complete URL that was actually requested, with encoded parameters.
- The server sees decoded values. The server receives "Alice Smith", not "Alice+Smith". The encoding is purely for safe transmission.
Always reach for the params argument instead of building query strings by hand. It's more reliable, more readable, and handles edge cases you might not think of until they bite you.
Next, we'll put every pattern from this chapter together: requests, responses, JSON, timeouts, errors, and parameters, all in one small program that talks to two different APIs.