Requests — Fundamentals & Methods
Installation
uv add requests
GET — Read Data
Basic GET
import requests
r = requests.get("https://api.example.com/users", timeout=(3.05, 10))
r.raise_for_status() # raises HTTPError on 4xx/5xx
users = r.json() # parse JSON body into a Python dict/list
Query Parameters
params= encodes values into the URL automatically.
# results in: /users?page=2&per_page=25&role=admin
r = requests.get(
"https://api.example.com/users",
params={"page": 2, "per_page": 25, "role": "admin"},
timeout=(3.05, 10),
)
For repeated keys use a list of tuples:
# results in: ?tag=python&tag=http&sort=stars
params = [("tag", "python"), ("tag", "http"), ("sort", "stars")]
r = requests.get(url, params=params, timeout=(3.05, 10))
POST — Create Data
JSON Body (most common)
# json= auto-sets Content-Type: application/json and serializes the dict
r = requests.post(
"https://api.example.com/users",
json={"name": "Alice", "email": "alice@example.com"},
timeout=(3.05, 10),
)
created = r.json()
Form Data
# data= sends application/x-www-form-urlencoded (HTML form format)
r = requests.post(
url,
data={"username": "alice", "password": "secret"},
timeout=(3.05, 10),
)
Raw Body
r = requests.post(
url,
data=b"raw bytes here",
headers={"Content-Type": "application/octet-stream"},
timeout=(3.05, 10),
)
PUT / PATCH / DELETE
# PUT replaces the entire resource
r = requests.put(
url,
json={"name": "Alice Updated", "email": "a@example.com"},
timeout=(3.05, 10),
)
# PATCH updates only the provided fields
r = requests.patch(url, json={"email": "new@example.com"}, timeout=(3.05, 10))
# DELETE removes the resource
r = requests.delete(f"https://api.example.com/users/{user_id}", timeout=(3.05, 10))
r.raise_for_status()
Headers
Custom Headers
headers = {
"Authorization": "Bearer eyJhbGciOi...",
"Accept": "application/json",
"X-Request-ID": "abc-123", # custom tracing header
}
r = requests.get(url, headers=headers, timeout=(3.05, 10))
Reading Response Headers
r.headers["Content-Type"] # "application/json; charset=utf-8"
r.headers.get("X-RateLimit-Remaining", "unknown") # safe access with default
# response headers are case-insensitive: r.headers["content-type"] works too
Cookies
# send cookies explicitly
r = requests.get(url, cookies={"session_id": "abc123"}, timeout=(3.05, 10))
# read cookies from response
session_cookie = r.cookies.get("session_id")
Sessions — Connection Pooling
A Session reuses TCP connections, shares headers and cookies across requests.
import requests
with requests.Session() as s:
s.headers.update({
"Authorization": "Bearer TOKEN",
"Accept": "application/json",
})
# both requests reuse the same TCP connection
users = s.get("https://api.example.com/users", timeout=(3.05, 10))
users.raise_for_status()
s.patch(
f"https://api.example.com/users/{users.json()[0]['id']}",
json={"role": "admin"},
timeout=(3.05, 10),
)
| Without Session | With Session |
|---|---|
| New TCP + TLS handshake per request | One handshake, reuses connection |
| Headers repeated manually | Set once on session |
| Cookies lost between calls | Cookies persist automatically |
Per-request headers merge with session headers; per-request value wins on conflict.
Response Object
| Attribute | Returns |
|---|---|
r.status_code |
200, 404, 500 |
r.ok |
True if status < 400 |
r.json() |
Parsed JSON -> dict/list |
r.text / r.content |
Body as str / bytes |
r.headers |
Case-insensitive dict |
r.cookies |
Response cookies |
r.url |
Final URL after redirects |
r.elapsed |
Round-trip timedelta |
r.raise_for_status() |
Raises HTTPError on 4xx/5xx |
Prefer r.raise_for_status() over manual if r.status_code == ... checks.