optii by chut.app

Ein Endpunkt.
Ein Header.
Eine Datei.

Alles hier unten ist gegen die laufende API verifiziert. Basis-URL https://api.optii.eu

Authentifizierung

Senden Sie Ihren Schlüssel im Header X-API-Key. Schlüssel beginnen mit optii_sk_ und werden einmal gezeigt, bei der Erstellung. Schlüssel werden gehasht gespeichert; ein verlorener Schlüssel wird rotiert, nicht wiederhergestellt.

Jede Antwort trägt einen X-Request-ID-Header. Nennen Sie ihn, wenn Sie uns schreiben.

curl https://api.optii.eu/ocr/extract \
  -H "X-API-Key: optii_sk_…" \
  -F "file=@document.pdf"

POST/ocr/extract

Multipart-Upload einer Datei. Liefert jede Zeile mit Bounding-Box und Konfidenz, in Lesereihenfolge, Seite für Seite.

Query-Parameter

structured
false (Standard) für Zeilen, true für belgische Dokumentfelder (nächster Abschnitt).
format
v2 (Standard). legacy liefert die flache Rechnungsform der Clients von 2025; nicht für neuen Code verwenden.

Antwort 200

{
  "success": true,
  "text": "DUMONT & FILS SPRL\nRue de la Loi 42 · 1000 Bruxelles\nTVA BE0202239951\n…",
  "lines": [
    { "text": "DUMONT & FILS SPRL", "confidence": 0.996, "bbox": [[50.0, 50.0], [304.0, 50.0], [304.0, 84.0], [50.0, 84.0]] },
    { "text": "Rue de la Loi 42 · 1000 Bruxelles", "confidence": 0.991, "bbox": [[56.0, 82.0], [286.0, 82.0], [286.0, 100.0], [56.0, 100.0]] },
    { "text": "TVA BE0202239951", "confidence": 0.993, "bbox": [[56.0, 106.0], [198.0, 106.0], [198.0, 124.0], [56.0, 124.0]] }
  ],
  "confidence": 0.991,
  "source": "ocr",
  "quality": { "blur": 812.4, "long_side_px": 2339, "low_confidence_ratio": 0.0, "warnings": [] },
  "file_hash": "a3b2c1d4e5f6",
  "processing_time_ms": 342
}

Felder

text
der volle Text, Zeilen mit Zeilenumbrüchen verbunden
lines[]
ein Objekt pro Zeile: text, confidence (0 bis 1), bbox als vier Eckpunkte [[x, y], …] in Seitenpixeln, im Uhrzeigersinn ab oben links
confidence
mittlere Zeilenkonfidenz
source
text, ocr oder hybrid, siehe Quelle und Qualität
quality
blur, long_side_px, low_confidence_ratio, warnings[]
file_hash
SHA-256 der Datei, erste Zeichen; dieselbe Datei ergibt immer denselben Wert
processing_time_ms
Serverzeit in Millisekunden, ohne Upload

POST/ocr/extract?structured=true

Derselbe Aufruf mit ?structured=true. Die Engine erkennt die Dokumentart aus dem Text (invoice, credit_note, debit_note, receipt, sonst unknown), extrahiert die belgischen Felder und führt die Prüfungen aus. Ist der Klassifikator sicher, trägt document_type stattdessen dessen Label.

Ein Feld, das nicht auf der Seite steht, ist null. Beträge sind Zahlen mit Punkt, Daten bleiben wie gedruckt (dd/mm/yyyy auf den meisten belgischen Belegen).

{
  "document_type": "invoice",
  "source": "ocr",
  "quality": { "blur": 812.4, "long_side_px": 2339, "low_confidence_ratio": 0.0, "warnings": [] },
  "supplier_name": "DUMONT & FILS SPRL",
  "supplier_vat": "BE0202239951",
  "supplier_iban": "BE68539007547034",
  "document_number": "2024-0847",
  "document_date": "15/03/2024",
  "due_date": "14/04/2024",
  "structured_communication": "+++123/4567/89002+++",
  "currency": "EUR",
  "line_items": [
    { "description": "Conseil juridique — mars 2024", "quantity": 10, "unit_price": 125.0, "line_total": 1250.0, "vat_rate": 21.0 }
  ],
  "vat_subtotals": [ { "rate": 21.0, "base_amount": 1250.0, "vat_amount": 262.5 } ],
  "total_excl_vat": 1250.0,
  "total_vat": 262.5,
  "total_incl_vat": 1512.5,
  "validations": [
    { "check": "vat_checkdigit", "passed": true, "message": "" },
    { "check": "iban_mod97", "passed": true, "message": "" },
    { "check": "structured_comm_mod97", "passed": true, "message": "" },
    { "check": "totals_sum", "passed": true, "message": "" }
  ],
  "confidence": 0.91,
  "processing_time_ms": 342
}

Prüfungen

vat_checkdigit
die Prüfziffern der belgischen MwSt.-Nummer (97 minus die ersten acht Ziffern mod 97)
iban_mod97
die umgestellte IBAN mod 97 ergibt 1
structured_comm_mod97
die Prüfziffern der strukturierten Mitteilung +++xxx/xxxx/xxxxx+++
totals_sum · vat_subtotals_sum · vat_bases_sum
total_excl_vat + total_vat = total_incl_vat auf einen Cent genau
line_items_sum
die Positionen ergeben die Summe ohne MwSt., wenn vorhanden

POST/ocr/classify

Multipart-Upload einer Datei. Der Text wird gelesen (Textebene oder OCR) und von einem XLM-RoBERTa-Modell in einen von 32 belgischen Dokumenttypen eingeordnet.

Query-Parameter

top_k
Anzahl zurückgegebener Vorhersagen, 1 bis 20, Standard 5.

Antwort 200

{
  "success": true,
  "document_type": "acc_purchase_invoice",
  "confidence": 0.9361,
  "confident": true,
  "predictions": [
    { "label": "acc_purchase_invoice", "confidence": 0.9361, "rank": 1 },
    { "label": "acc_sales_invoice", "confidence": 0.0298, "rank": 2 },
    { "label": "acc_credit_note", "confidence": 0.0112, "rank": 3 }
  ],
  "processing_time_ms": 388
}
document_type
das Label, wenn confident true ist, sonst unknown
confident
true, wenn die höchste Wahrscheinlichkeit die Schwelle erreicht und das Label kein Fallback ist
predictions[]
label, confidence, rank, bestes zuerst

Labels sind snake_case mit Familienpräfix, zum Beispiel acc_purchase_invoice oder acc_sales_invoice. Die vollständige Liste steht im Dashboard.

Quelle und Qualität

Jede extract-Antwort sagt, woher der Text stammt.

text
das PDF hatte eine brauchbare Textebene; Zeichen sind exakt, jede Zeile hat Konfidenz 1
ocr
jede Seite lief durch die Engine
hybrid
manche Seiten hatten eine Ebene, manche nicht

quality quality wird auf den Seiten berechnet, die durch OCR liefen:

blur
Varianz des Laplace-Operators auf einem auf 1500 px verkleinerten Graustufenbild; niedriger ist unschärfer
long_side_px
die kleinste lange Seitenkante in Pixeln
low_confidence_ratio
Anteil der Zeilen unter 0.80 Konfidenz
warnings[]
eines von low_resolution (unter 1500 px), blurry (unter 400), low_confidence (Anteil über 0.15)

Ist warnings nicht leer, zeigen Sie dem Nutzer die Seite und verlangen Sie einen neuen Scan. Die Zahlen können trotzdem stimmen; wissen können Sie es nicht.

Idempotenz

Senden Sie einen Idempotency-Key-Header (beliebiger String bis 255 Zeichen, eine UUID ist in Ordnung). 24 Stunden lang liefert derselbe Key mit derselben Datei und denselben Parametern die gespeicherte Antwort, ohne zweites Lesen und ohne zweite Zählung. Derselbe Key mit einer anderen Datei antwortet 409 idempotency_conflict.

Der Fingerabdruck ist der SHA-256 der Datei plus Parameter, sodass ein wiederholter Multipart-Upload auch bei anderer Boundary passt.

Rate-Limits und Kontingente

Limits gelten pro Tarif und pro Schlüssel. Die Tabelle ist die Live-Konfiguration.

TarifAnfragen / minMax. DateiSchlüsselMonatskontingent
Free105 MB1500
Pro6020 MB510.000
Enterprise30020 MB100unbegrenzt

Über dem Rate-Limit: 429 rate_limited mit Retry-After. Über dem Kontingent: 429 mit einer Meldung, die das Kontingent nennt. Der öffentliche Demo-Endpunkt erlaubt 10 Anfragen pro Minute pro IP und 5 MB.

Fehler

Jeder Fehler ist JSON mit zwei Feldern: error, ein snake_case-Code, und message, ein Satz für Menschen. Parsen Sie die message nie.

HTTP/1.1 413 Payload Too Large
X-Request-ID: 6f1c0a2b9d3e
Content-Type: application/json

{ "error": "file_too_large", "message": "File too large (max 5MB)" }
StatuserrorWannWas tun
400unsupported_file_typeEndung nicht in pdf png jpg jpeg tiff tif bmp webpDatei konvertieren
401unauthorizedfehlender oder unbekannter SchlüsselSchlüssel im Dashboard prüfen
403forbiddender Tarif erlaubt es nicht (Schlüssellimit, Größe)siehe Preise
409idempotency_conflictgleicher Idempotency-Key, andere Dateineuen Key verwenden
413file_too_largeüber der max. Dateigröße des Tarifskomprimieren oder aufteilen
422validation_failedkein Dateifeld, falscher Query-ParameterAbschnitt zur Anfrage lesen
429rate_limitedRate-Limit oder MonatskontingentRetry-After abwarten oder upgraden
500processing_faileddie Engine konnte die Datei nicht leseneinmal wiederholen, dann mit der Request-ID an uns schreiben
503model_unavailabledie Engine lädt nach einem Neustartin ein paar Sekunden wiederholen
503db_unavailableNutzungsdatenbank nicht erreichbar; Lesen funktioniert weiterwiederholen

Formate und Limits

Eingabe: PDF (Text oder gescannt, mehrseitig), PNG, JPG, TIFF, BMP, WebP. Max. 20 MB mit Schlüssel, 5 MB in der Demo. Gelesene Sprachen: Französisch, Niederländisch, Deutsch, Englisch; die Engine beherrscht 50 Schriften, aber Feldextraktion und Klassifikator sind belgisch.

Ausgabe: JSON, UTF-8. Die Zeilenreihenfolge ist die Lesereihenfolge pro Seite. Rahmen sind Polygone im Pixelraum der gerenderten Seite.

Snippets

Kein SDK zu installieren. Drei Snippets, die so funktionieren, wie sie sind.

curl -X POST "https://api.optii.eu/ocr/extract?structured=true" \
  -H "X-API-Key: optii_sk_…" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "file=@facture-2024-0847.pdf"

OpenAPI

Der maschinenlesbare Vertrag liegt unter https://api.optii.eu/openapi.json. Es ist das kuratierte Tool-Manifest der chut.app-Plattform; die Dashboard-Routen sind nicht enthalten.

Changelog

Zum Changelog →