Python Arbeiten mit APIs (requests)
Lerne, die Python-Bibliothek requests zu verwenden: GET- und POST-Anfragen, Header, Query-Parameter, JSON-Antworten und Fehlerbehandlung.
Die requests-Bibliothek ist der Standardweg, um HTTP-Aufrufe aus Python heraus durchzuführen. Sie kapselt Pythons niedrigstufiges urllib in einer übersichtlichen API, sodass das Abrufen einer Webseite oder das Aufrufen einer REST-API eine Zeile statt zehn kostet. Dieses Kapitel behandelt alles, was du benötigst: die Installation von requests, GET- und POST-Anfragen, das Senden von Headern und Query-Parametern, die Arbeit mit JSON-Antworten, das Hochladen von Dateien, die Verwendung von Sessions und eine robuste Fehlerbehandlung.
Installation
requests ist nicht Teil der Standardbibliothek, daher installierst du es mit pip:
pip install requestsWenn du innerhalb einer virtuellen Umgebung arbeitest (empfohlen), aktiviere sie zuerst, damit das Paket auf dein Projekt beschränkt ist. Verifiziere nach der Installation, dass es funktioniert:
import requests
print(requests.__version__) # e.g. 2.32.3Eine GET-Anfrage stellen
requests.get() sendet eine HTTP GET-Anfrage und gibt ein Response-Objekt zurück. Dies ist die häufigste Operation – verwendet zum Abrufen von Daten aus APIs, Webseiten und Dateien.
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 stringErwartete Ausgabe:
200
https://jsonplaceholder.typicode.com/todos/1
{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}Die Antwort als JSON lesen
Die meisten modernen APIs geben JSON zurück. Rufe .json() auf der Antwort auf, anstatt .text manuell zu parsen – es ruft json.loads() für dich auf und gibt ein Python-Dict oder eine Liste zurück.
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
data = response.json()
print(data["title"]) # delectus aut autem
print(data["completed"]) # FalseWeitere Details dazu, wie Python-Objekte auf JSON-Typen abgebildet werden, findest du im Kapitel Python JSON.
Query-Parameter senden
Query-Parameter sind die Schlüssel-Wert-Paare nach dem ? in einer URL, wie ?q=python&page=2. Übergebe sie als Dict an das Argument params – requests URL-kodiert und fügt sie automatisch an.
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=5Verwende stets params=, anstatt die URL manuell zusammenzubauen – das behandelt Sonderzeichen und die Kodierung korrekt.
Request-Header senden
Header übertragen Metadaten: Authentifizierungstoken, Content-Type-Präferenzen, API-Keys und mehr. Übergebe sie als Dict an 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())Häufig verwendete Header:
| Header | Zweck |
|---|---|
Authorization | Authentifizierungstoken (Bearer, Basic usw.) |
Content-Type | Format des Request-Bodys (z. B. application/json) |
Accept | Format, das du vom Server zurückwillst |
User-Agent | Identifiziert deinen Client beim Server |
X-API-Key | API-Key in einem benutzerdefinierten Header (variiert je nach Dienst) |
Eine POST-Anfrage stellen
requests.post() sendet Daten an den Server – verwendet zum Erstellen von Ressourcen, Absenden von Formularen oder Aufrufen von Aktionen.
JSON senden
Übergebe ein Python-Dict an json=. Die Bibliothek serialisiert es und setzt den Header Content-Type: application/json automatisch:
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())Erwartete Ausgabe:
201
{'title': 'Buy groceries', 'completed': False, 'userId': 1, 'id': 201}Formulardaten senden
Einige APIs oder HTML-Formulare erwarten Daten im Format application/x-www-form-urlencoded. Verwende dafür data= anstatt json=:
import requests
form_data = {
"username": "alice",
"password": "secret",
}
response = requests.post("https://httpbin.org/post", data=form_data)
print(response.status_code)Dateien senden (Multipart-Upload)
Um eine Datei hochzuladen, öffne sie im Binärmodus und übergebe sie über 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 kodiert den Upload als multipart/form-data, was die meisten Datei-Upload-Endpunkte erwarten.
Weitere HTTP-Methoden
REST-APIs verwenden verschiedene HTTP-Verben für unterschiedliche Operationen. requests stellt eine Funktion pro Methode bereit:
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) # 200Fehlerbehandlung
Statuscodes prüfen
Der HTTP-Statuscode zeigt an, ob die Anfrage erfolgreich war. Die wichtigsten Gruppen:
| Bereich | Bedeutung |
|---|---|
| 2xx | Erfolg (200 OK, 201 Created, 204 No Content) |
| 3xx | Weiterleitung (von requests automatisch behandelt) |
| 4xx | Client-Fehler (400 Bad Request, 401 Unauthorized, 404 Not Found) |
| 5xx | Server-Fehler (500 Internal Server Error, 503 Service Unavailable) |
raise_for_status()
Der Aufruf von .raise_for_status() auf einer Antwort löst automatisch eine HTTPError-Ausnahme aus, wenn der Statuscode 4xx oder 5xx ist. Dies ist der sauberste Weg, um bei fehlerhaften Antworten schnell zu scheitern:
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}")Ohne raise_for_status() sieht eine 404-Antwort wie ein Erfolg aus – dein Code liest einen Fehler-Body und verarbeitet ihn stillschweigend.
Netzwerkfehler behandeln
Netzwerkfehler (DNS-Lookup-Fehler, Verbindung abgelehnt, Timeout) lösen requests.exceptions.ConnectionError oder requests.exceptions.Timeout aus. Fange beide mit der Basisklasse requests.exceptions.RequestException ab:
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}")Dieses Muster deckt die gesamte Ausnahmehierarchie ab: Timeout, Verbindungsfehler, HTTP-Fehler und die allgemeine Basisklasse. Weitere Informationen zur Ausnahmebehandlung in Python findest du im Kapitel Python try/except.
Immer einen Timeout setzen
Standardmäßig wartet requests ewig, wenn der Server nie antwortet. Übergebe stets timeout=, um hängende Programme zu vermeiden:
# timeout=(connect_timeout, read_timeout) in seconds
response = requests.get("https://api.example.com/data", timeout=(3, 10))Die Tupel-Form setzt den Verbindungs-Timeout und den Lese-Timeout separat. Ein Lese-Timeout von 10 Sekunden bedeutet: „Warte bis zu 10 Sekunden zwischen Bytes, nachdem die Verbindung hergestellt wurde."
Sessions verwenden
Ein requests.Session-Objekt speichert Einstellungen – Header, Cookies, Authentifizierung – über mehrere Anfragen an denselben Host hinweg. Es verwendet auch die zugrunde liegende TCP-Verbindung wieder (Connection Pooling), was schneller ist als für jeden Aufruf eine neue Verbindung aufzubauen.
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)Verwende eine Session, wenn du mehr als eine Anfrage an denselben Server stellst.
Die Antwort untersuchen
Das Response-Objekt gibt alles preis, was der Server zurückgesendet hat:
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)Für binäre Inhalte (Bilder, PDFs) verwende response.content (gibt bytes zurück) anstatt response.text:
import requests
response = requests.get("https://httpbin.org/image/png")
with open("image.png", "wb") as f:
f.write(response.content)Authentifizierung
HTTP Basic Auth
Übergebe ein (username, password)-Tupel an auth=:
import requests
response = requests.get(
"https://httpbin.org/basic-auth/alice/secret",
auth=("alice", "secret"),
)
print(response.status_code) # 200Bearer-Token (API-Keys)
Die meisten modernen APIs verwenden ein Bearer-Token im Authorization-Header:
import requests
headers = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."}
response = requests.get("https://api.example.com/me", headers=headers)Hard-code niemals Token in Quelldateien. Lade sie aus Umgebungsvariablen oder einem Secrets-Manager:
import os
import requests
token = os.environ["API_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}
response = requests.get("https://api.example.com/me", headers=headers)Praxisbeispiel — GitHub API
Dieses Beispiel demonstriert ein vollständiges Muster: Session, Bearer-Auth, Paginierung, Fehlerbehandlung und JSON-Parsing:
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}")Dieses Muster – Session mit gemeinsamen Headern, raise_for_status(), eingegrenztes try/except – ist der produktionsreife Ansatz für jeden API-Client, den du schreibst.
Gleichzeitige Anfragen mit asyncio
Bei Programmen, die viele Endpunkte gleichzeitig aufrufen, blockiert die synchrone requests-Bibliothek bei jedem Aufruf. Wechsle zu aiohttp (dem asynchronen Äquivalent) und kombiniere es mit Pythons asyncio-Modul:
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())Lies das Kapitel Python asyncio, um zu verstehen, wie async/await funktioniert, bevor du dieses Muster übernimmst.
Kurzreferenz
| Aufgabe | Code |
|---|---|
| GET-Anfrage | requests.get(url) |
| GET mit Parametern | requests.get(url, params={"key": "val"}) |
| GET mit Headern | requests.get(url, headers={"Authorization": "Bearer token"}) |
| POST JSON-Body | requests.post(url, json={"key": "val"}) |
| POST Formulardaten | requests.post(url, data={"key": "val"}) |
| Datei hochladen | requests.post(url, files={"file": open("f.pdf", "rb")}) |
| PUT / PATCH / DELETE | requests.put/patch/delete(url, json=...) |
| Status prüfen | response.status_code |
| Bei 4xx/5xx auslösen | response.raise_for_status() |
| JSON-Body parsen | response.json() |
| Roher Text-Body | response.text |
| Roher Bytes-Body | response.content |
| Timeout setzen | requests.get(url, timeout=5) |
| Verbindung wiederverwenden | with requests.Session() as s: ... |
Verwandte Kapitel
- Python pip —
requestsinstallieren und Projektabhängigkeiten verwalten - Python JSON — die JSON-Kodierung verstehen, die die meisten API-Antworten antreibt
- Python try/except — robuste Ausnahmebehandlung für Netzwerkaufrufe schreiben
- Python Virtual Environments — Projektabhängigkeiten isolieren
- Python asyncio — gleichzeitige HTTP-Aufrufe ohne Blockierung