> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abbyy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# REST API

Starten Sie den Container ohne Positionsargumente, läuft FineParser als HTTP-Server auf Port 8080. Er bleibt so lange aktiv, bis Sie ihn beenden. Verwenden Sie diesen Modus, wenn Ihre Anwendungen oder andere Services Documents über das Netzwerk an FineParser senden sollen.

```bash theme={null}
docker run -d --name fineparser -p 8080:8080 --stop-timeout 60 \
  -e FINEPARSER_LICENSE_DATA="$(cat acme.fineparserlicense)" \
  -v fineparser-output:/app/output \
  abbyyteam/fineparser
```

Erkennungsmodus, Sprachen und die Lizenz werden beim Start des Containers festgelegt, daher enthalten Anfragen weder Anmeldedaten noch Erkennungseinstellungen. Die Ergebnisse werden nach `/app/output` geschrieben – binden Sie dort also ein Volume ein, sonst gehen sie zusammen mit dem Container verloren. Siehe [Konfiguration](/de/fine-parser/basics/configuration).

<h2 id="how-a-parse-works">
  Ablauf eines Parsing-Vorgangs
</h2>

Das Parsing erfolgt asynchron. Sie übermitteln ein Document und erhalten sofort eine Job-ID zurück, fragen den Job ab, bis er einen Endzustand erreicht hat, und laden anschließend das Ergebnis herunter. Die Erkennung läuft auf einem einzelnen Worker innerhalb des Containers, und zwar immer nur ein Document gleichzeitig, sodass die HTTP-Verbindung nie über die gesamte Dauer einer Erkennung offen gehalten wird.

<Steps>
  <Step title="Übermitteln">
    `POST /parse` mit dem Dokument und einem Ausgabetyp. Die Antwort lautet `202 Accepted` und enthält die Job-ID.
  </Step>

  <Step title="Abfragen">
    `GET /jobs/{id}`, bis `status` den Wert `successful`, `failed` oder `cancelled` hat.
  </Step>

  <Step title="Herunterladen">
    `GET /jobs/{file}`, wobei `file` der in der Statusantwort angegebene Pfad ist. Das Ergebnis wird als Attachment zurückgestreamt.
  </Step>
</Steps>

<CodeGroup>
  ```bash cURL theme={null}
  # Übermitteln
  curl -s -X POST http://localhost:8080/parse \
    -F "file=@document.pdf" \
    -F "outputType=doclang"
  # {"jobId":"6c4f1f0e-...","file":"document.pdf"}

  # Abfragen
  curl -s http://localhost:8080/jobs/6c4f1f0e-...
  # {"status":"successful","file":"6c4f1f0e-.../document.pdf.doclang"}

  # Herunterladen
  curl -OJ http://localhost:8080/jobs/6c4f1f0e-.../document.pdf.doclang
  ```

  ```python Python theme={null}
  import time
  import requests

  BASE = "http://localhost:8080"

  with open("document.pdf", "rb") as f:
      submitted = requests.post(
          f"{BASE}/parse",
          files={"file": f},
          data={"outputType": "doclang"},
      )
  submitted.raise_for_status()
  job_id = submitted.json()["jobId"]

  while True:
      job = requests.get(f"{BASE}/jobs/{job_id}").json()
      if job["status"] not in ("pending", "in-progress"):
          break
      time.sleep(1)

  if job["status"] != "successful":
      raise RuntimeError(job.get("error", job["status"]))

  result = requests.get(f"{BASE}/jobs/{job['file']}")
  result.raise_for_status()
  with open("document.doclang", "wb") as out:
      out.write(result.content)
  ```

  ```javascript Node.js theme={null}
  import { openAsBlob } from "node:fs";
  import { writeFile } from "node:fs/promises";
  import { setTimeout as sleep } from "node:timers/promises";

  const BASE = "http://localhost:8080";

  const form = new FormData();
  form.append("file", await openAsBlob("document.pdf"), "document.pdf");
  form.append("outputType", "doclang");

  const submitted = await fetch(`${BASE}/parse`, { method: "POST", body: form });
  if (!submitted.ok) throw new Error(await submitted.text());
  const { jobId } = await submitted.json();

  let job;
  do {
    await sleep(1000);
    job = await (await fetch(`${BASE}/jobs/${jobId}`)).json();
  } while (job.status === "pending" || job.status === "in-progress");

  if (job.status !== "successful") throw new Error(job.error ?? job.status);

  const result = await fetch(`${BASE}/jobs/${job.file}`);
  await writeFile("document.doclang", Buffer.from(await result.arrayBuffer()));
  ```
</CodeGroup>

<h2 id="endpoints">
  Endpunkte
</h2>

Jede Fehlerantwort ist ein JSON-Objekt mit einem Feld `error`, das das Problem beschreibt. Einige enthalten zusätzlich ein Feld `code` oder `status`, wie unten angegeben.

<h3 id="post-parse">
  POST /parse
</h3>

Übermitteln Sie ein Document. Senden Sie es als `multipart/form-data` mit einem Feld `file`, das das Document enthält, und einem Feld `outputType` mit dem Wert `doclang`, `json` oder `txt`. Siehe [Ausgabeformate](/de/fine-parser/basics/output-formats).

Bei erfolgreicher Übermittlung werden `202 Accepted`, ein `Location`-Header, der auf den Job verweist, sowie ein Body mit der Job-ID und dem von FineParser erfassten Dateinamen zurückgegeben.

```json theme={null}
{"jobId":"6c4f1f0e-...","file":"document.pdf"}
```

| Status | Ursache                                                                                                                                                                                        |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `202`  | Angenommen. `/jobs/{id}` abfragen.                                                                                                                                                             |
| `400`  | `outputType` fehlt oder ist weder `doclang`, `json` noch `txt`, oder die Anfrage enthält nicht genau einen Part mit dem Namen `file`.                                                          |
| `402`  | Ihr Tarif enthält keine verbleibenden Seiten mehr. Der Body enthält `code: out_of_credits`. Siehe [Limit erreicht](/de/fine-parser/basics/licenses-and-plans#reaching-your-limit).             |
| `403`  | Der Container hat keine Lizenz oder die Lizenz ist nicht bereitgestellt. Der Body enthält `code: no_instance` oder `code: not_provisioned`.                                                    |
| `413`  | Der Anfrage-Body überschreitet das Upload-Limit von standardmäßig 1 GiB. Siehe [Konfiguration](/de/fine-parser/basics/configuration).                                                          |
| `503`  | Der Lizenzserver ist nicht erreichbar oder hat einen erneuten Versuch angefordert. Der Body enthält `code: server_unreachable`, `rate_limited` oder `unknown`. Versuchen Sie es später erneut. |

Die Lizenzprüfung erfolgt, bevor der Upload überhaupt eingelesen wird. Ein nicht lizenzierter Container weist die Anfrage daher ab, ohne dafür Bandbreite oder Festplattenspeicher zu verbrauchen.

<h3 id="get-jobsid">
  GET /jobs/{id}
</h3>

Gibt den Status eines Jobs zurück.

```json theme={null}
{"status":"successful","file":"6c4f1f0e-.../document.pdf.doclang"}
```

| `status`      | Bedeutung                                                                                                             |
| ------------- | --------------------------------------------------------------------------------------------------------------------- |
| `pending`     | In der Warteschlange, wartet auf den Worker.                                                                          |
| `in-progress` | Wird erkannt.                                                                                                         |
| `successful`  | Abgeschlossen. `file` ist vorhanden, solange das Ergebnis auf der Festplatte liegt.                                   |
| `failed`      | Nicht abgeschlossen. `error` enthält den Grund.                                                                       |
| `cancelled`   | Der Container wurde heruntergefahren, während dieses Document erkannt wurde. Endgültig. Bei Bedarf erneut einreichen. |

`file` ist der Pfad des Ergebnisses in der Form `{jobId}/{name}`. Stellen Sie `/jobs/` voran, um die Download-URL zu erhalten. Der Name entspricht dem hochgeladenen Dateinamen mit angehängtem Ausgabetyp: Aus `document.pdf`, zu DocLang geparst, wird also `document.pdf.doclang`. Das Feld fehlt, bis der Job erfolgreich war, und verschwindet wieder, wenn das Ergebnis von der Festplatte entfernt wird.

Ein Job kann auch nach der Annahme aus Lizenzgründen fehlschlagen, da die Seitenzahl erst bekannt ist, sobald das Document geladen wurde. In diesem Fall lautet `status` `failed` und `error` weist darauf hin. Für einen fehlgeschlagenen Job wird nichts berechnet.

Gibt `404` zurück, wenn kein solcher Job existiert.

<h3 id="get-jobsidname">
  GET /jobs/{id}/{name}
</h3>

Ein Ergebnis herunterladen. Verwenden Sie den Wert `file` aus der Status-Antwort als Pfad.

Das Ergebnis wird mit einem zum Ausgabeformat passenden `Content-Type` und einem `Content-Disposition`-Header vom Typ `attachment` zurückgestreamt, der den Dateinamen angibt, sodass `curl -OJ` die Datei unter dem richtigen Namen speichert. Bereichsanfragen werden nicht unterstützt.

| Status | Ursache                                                                                                  |
| ------ | -------------------------------------------------------------------------------------------------------- |
| `200`  | Das Ergebnis.                                                                                            |
| `404`  | Kein solcher Job vorhanden oder der Name gehört nicht zum Ergebnis dieses Jobs.                          |
| `409`  | Der Job war nicht erfolgreich. Der Body enthält `status` und, falls der Job fehlgeschlagen ist, `error`. |
| `410`  | Der Job war erfolgreich, aber sein Ergebnis befindet sich nicht mehr auf der Festplatte.                 |

<h3 id="delete-jobsid">
  DELETE /jobs/{id}
</h3>

Entfernt einen Job und dessen Ergebnis von der Festplatte.

| Status | Ursache                                                                                                         |
| ------ | --------------------------------------------------------------------------------------------------------------- |
| `204`  | Gelöscht.                                                                                                       |
| `404`  | Kein solcher Job vorhanden.                                                                                     |
| `409`  | Der Job wird gerade erkannt und kann nicht gelöscht werden. Warten Sie, bis die Verarbeitung abgeschlossen ist. |

<h3 id="get-healthz">
  GET /healthz
</h3>

Gibt an, ob der Container lizenziert und bereit ist, Documents entgegenzunehmen. Verwenden Sie diesen Endpunkt als Readiness-Probe in Kubernetes oder in Ihrem Orchestrator. Das Container-Image führt ihn außerdem als eigenen Docker-`HEALTHCHECK` aus.

```bash theme={null}
curl -s localhost:8080/healthz
# {"code":"ok","ready":true,"telemetry":true}
```

Ein betriebsbereiter Container gibt `200` zurück. Ein nicht betriebsbereiter Container gibt `503` zurück – mit `ready: false`, einem `code`, der den Grund angibt, und in der Regel einer `detail`-Meldung. Die Bereitschaft richtet sich ausschließlich nach der Lizenzierung. Telemetriedaten werden zwar mitgemeldet, haben darauf aber keinerlei Einfluss. Siehe [Lizenz bestätigen](/de/fine-parser/basics/configuration#confirming-the-license).

<h2 id="results-on-disk">
  Ergebnisse auf der Festplatte
</h2>

Jedes Ergebnis wird unter `/app/output` als `{jobId}/{name}` abgelegt, direkt neben einer kleinen Job-Datenbank. Das Layout ist stabil, sodass Sie Ergebnisse auch direkt vom eingebundenen Volume abholen können, statt sie über HTTP herunterzuladen.

Standardmäßig bleibt ein Ergebnis erhalten, bis Sie den Job per `DELETE` entfernen oder ihn selbst vom Volume löschen. Nichts verfällt aufgrund seines Alters. Wenn stattdessen FineParser aufräumen soll, starten Sie den Container mit `DELETE_ON_DOWNLOAD=true`; dann wird jedes Ergebnis samt zugehörigem Job-Datensatz gelöscht, sobald es vollständig heruntergeladen wurde. Danach geben sowohl der Status als auch der Download `404` zurück. Bei einem abgebrochenen Download bleibt das Ergebnis für einen erneuten Versuch erhalten.

Uploads werden in einem separaten temporären Verzeichnis abgelegt und gelöscht, sobald die Recognition abgeschlossen ist – unabhängig vom Ausgang.

<h2 id="restarts-and-shutdown">
  Neustarts und Herunterfahren
</h2>

Jobs werden in der Job-Datenbank auf dem Ausgabe-Volume erfasst und überstehen daher einen Neustart, solange das Volume erhalten bleibt. Ausstehende Jobs werden erneut aufgenommen. Ein Job, der zum Zeitpunkt des Prozessabbruchs gerade ausgeführt wurde, wird als `failed` gekennzeichnet, da der Prozess nicht wissen kann, wie viel des Ergebnisses bereits geschrieben wurde.

Bei einem sauberen Stopp nimmt FineParser keine Anfragen mehr an, lässt laufende Anfragen zu Ende laufen und beendet sich, ohne auf das Dokument zu warten, das gerade erkannt wird. Die Erkennung kann nicht unterbrochen werden, und das Warten darauf könnte das Herunterfahren um Minuten hinauszögern. Dieser Job wird als `cancelled` gekennzeichnet und sein Teilergebnis wird entfernt. Reichen Sie ihn erneut ein, wenn Sie ihn weiterhin benötigen.

Legen Sie für den Container ein Stopp-Timeout von 60 Sekunden fest – mit `--stop-timeout 60` in Docker oder `terminationGracePeriodSeconds: 60` in Kubernetes. Der Docker-Standardwert von 10 Sekunden bricht das Auslaufen vorzeitig ab, wodurch die Nutzungstelemetrie seit dem letzten Export verloren geht.

Ein Ausgabeverzeichnis kann immer nur von einem Container verwendet werden. Die Job-Datenbank ist im geöffneten Zustand gesperrt, sodass ein zweiter Container auf demselben Volume den Start verweigert. Zwei Container benötigen zwei Volumes.
