PDF in Markdown umwandeln mit Python
Ein Schritt-für-Schritt-Tutorial mit der REST-API: hol dir einen Schlüssel, lege einen Job an, frage ab und lade sauberes Markdown, mit einem kompletten, kopierbaren Beispiel und ordentlicher Fehlerbehandlung.
Ein Schlüssel, drei Calls
Um eine PDF mit Python in Markdown umzuwandeln, rufst du eine kleine REST-API mit einem Bearer-Schlüssel auf: POST /api/v2/jobs, um einen Job anzulegen, GET /api/v2/jobs/{id}, um abzufragen, bis er fertig ist, und dann GET /api/v2/jobs/{id}/download für das Markdown. Es gibt nichts zu hosten und keine schwere Bibliothek zu installieren, nur requests. Derselbe Lebenszyklus ist als gehostetes MCP für Agenten verfügbar, und die Ausgabe ist dasselbe Markdown, das die Erweiterung und die Web-App erzeugen, sodass du im Browser prototypisieren und dann mit Vertrauen automatisieren kannst.
In vier Schritten einrichten
API-Schlüssel holen
Melde dich mit einem kostenlosen Google-Konto an und erstelle in deinem Konto einen API-Schlüssel; er wird nur einmal angezeigt. Sende ihn als Authorization: Bearer p2m_your_key.
Job anlegen
Schicke die PDF-URL (oder die hochgeladenen Bytes) per POST an /api/v2/jobs. Du bekommst eine Job-ID und einen Status zurück.
Bis ready abfragen
Mache GET /api/v2/jobs/{id}, bis status ready oder error ist.
Markdown herunterladen
Mache GET /api/v2/jobs/{id}/download für den Markdown-Text. Beachte truncated und pages.
Das vollständige Python-Skript
Standardbibliothek plus requests. Aus einer PDF-URL anlegen, abfragen und dann das Markdown speichern.
# pip install requests import requests, time API = "https://pdf2md.dev/api/v2" H = {"Authorization": "Bearer p2m_your_key"} # 1) create a job from a PDF URL r = requests.post(f"{API}/jobs", headers={**H, "Idempotency-Key": "report-2026-01"}, json={"url": "https://example.com/report.pdf"}) r.raise_for_status() jid = r.json()["job_id"] # 2) poll until ready or error while True: job = requests.get(f"{API}/jobs/{jid}", headers=H).json() if job["status"] in ("ready", "error"): break time.sleep(3) if job["status"] == "error": raise SystemExit(f"conversion failed: {job.get('error_code')} {job.get('error_message')}") # 3) download the Markdown md = requests.get(f"{API}/jobs/{jid}/download", headers=H).text if job.get("truncated"): print("note: partial result (hit the time budget)") open("report.md", "w").write(md)
Eine lokale Datei statt einer URL hochladen? Schicke die Bytes per POST als multipart/form-data mit einem file-Feld an denselben Endpoint. Die vollständigen Formen von Anfrage und Antwort stehen in der OpenAPI-Spezifikation.
Um viele PDF umzuwandeln, lege pro Datei einen Job an und frage sie parallel bis zu deinem Slot-Limit ab (3 im Gratis-Tarif, mehr in bezahlten). Halte pro Datei einen eigenen Idempotency-Key, damit Wiederholungen nie Arbeit doppeln, und warte bei einem 429 mithilfe des Retry-After-Headers.
Fehler, Wiederholungen und Webhooks
Lies den Fehlercode
Ein fehlgeschlagener Job liefert status: error mit einem maschinenlesbaren error_code (processing_timeout oder conversion_failed) und einer sicheren error_message: verzweige nach dem Code, nicht nach dem Text.
Behandle truncated
Ein langes Dokument kann mit truncated=true als ready zurückkommen. Prüfe das Flag und teile die Datei oder nutze ein größeres bezahltes Budget.
Idempotency-Key
Sende einen Idempotency-Key-Header, damit ein wiederholtes Create denselben Job zurückgibt, statt Arbeit zu doppeln.
Webhooks statt Abfragen
In bezahlten Tarifen registrierst du einen Webhook oder übergibst callback_url und bekommst einen POST bei ready/error statt abzufragen.
Respektiere 429
Bei einem 429 warte die Sekunden aus Retry-After, bevor du es erneut versuchst; überlaste die Warteschlange nicht.
Halte Schlüssel serverseitig
Der Schlüssel ist ein Geheimnis: speichere ihn serverseitig, sende ihn über TLS und rotiere oder widerrufe ihn jederzeit.
Nicht in Python? Dieselben drei Calls
Es ist eine simple HTTPS-API, also funktioniert jede Sprache. Dasselbe Anlegen / Abfragen / Herunterladen in Node:
// Node 18+ (fetch global) const API = "https://pdf2md.dev/api/v2"; const H = { Authorization: "Bearer p2m_your_key" }; let r = await fetch(`${API}/jobs`, { method: "POST", headers: { ...H, "Content-Type": "application/json" }, body: JSON.stringify({ url: "https://example.com/report.pdf" }) }); let { job_id } = await r.json(); let job; do { await new Promise(s => setTimeout(s, 3000)); job = await (await fetch(`${API}/jobs/${job_id}`, { headers: H })).json(); } while (!["ready", "error"].includes(job.status)); const md = await (await fetch(`${API}/jobs/${job_id}/download`, { headers: H })).text();
Lieber Agent-Tools oder RAG?
Derselbe Lebenszyklus ist ein gehostetes MCP für ChatGPT, Claude und Agent-Frameworks. Für Ingest und Chunking siehe die RAG-Anleitung.
Häufige Fragen
Wie wandle ich eine PDF mit Python in Markdown um?
Rufe die REST-API mit einem Bearer-Schlüssel auf: POST die PDF an /api/v2/jobs, frage GET /api/v2/jobs/{id} bis ready ab und mache dann GET auf /download für das Markdown. Das vollständige requests-Beispiel steht oben.
Brauche ich einen API-Schlüssel?
Für die API ja. Mit einem kostenlosen Google-Konto kannst du einen Schlüssel erstellen und das gehostete MCP nutzen. Die Browser-Erweiterung und die Web-App bleiben anonym und brauchen keinen Schlüssel.
Wie gehe ich mit Fehlern und Timeouts um?
Ein fehlgeschlagener Job liefert status: error mit einem maschinenlesbaren error_code (processing_timeout oder conversion_failed) und einer sicheren error_message. Ein langes Dokument kann mit truncated=true als ready zurückkommen; prüfe dieses Flag.
Kann ich das Abfragen vermeiden?
In bezahlten Tarifen registrierst du einen Webhook oder übergibst callback_url beim Anlegen des Jobs, und der Dienst macht POST bei ready oder error, sodass du nicht abfragst.
Funktioniert das aus Node oder anderen Sprachen?
Ja. Es ist eine simple HTTPS-API, also funktioniert jede Sprache. Oben steht ein kurzes Node-Beispiel, und der vollständige Vertrag steht in der OpenAPI-Spezifikation.
Wie wandle ich eine lokale Datei statt einer URL um?
Schicke die Datei-Bytes per POST als multipart/form-data mit einem file-Feld an /api/v2/jobs, statt eines JSON-Bodys mit url. Die Schritte zum Abfragen und Herunterladen sind identisch.
Kann ich viele PDF auf einmal umwandeln?
Lege pro Datei einen Job an und frage sie parallel bis zu deinem Slot-Limit ab (3 im Gratis-Tarif, mehr in bezahlten). Bezahlte Tarife ergänzen einen Endpoint zur Stapelanlage und Webhooks, sodass du gar nicht abfragst.
Ist die Ausgabe dieselbe wie bei der Erweiterung und der Web-App?
Ja. Jede Oberfläche nutzt dieselbe Konvertierungs-Engine, also liefert die API dasselbe Markdown, das du auch im Browser bekommen würdest.
Ist es kostenlos?
Der Gratis-Tarif bietet 3 Slots, Dateien bis 10 MB, ein Zeitbudget von 15 Minuten und 1 Stunde Aufbewahrung. Bezahlte Tarife heben jedes Limit an und ergänzen Webhooks und Stapelanlage.