pdf-api

HTML zu PDF, gerendert mit mPDF. Gedacht für den HTTP-Request-Node in n8n.

Endpunkte

POST/convertHTML in ein PDF umwandeln
GET/healthStatus und DB-Verbindung
GET/files/{token}Download bei output=url

Das 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

FeldDefaultBedeutung
htmlPflichtfeld. Vollständiges HTML-Dokument oder ein Fragment.
filenamedocument.pdfDateiname in der Antwort.
outputbinarybinary liefert die PDF-Datei, base64 ein JSON mit data, url einen temporären Download-Link.
inlinefalseBei binary: im Browser anzeigen statt herunterladen.
options{}Rendering-Optionen, siehe unten.

options

OptionDefaultBedeutung
formatA4A0–A8, B0–B6, Letter, Legal, Ledger, Tabloid, Executive, Folio – oder eigenes Maß in Millimetern, z. B. "210x297".
orientationportraitportrait oder landscape.
margin_top, margin_right, margin_bottom, margin_left16 / 15 / 16 / 15Seitenränder in Millimetern.
margin_header, margin_footer9Abstand von Kopf- und Fußzeile zum Blattrand.
header_htmlHTML für die Kopfzeile jeder Seite.
footer_htmlHTML für die Fußzeile. {PAGENO} und {nbpg} werden ersetzt.
page_numbersfalseSetzt eine schlichte Fußzeile „Seite / Gesamt“, wenn kein footer_html gesetzt ist.
cssZusätzliches Stylesheet, wird vor dem HTML geladen.
default_fontdejavusansBasis-Schrift, z. B. dejavuserif, helvetica, times.
default_font_size10Basis-Schriftgröße in pt.
watermarkText-Wasserzeichen, z. B. "ENTWURF".
watermark_imageBild-URL als Wasserzeichen.
watermark_alpha0.1Deckkraft 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, keywordsPDF-Metadaten.
dpi, img_dpi96Auflö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." } }
StatusWann
401API-Key fehlt oder ist falsch
403API-Key ist deaktiviert
413Body größer als 8 MB
422html fehlt, oder eine Option ist ungültig
429Stündliches Limit des Keys erreicht

Nutzung in n8n

Ein HTTP Request-Node genügt:

EinstellungWert
MethodPOST
URLhttp://pdf-api.localhost/convert
AuthenticationGeneric → Header Auth, Name X-API-Key, Value = dein Key
Send Bodyan, Body Content Type JSON
ResponseFormat 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.