Python — Travailler avec les API (requests)
Apprenez à utiliser la bibliothèque requests de Python pour faire des appels GET et POST, envoyer des en-têtes, des paramètres et gérer les réponses JSON.
La bibliothèque requests est la méthode standard pour effectuer des appels HTTP depuis Python. Elle encapsule le module bas niveau urllib dans une API propre, de sorte que récupérer une page web ou appeler une API REST ne demande qu'une seule ligne au lieu de dix. Ce chapitre couvre tout ce dont vous avez besoin : installer requests, effectuer des appels GET et POST, envoyer des en-têtes et des paramètres de requête, travailler avec des réponses JSON, téléverser des fichiers, utiliser des sessions et gérer les erreurs de manière robuste.
Installation
requests ne fait pas partie de la bibliothèque standard, vous devez donc l'installer avec pip :
pip install requestsSi vous travaillez dans un environnement virtuel (recommandé), activez-le d'abord afin que le package soit limité à votre projet. Après l'installation, vérifiez que tout fonctionne :
import requests
print(requests.__version__) # e.g. 2.32.3Effectuer une requête GET
requests.get() envoie une requête HTTP GET et retourne un objet Response. Il s'agit de l'opération la plus courante — utilisée pour récupérer des données depuis des API, des pages web et des fichiers.
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
print(response.status_code) # 200
print(response.url) # https://jsonplaceholder.typicode.com/todos/1
print(response.text) # raw response body as a stringRésultat attendu :
200
https://jsonplaceholder.typicode.com/todos/1
{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}Lire la réponse en JSON
La plupart des API modernes retournent du JSON. Appelez .json() sur la réponse plutôt que d'analyser .text manuellement — cela appelle json.loads() pour vous et retourne un dict ou une liste Python.
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
data = response.json()
print(data["title"]) # delectus aut autem
print(data["completed"]) # FalseConsultez le chapitre Python JSON pour en savoir plus sur la correspondance entre les objets Python et les types JSON.
Envoyer des paramètres de requête
Les paramètres de requête sont les paires clé-valeur après le ? dans une URL, comme ?q=python&page=2. Passez-les sous forme de dict dans l'argument params — requests les encode dans l'URL et les ajoute automatiquement.
import requests
params = {
"q": "python requests",
"page": 1,
"per_page": 5,
}
response = requests.get("https://httpbin.org/get", params=params)
# requests builds the full URL for you
print(response.url)
# https://httpbin.org/get?q=python+requests&page=1&per_page=5Utilisez toujours params= plutôt que de construire l'URL manuellement — cela gère correctement les caractères spéciaux et l'encodage.
Envoyer des en-têtes de requête
Les en-têtes transportent des métadonnées : jetons d'authentification, préférences de type de contenu, clés API, etc. Passez-les sous forme de dict dans headers= :
import requests
headers = {
"Accept": "application/json",
"Authorization": "Bearer my-api-token",
"User-Agent": "MyApp/1.0",
}
response = requests.get("https://httpbin.org/headers", headers=headers)
print(response.json())En-têtes courants à envoyer :
| En-tête | Rôle |
|---|---|
Authorization | Jeton d'authentification (Bearer, Basic, etc.) |
Content-Type | Format du corps de la requête (ex. application/json) |
Accept | Format attendu en retour du serveur |
User-Agent | Identifie votre client auprès du serveur |
X-API-Key | Clé API dans un en-tête personnalisé (varie selon le service) |
Effectuer une requête POST
requests.post() envoie des données au serveur — utilisé pour créer des ressources, soumettre des formulaires ou appeler des actions.
Envoyer du JSON
Passez un dict Python à json=. La bibliothèque le sérialise et définit automatiquement l'en-tête Content-Type: application/json :
import requests
payload = {
"title": "Buy groceries",
"completed": False,
"userId": 1,
}
response = requests.post(
"https://jsonplaceholder.typicode.com/todos",
json=payload,
)
print(response.status_code) # 201 Created
print(response.json())Résultat attendu :
201
{'title': 'Buy groceries', 'completed': False, 'userId': 1, 'id': 201}Envoyer des données de formulaire
Certaines API ou formulaires HTML attendent des données au format application/x-www-form-urlencoded. Utilisez data= à la place de json= :
import requests
form_data = {
"username": "alice",
"password": "secret",
}
response = requests.post("https://httpbin.org/post", data=form_data)
print(response.status_code)Envoyer des fichiers (téléversement multipart)
Pour téléverser un fichier, ouvrez-le en mode binaire et passez-le via files= :
import requests
with open("report.pdf", "rb") as f:
response = requests.post(
"https://httpbin.org/post",
files={"file": f},
)
print(response.status_code)requests encode le téléversement en multipart/form-data, ce qu'attendent la plupart des points de terminaison de téléversement de fichiers.
Autres méthodes HTTP
Les API REST utilisent différents verbes HTTP pour différentes opérations. requests fournit une fonction par méthode :
import requests
base = "https://jsonplaceholder.typicode.com/todos/1"
# Update a resource (replace entirely)
response = requests.put(base, json={"title": "Updated", "completed": True, "userId": 1})
print(response.status_code) # 200
# Partial update
response = requests.patch(base, json={"completed": True})
print(response.status_code) # 200
# Delete a resource
response = requests.delete(base)
print(response.status_code) # 200Gestion des erreurs
Vérifier les codes de statut
Le code de statut HTTP vous indique si la requête a réussi. Les groupes les plus importants :
| Plage | Signification |
|---|---|
| 2xx | Succès (200 OK, 201 Created, 204 No Content) |
| 3xx | Redirection (gérée automatiquement par requests) |
| 4xx | Erreur client (400 Bad Request, 401 Unauthorized, 404 Not Found) |
| 5xx | Erreur serveur (500 Internal Server Error, 503 Service Unavailable) |
raise_for_status()
Appeler .raise_for_status() sur une réponse lève automatiquement une exception HTTPError si le code de statut est 4xx ou 5xx. C'est la manière la plus propre d'échouer rapidement sur les mauvaises réponses :
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos/99999")
try:
response.raise_for_status()
data = response.json()
print(data)
except requests.exceptions.HTTPError as err:
print(f"HTTP error: {err}")Sans raise_for_status(), une réponse 404 ressemble à un succès — votre code lit un corps d'erreur et le traite silencieusement.
Gérer les erreurs réseau
Les échecs au niveau réseau (échec de résolution DNS, connexion refusée, délai d'expiration) lèvent requests.exceptions.ConnectionError ou requests.exceptions.Timeout. Attrapez les deux avec la classe de base requests.exceptions.RequestException :
import requests
try:
response = requests.get("https://api.example.com/data", timeout=5)
response.raise_for_status()
data = response.json()
except requests.exceptions.Timeout:
print("The request timed out — server took too long to respond.")
except requests.exceptions.ConnectionError:
print("Could not connect — check your network or the URL.")
except requests.exceptions.HTTPError as err:
print(f"HTTP error {response.status_code}: {err}")
except requests.exceptions.RequestException as err:
print(f"Unexpected error: {err}")Ce modèle couvre toute la hiérarchie des exceptions : délai d'expiration, connexion, erreur HTTP et la classe de base fourre-tout. Consultez Python try/except pour approfondir la gestion des exceptions en Python.
Toujours définir un délai d'expiration
Par défaut, requests attend indéfiniment si le serveur ne répond jamais. Passez toujours timeout= pour éviter les programmes bloqués :
# timeout=(connect_timeout, read_timeout) in seconds
response = requests.get("https://api.example.com/data", timeout=(3, 10))La forme en tuple définit séparément le délai de connexion et le délai de lecture. Un délai de lecture de 10 secondes signifie « attendre jusqu'à 10 secondes entre les octets après l'établissement de la connexion ».
Utiliser les sessions
Un objet requests.Session maintient des paramètres — en-têtes, cookies, authentification — sur plusieurs requêtes vers le même hôte. Il réutilise également la connexion TCP sous-jacente (mise en pool des connexions), ce qui est plus rapide que créer une nouvelle connexion à chaque appel.
import requests
with requests.Session() as session:
# Set headers once — every request in this session will include them
session.headers.update({
"Authorization": "Bearer my-api-token",
"Accept": "application/json",
})
# All requests reuse the connection and headers
r1 = session.get("https://api.example.com/users")
r2 = session.get("https://api.example.com/posts")
r3 = session.post("https://api.example.com/todos", json={"title": "New"})
print(r1.status_code, r2.status_code, r3.status_code)Utilisez une session chaque fois que vous effectuez plus d'une requête vers le même serveur.
Inspecter la réponse
L'objet Response expose tout ce que le serveur a renvoyé :
import requests
response = requests.get("https://httpbin.org/get")
print(response.status_code) # 200
print(response.reason) # OK
print(response.headers) # dict of response headers
print(response.headers["Content-Type"]) # application/json
print(response.encoding) # utf-8
print(response.elapsed) # how long the request took
print(response.url) # final URL (after redirects)Pour le contenu binaire (images, PDF), utilisez response.content (retourne des bytes) à la place de response.text :
import requests
response = requests.get("https://httpbin.org/image/png")
with open("image.png", "wb") as f:
f.write(response.content)Authentification
HTTP Basic Auth
Passez un tuple (username, password) à auth= :
import requests
response = requests.get(
"https://httpbin.org/basic-auth/alice/secret",
auth=("alice", "secret"),
)
print(response.status_code) # 200Jeton Bearer (clés API)
La plupart des API modernes utilisent un jeton Bearer dans l'en-tête Authorization :
import requests
headers = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."}
response = requests.get("https://api.example.com/me", headers=headers)Ne codez jamais en dur les jetons dans les fichiers source. Chargez-les depuis des variables d'environnement ou un gestionnaire de secrets :
import os
import requests
token = os.environ["API_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}
response = requests.get("https://api.example.com/me", headers=headers)Exemple concret — API GitHub
Cet exemple illustre un modèle complet : session, authentification Bearer, pagination, gestion des erreurs et analyse JSON :
import os
import requests
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
BASE_URL = "https://api.github.com"
with requests.Session() as session:
session.headers.update({
"Authorization": f"Bearer {GITHUB_TOKEN}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
})
try:
# Fetch the first page of public repos for a user
response = session.get(
f"{BASE_URL}/users/torvalds/repos",
params={"per_page": 5, "sort": "updated"},
timeout=10,
)
response.raise_for_status()
repos = response.json()
for repo in repos:
print(f"{repo['name']:40s} ★ {repo['stargazers_count']}")
except requests.exceptions.HTTPError as err:
print(f"GitHub API error: {err}")
except requests.exceptions.RequestException as err:
print(f"Network error: {err}")Ce modèle — session avec en-têtes partagés, raise_for_status(), try/except délimité — est l'approche prête pour la production pour tout client API que vous écrivez.
Requêtes concurrentes avec asyncio
Pour les programmes qui appellent de nombreux points de terminaison simultanément, la bibliothèque synchrone requests bloque à chaque appel. Passez à aiohttp (l'équivalent asynchrone) et combinez-le avec le module asyncio de Python :
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as response:
return await response.json()
async def main():
urls = [
"https://jsonplaceholder.typicode.com/todos/1",
"https://jsonplaceholder.typicode.com/todos/2",
"https://jsonplaceholder.typicode.com/todos/3",
]
async with aiohttp.ClientSession() as session:
results = await asyncio.gather(*[fetch(session, u) for u in urls])
for r in results:
print(r["title"])
asyncio.run(main())Consultez le chapitre Python asyncio pour comprendre le fonctionnement de async/await avant d'adopter ce modèle.
Référence rapide
| Tâche | Code |
|---|---|
| Requête GET | requests.get(url) |
| GET avec paramètres | requests.get(url, params={"key": "val"}) |
| GET avec en-têtes | requests.get(url, headers={"Authorization": "Bearer token"}) |
| POST corps JSON | requests.post(url, json={"key": "val"}) |
| POST données de formulaire | requests.post(url, data={"key": "val"}) |
| Téléverser un fichier | requests.post(url, files={"file": open("f.pdf", "rb")}) |
| PUT / PATCH / DELETE | requests.put/patch/delete(url, json=...) |
| Vérifier le statut | response.status_code |
| Lever une exception sur 4xx/5xx | response.raise_for_status() |
| Analyser le corps JSON | response.json() |
| Corps texte brut | response.text |
| Corps en octets bruts | response.content |
| Définir un délai d'expiration | requests.get(url, timeout=5) |
| Réutiliser la connexion | with requests.Session() as s: ... |
Chapitres associés
- Python pip — installer
requestset gérer les dépendances du projet - Python JSON — comprendre l'encodage JSON qui alimente la plupart des réponses API
- Python try/except — écrire une gestion robuste des exceptions autour des appels réseau
- Python Virtual Environments — isoler les dépendances du projet
- Python asyncio — appels HTTP concurrents sans blocage