pdf-api
HTML zu PDF, gerendert mit mPDF. Gedacht für den HTTP-Request-Node in n8n.
Endpunkte
output=urlDas Präfix /api/v1 ist optional: /api/v1/convert trifft dieselbe Route.
Authentifizierung
Jeder Aufruf von /convert braucht einen API-Key im Header X-API-Key. Alternativ geht Authorization: Bearer <key>.
curl -X POST http://pdf-api.localhost/convert \
-H "X-API-Key: pdf_..." \
-H "Content-Type: application/json" \
-d '{"html":"<h1>Hallo Welt</h1>","filename":"test.pdf"}' \
--output test.pdf
Request
Der Body kann JSON, Formulardaten oder rohes HTML sein. Bei rohem HTML (Content-Type: text/html) werden Optionen aus dem Query-String gelesen.
{
"html": "<h1>Rechnung 2026-001</h1>",
"filename": "rechnung.pdf",
"output": "binary",
"options": {
"format": "A4",
"orientation": "portrait",
"margin_top": 20,
"page_numbers": true,
"header_html": "<div style='text-align:right'>Meine Firma GmbH</div>",
"title": "Rechnung 2026-001"
}
}
Felder auf oberster Ebene
| Feld | Default | Bedeutung |
|---|---|---|
html | – | Pflichtfeld. Vollständiges HTML-Dokument oder ein Fragment. |
filename | document.pdf | Dateiname in der Antwort. |
output | binary | binary liefert die PDF-Datei, base64 ein JSON mit data, url einen temporären Download-Link. |
inline | false | Bei binary: im Browser anzeigen statt herunterladen. |
options | {} | Rendering-Optionen, siehe unten. |
options
| Option | Default | Bedeutung |
|---|---|---|
format | A4 | A0–A8, B0–B6, Letter, Legal, Ledger, Tabloid, Executive, Folio – oder eigenes Maß in Millimetern, z. B. "210x297". |
orientation | portrait | portrait oder landscape. |
margin_top, margin_right, margin_bottom, margin_left | 16 / 15 / 16 / 15 | Seitenränder in Millimetern. |
margin_header, margin_footer | 9 | Abstand von Kopf- und Fußzeile zum Blattrand. |
header_html | – | HTML für die Kopfzeile jeder Seite. |
footer_html | – | HTML für die Fußzeile. {PAGENO} und {nbpg} werden ersetzt. |
page_numbers | false | Setzt eine schlichte Fußzeile „Seite / Gesamt“, wenn kein footer_html gesetzt ist. |
css | – | Zusätzliches Stylesheet, wird vor dem HTML geladen. |
default_font | dejavusans | Basis-Schrift, z. B. dejavuserif, helvetica, times. |
default_font_size | 10 | Basis-Schriftgröße in pt. |
watermark | – | Text-Wasserzeichen, z. B. "ENTWURF". |
watermark_image | – | Bild-URL als Wasserzeichen. |
watermark_alpha | 0.1 | Deckkraft des Wasserzeichens. |
password | – | Öffnungspasswort. Setzt zugleich den Kopierschutz. |
permissions | ["print"] | Erlaubte Aktionen bei gesetztem Passwort: copy, print, modify, extract, assemble, fill-forms, annot-forms, print-highres. |
title, author, subject, keywords | – | PDF-Metadaten. |
dpi, img_dpi | 96 | Auflösung für Layout beziehungsweise Bilder. |
format, orientation, title, author, css, header_html, footer_html, page_numbers, watermark und password dürfen auch direkt neben html stehen, ohne das options-Objekt.
Antwort
Bei output: "binary" kommt die PDF-Datei direkt zurück (Content-Type: application/pdf). Sonst ein JSON-Objekt:
{
"success": true,
"filename": "rechnung.pdf",
"pages": 2,
"size": 48213,
"url": "http://pdf-api.localhost/files/9f2c...",
"expires_at": "2026-08-28T14:05:12+02:00"
}
Fehler kommen als JSON mit passendem HTTP-Status:
{ "success": false, "error": { "code": "invalid_api_key", "message": "API-Key ungueltig." } }
| Status | Wann |
|---|---|
| 401 | API-Key fehlt oder ist falsch |
| 403 | API-Key ist deaktiviert |
| 413 | Body größer als 8 MB |
| 422 | html fehlt, oder eine Option ist ungültig |
| 429 | Stündliches Limit des Keys erreicht |
Nutzung in n8n
Ein HTTP Request-Node genügt:
| Einstellung | Wert |
|---|---|
| Method | POST |
| URL | http://pdf-api.localhost/convert |
| Authentication | Generic → Header Auth, Name X-API-Key, Value = dein Key |
| Send Body | an, Body Content Type JSON |
| Response | Format File, Put Output in Field data |
Im Body-Feld html lässt sich ein Expression-Wert einsetzen, etwa {{ $json.rechnungHtml }}. Das Ergebnis liegt danach als Binary im Feld data und kann direkt an „Send Email“, Google Drive oder „Write Binary File“ weitergereicht werden.
Läuft n8n in Docker, ist pdf-api.localhost von dort nicht erreichbar. Dann http://host.docker.internal/convert verwenden und im Request den Header Host: pdf-api.localhost mitgeben, damit Apache den richtigen vHost trifft.
Grenzen des Renderers
mPDF versteht klassisches CSS zuverlässig: Tabellen, Floats, absolute Positionierung, Seitenumbrüche über page-break-*, Kopf- und Fußzeilen. Flexbox und CSS Grid unterstützt es nicht – Layouts also mit Tabellen oder Floats bauen. Webfonts über @font-face funktionieren, brauchen aber eine erreichbare TTF-Datei.