À quoi ça sert

Dès que ton code Python doit parler à une autre API (météo, GitHub, OpenAI, ton propre backend depuis ton frontend Python…), il te faut un client HTTP. Python a une lib standard (urllib) mais elle est verbeuse — la communauté utilise depuis 15 ans requests, et depuis 2020 son successeur moderne : httpx.

  • API synchrone style requests — drop-in remplaçant pour requests.get(...).
  • API async — la vraie raison d'utiliser httpx. Indispensable dans une app FastAPI ou pour des appels parallèles.
  • HTTP/2 — plus rapide pour multiplexer plusieurs requêtes sur la même connexion.
  • Test client pour FastAPITestClient de FastAPI est basé sur httpx, donc apprendre httpx te sert deux fois.
httpx vs requests vs urllib ?

urllib = stdlib Python, verbeux, à éviter sauf contrainte « zéro dépendance ». requests = lib historique (2011), API très lisible mais maintenance ralentie et pas d'async. httpx = même API que requests + async + HTTP/2 + typage. Pour un nouveau projet, prends httpx. Sur un vieux projet, requests reste très bien — le code se traduit ligne à ligne.

Un exemple d'usage

Tu veux récupérer la météo actuelle d'une ville depuis l'API publique open-meteo.com :

python
import httpx

response = httpx.get(
    "https://api.open-meteo.com/v1/forecast",
    params={
        "latitude": 50.85,
        "longitude": 4.35,
        "current": "temperature_2m",
    },
    timeout=10.0,
)

response.raise_for_status()              # exception si 4xx/5xx
data = response.json()
print(data["current"]["temperature_2m"])  # 12.3

Le code se lit comme du pseudo-code : URL, paramètres, timeout. On appelle raise_for_status() pour transformer un code HTTP d'erreur en exception explicite, puis .json() pour parser le corps de la réponse.

How-to : installer et utiliser httpx

  1. Installer httpx

    Avec UV :

    bash
    uv add httpx

    Pour activer HTTP/2 (optionnel), il faut un extra :

    bash
    uv add "httpx[http2]"
  2. Les méthodes HTTP de base

    python
    import httpx
    
    # GET (avec query parameters)
    r = httpx.get("https://api.example.com/users", params={"page": 2})
    
    # POST (avec corps JSON)
    r = httpx.post("https://api.example.com/users",
                    json={"name": "Alice", "age": 30})
    
    # PUT / PATCH / DELETE — même signature
    httpx.put(url, json=...)
    httpx.patch(url, json=...)
    httpx.delete(url)
  3. Inspecter la réponse

    python
    r.status_code        # 200, 404, 500…
    r.headers            # dict des en-têtes de réponse
    r.text               # corps en str
    r.content            # corps en bytes (binaire)
    r.json()             # parse en dict/list
    r.url                # URL finale (après redirects)
    
    r.raise_for_status()  # exception si 4xx/5xx
  4. Headers, auth, timeouts

    python
    # Headers personnalisés (ex: token API)
    r = httpx.get(
        url,
        headers={"Authorization": f"Bearer {token}",
                 "User-Agent": "mon-app/1.0"},
    )
    
    # Auth basique
    r = httpx.get(url, auth=("user", "password"))
    
    # Timeout
    r = httpx.get(url, timeout=10.0)
    r = httpx.get(url, timeout=httpx.Timeout(
        connect=5.0, read=30.0, write=10.0, pool=5.0,
    ))
    Toujours mettre un timeout

    Sans timeout, une requête peut bloquer ton code indéfiniment si le serveur tarde à répondre. C'est particulièrement critique dans une API FastAPI : un appel sortant sans timeout peut pendre toutes tes requêtes entrantes. httpx met un timeout par défaut de 5s (contrairement à requests qui n'en met pas) — bonne pratique conservée.

  5. Utiliser un Client pour plusieurs requêtes

    Si tu fais plus d'une requête vers le même hôte, utilise un Client : il réutilise la connexion TCP (plus rapide) et factorise la config.

    python
    with httpx.Client(
        base_url="https://api.github.com",
        headers={"Authorization": f"Bearer {token}"},
        timeout=10.0,
    ) as client:
        user = client.get("/user").json()
        repos = client.get("/user/repos").json()
        issues = client.get("/issues").json()
  6. Async pour les requêtes en parallèle

    C'est la raison d'utiliser httpx plutôt que requests. AsyncClient permet de lancer N requêtes simultanément sans bloquer le thread.

    python
    import asyncio
    import httpx
    
    async def fetch_all(urls):
        async with httpx.AsyncClient(timeout=10) as client:
            tasks = [client.get(u) for u in urls]
            responses = await asyncio.gather(*tasks)
            return [r.json() for r in responses]
    
    urls = ["https://api.x.com/a", "https://api.x.com/b", "…"]
    results = asyncio.run(fetch_all(urls))

    10 requêtes séquentielles à 200 ms = 2 s. Les mêmes 10 en async avec gather = ~200 ms total. Différence massive sur un appel-aggregator (style « passerelle » qui interroge plusieurs services).

  7. Upload de fichier et download streamé

    python
    # Upload (multipart/form-data)
    with open("photo.jpg", "rb") as f:
        r = httpx.post(url, files={"image": f})
    
    # Download streamé (pour gros fichiers, sans tout mettre en RAM)
    with httpx.stream("GET", big_url) as r:
        with open("out.zip", "wb") as f:
            for chunk in r.iter_bytes():
                f.write(chunk)

Aide-mémoire

python (one-shot)
import httpx
r = httpx.get(url, params={"q": "…"}, timeout=10)
r.raise_for_status(); r.json()
httpx.post(url, json={...})
httpx.put(url, json={...}); httpx.delete(url)
python (Client réutilisable)
with httpx.Client(base_url="…", timeout=10) as c:
    c.get("/a"); c.post("/b", json=...)
python (async)
async with httpx.AsyncClient() as c:
    r = await c.get(url)
results = await asyncio.gather(*tasks)
python (réponse)
r.status_code; r.headers; r.text; r.content
r.json(); r.raise_for_status()

httpx et le reste de l'écosystème

  • FastAPI — couple évident. FastAPI est un serveur asynchrone ; depuis du code FastAPI on parle aux autres APIs avec AsyncClient pour ne pas bloquer le worker. Le TestClient de FastAPI est lui-même un wrapper httpx.
  • Pydantic — pattern habituel : data = MyModel.model_validate(response.json()). Tu transformes une réponse JSON en objet typé d'un coup.
  • pytest — le module respx mocke httpx pour les tests, sans toucher au réseau. Tests rapides et déterministes.
  • BeautifulSoup — scraping = récupérer le HTML avec httpx, le parser avec BeautifulSoup. Remplace progressivement le couple requests + BeautifulSoup.
  • Hugging Face / Ollama — pour appeler les endpoints HTTP de ces services depuis Python (inférence à distance d'un modèle).
  • Loguru / logging — en cas d'erreur, logger r.status_code et r.text rend le debug bien plus rapide qu'une simple HTTPStatusError.
Migration depuis requests

La majorité du code traduit ligne-à-ligne : requests.get(...)httpx.get(...), même API pour params, json, headers, auth. Les rares différences : timeout par défaut (httpx en met un, requests non), SessionClient, et le support async en bonus.

Pour aller plus loin