~/wiki

Python requests

Mis à jour le 2026-08-07Confiance : high
pythonrequestshttpapirestheadersauthjson

Client HTTP synchrone. Pas de timeout par défaut — toujours le passer explicitement.

Le squelette à écrire de mémoire

import os
import requests

BASE = "https://api.example.com/v1"

r = requests.post(
    f"{BASE}/environments",
    headers={
        "Authorization": f"Bearer {os.environ['API_KEY']}",
        "Content-Type": "application/json",
    },
    json={"name": "demo", "url": "https://example.com"},
    timeout=30,
)
r.raise_for_status()
print(r.status_code, r.json())

json= sérialise le dict et pose Content-Type: application/json tout seul. data= envoie du form-urlencoded ou des bytes bruts — c'est la confusion la plus fréquente.

Réponse

Attribut Contenu
r.status_code entier, 200, 404
r.ok True si status_code < 400
r.json() corps parsé, lève JSONDecodeError si ce n'est pas du JSON
r.text corps brut décodé
r.headers dict insensible à la casse
r.raise_for_status() lève HTTPError sur 4xx/5xx, ne fait rien sinon

Session : réutiliser la connexion et les headers

with requests.Session() as s:
    s.headers.update({"Authorization": f"Bearer {os.environ['API_KEY']}"})
    for page in range(1, 6):
        r = s.get(f"{BASE}/items", params={"page": page}, timeout=30)
        r.raise_for_status()
        print(r.json()["items"])

params= construit la query string, pas besoin de la concaténer à la main.

Polling d'un job asynchrone

import time

def wait_for(session_id: str, timeout_s: int = 300, every_s: int = 3) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{BASE}/sessions/{session_id}", timeout=30)
        r.raise_for_status()
        payload = r.json()
        if payload["status"] in {"completed", "failed"}:
            return payload
        time.sleep(every_s)
    raise TimeoutError(f"session {session_id} toujours en cours après {timeout_s}s")

Erreurs à distinguer

try:
    r = requests.get(url, timeout=10)
    r.raise_for_status()
except requests.Timeout:              # dépassement du timeout
    ...
except requests.ConnectionError:      # DNS, refus de connexion, réseau
    ...
except requests.HTTPError as exc:     # 4xx / 5xx après raise_for_status
    print(exc.response.status_code, exc.response.text[:200])

Toutes héritent de requests.RequestException.

Charger la clé depuis un .env

from dotenv import load_dotenv   # pip install python-dotenv
load_dotenv()
key = os.environ["API_KEY"]      # KeyError explicite si absente

os.environ["X"] lève si la variable manque, os.getenv("X") renvoie None en silence. Préférer le premier pour une clé obligatoire.

Debug rapide

print(r.request.method, r.request.url)
print(r.request.headers)
print(r.request.body)

Équivalences avec curl

curl requests
-X POST requests.post(...)
-H "K: V" headers={"K": "V"}
-d '{"a":1}' json={"a": 1}
-G --data-urlencode "q=x" params={"q": "x"}
-u user:pass auth=("user", "pass")
--max-time 30 timeout=30

See also

  • httpx — même API, plus l'async et un timeout par défaut