W3docs

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 requests

Wenn 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.3

Eine 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 string

Erwartete 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"])  # False

Weitere 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 paramsrequests 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=5

Verwende 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:

HeaderZweck
AuthorizationAuthentifizierungstoken (Bearer, Basic usw.)
Content-TypeFormat des Request-Bodys (z. B. application/json)
AcceptFormat, das du vom Server zurückwillst
User-AgentIdentifiziert deinen Client beim Server
X-API-KeyAPI-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)   # 200

Fehlerbehandlung

Statuscodes prüfen

Der HTTP-Statuscode zeigt an, ob die Anfrage erfolgreich war. Die wichtigsten Gruppen:

BereichBedeutung
2xxErfolg (200 OK, 201 Created, 204 No Content)
3xxWeiterleitung (von requests automatisch behandelt)
4xxClient-Fehler (400 Bad Request, 401 Unauthorized, 404 Not Found)
5xxServer-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)   # 200

Bearer-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

AufgabeCode
GET-Anfragerequests.get(url)
GET mit Parameternrequests.get(url, params={"key": "val"})
GET mit Headernrequests.get(url, headers={"Authorization": "Bearer token"})
POST JSON-Bodyrequests.post(url, json={"key": "val"})
POST Formulardatenrequests.post(url, data={"key": "val"})
Datei hochladenrequests.post(url, files={"file": open("f.pdf", "rb")})
PUT / PATCH / DELETErequests.put/patch/delete(url, json=...)
Status prüfenresponse.status_code
Bei 4xx/5xx auslösenresponse.raise_for_status()
JSON-Body parsenresponse.json()
Roher Text-Bodyresponse.text
Roher Bytes-Bodyresponse.content
Timeout setzenrequests.get(url, timeout=5)
Verbindung wiederverwendenwith requests.Session() as s: ...

Verwandte Kapitel

Übungen

Übung
Which argument sends a Python dict as a JSON body in a POST request?
Which argument sends a Python dict as a JSON body in a POST request?
Was this page helpful?