> ## 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

Inicie el contenedor sin argumentos posicionales y FineParser se ejecutará como un servidor HTTP en el puerto 8080. Permanecerá activo hasta que lo detenga. Utilice este modo cuando sus aplicaciones u otros servicios necesiten enviar documentos a FineParser a través de la red.

```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
```

El modo de reconocimiento, los idiomas y la License se definen al iniciar el contenedor, por lo que las solicitudes no llevan credenciales ni ajustes de reconocimiento. Los resultados se escriben en `/app/output`, por lo que debe montar un volumen allí; de lo contrario, desaparecerán junto con el contenedor. Consulte [Configuration](/es/fine-parser/basics/configuration).

<h2 id="how-a-parse-works">
  Cómo funciona un análisis
</h2>

El análisis es asíncrono: se envía un documento y se recibe de inmediato un ID de trabajo, se consulta el trabajo hasta que finalice y luego se descarga el resultado. El reconocimiento se realiza en un único worker dentro del contenedor, un documento a la vez, por lo que la conexión HTTP nunca permanece abierta mientras dura el reconocimiento.

<Steps>
  <Step title="Enviar">
    `POST /parse` con el documento y un tipo de salida. La respuesta es `202 Accepted` con el ID del trabajo.
  </Step>

  <Step title="Consultar">
    `GET /jobs/{id}` hasta que `status` sea `successful`, `failed` o `cancelled`.
  </Step>

  <Step title="Descargar">
    `GET /jobs/{file}`, donde `file` es la ruta indicada en la respuesta de estado. El resultado se devuelve como archivo adjunto.
  </Step>
</Steps>

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

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

  # Descargar
  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">
  Endpoints
</h2>

Toda respuesta de error es un JSON con un campo `error` que describe el problema. Algunas incluyen además un campo `code` o `status`, como se indica a continuación.

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

Envíe un documento. Envíelo como `multipart/form-data` con un campo `file` que contenga el documento y un campo `outputType` con el valor `doclang`, `json` o `txt`. Consulte [Formatos de salida](/es/fine-parser/basics/output-formats).

Un envío correcto devuelve `202 Accepted`, un encabezado `Location` que apunta al trabajo y un cuerpo con el ID del trabajo y el nombre de archivo registrado por FineParser.

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

| Estado | Causa                                                                                                                                                                             |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `202`  | Aceptado. Sondee `/jobs/{id}`.                                                                                                                                                    |
| `400`  | Falta `outputType` o su valor no es `doclang`, `json` ni `txt`, o la solicitud no contiene exactamente una parte llamada `file`.                                                  |
| `402`  | Su plan no tiene páginas disponibles. El cuerpo incluye `code: out_of_credits`. Consulte [Alcanzar el límite](/es/fine-parser/basics/licenses-and-plans#reaching-your-limit).     |
| `403`  | El contenedor no tiene licencia o la licencia no está aprovisionada. El cuerpo incluye `code: no_instance` o `code: not_provisioned`.                                             |
| `413`  | El cuerpo de la solicitud supera el límite de carga, que de forma predeterminada es de 1 GiB. Consulte [Configuration](/es/fine-parser/basics/configuration).                     |
| `503`  | No se puede contactar con el license server o este solicitó un reintento. El cuerpo incluye `code: server_unreachable`, `rate_limited` o `unknown`. Inténtelo de nuevo más tarde. |

La comprobación de licencia se realiza antes de leer nada de la carga, de modo que un contenedor sin licencia rechaza la solicitud sin consumir ancho de banda ni disco.

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

Informa el estado de un trabajo.

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

| `status`      | Significado                                                                                                      |
| ------------- | ---------------------------------------------------------------------------------------------------------------- |
| `pending`     | En cola, a la espera del worker.                                                                                 |
| `in-progress` | En proceso de reconocimiento.                                                                                    |
| `successful`  | Finalizado. `file` está presente mientras el resultado permanece en disco.                                       |
| `failed`      | No finalizó. `error` indica el motivo.                                                                           |
| `cancelled`   | El contenedor se apagó mientras reconocía este documento. Estado terminal. Vuelva a enviarlo si aún lo necesita. |

`file` es la ruta del resultado con el formato `{jobId}/{name}`. Anteponga `/jobs/` para obtener la URL de descarga. El nombre corresponde al del archivo cargado con el tipo de salida añadido al final, de modo que `document.pdf` analizado a DocLang pasa a ser `document.pdf.doclang`. El campo no aparece hasta que el trabajo se completa correctamente, y vuelve a desaparecer si el resultado se elimina del disco.

Un trabajo puede fallar por motivos de licencia después de haber sido aceptado, ya que el número de páginas solo se conoce una vez cargado el documento. En ese caso, `status` es `failed` y `error` así lo indica. No se cobra nada por un trabajo fallido.

Devuelve `404` si no existe dicho trabajo.

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

Descarga un resultado. Use el valor `file` de la respuesta de estado como ruta.

El resultado se devuelve como flujo con un `Content-Type` acorde al formato de salida y un encabezado `Content-Disposition` de tipo attachment que indica el nombre del archivo, de modo que `curl -OJ` lo guarda con el nombre correcto. No se admiten solicitudes por rangos.

| Estado | Causa                                                                                                  |
| ------ | ------------------------------------------------------------------------------------------------------ |
| `200`  | El resultado.                                                                                          |
| `404`  | No existe ese trabajo, o el nombre no corresponde al resultado de este trabajo.                        |
| `409`  | El trabajo no ha finalizado correctamente. El cuerpo incluye `status` y, si el trabajo falló, `error`. |
| `410`  | El trabajo finalizó correctamente, pero su resultado ya no está en disco.                              |

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

Elimina un trabajo y su resultado del disco.

| Estado | Causa                                                                                          |
| ------ | ---------------------------------------------------------------------------------------------- |
| `204`  | Eliminado.                                                                                     |
| `404`  | No existe ese trabajo.                                                                         |
| `409`  | El trabajo se está reconociendo en este momento y no se puede eliminar. Espere a que finalice. |

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

Indica si el contenedor tiene licencia y está listo para aceptar documentos. Úselo como sonda de disponibilidad (readiness probe) en Kubernetes o en su orquestador. La imagen del contenedor también lo ejecuta como su propio `HEALTHCHECK` de Docker.

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

Un contenedor listo devuelve `200`. Uno que no está listo devuelve `503` con `ready: false`, un `code` que explica el motivo y, por lo general, un mensaje `detail`. La disponibilidad refleja únicamente el estado de la License. La telemetría se informa junto con ella, pero nunca la afecta. Consulte [Confirmar la License](/es/fine-parser/basics/configuration#confirming-the-license).

<h2 id="results-on-disk">
  Resultados en disco
</h2>

Cada resultado se escribe en `/app/output` como `{jobId}/{name}`, junto a una pequeña base de datos de trabajos. El layout es estable, por lo que también puede recopilar los resultados directamente desde el volumen montado en lugar de descargarlos por HTTP.

De forma predeterminada, un resultado permanece hasta que ejecute `DELETE` sobre el trabajo o lo elimine usted mismo del volumen. Nada caduca por antigüedad. Si prefiere que FineParser se encargue de la limpieza, inicie el contenedor con `DELETE_ON_DOWNLOAD=true`: cada resultado se eliminará, junto con su record de trabajo, en cuanto se haya descargado por completo. A partir de ese momento, tanto el estado como la descarga devuelven `404`. Una descarga interrumpida deja el resultado disponible para un nuevo intento.

Los archivos cargados se conservan en un directorio temporal aparte y se eliminan en cuanto finaliza el reconocimiento, con independencia del resultado.

<h2 id="restarts-and-shutdown">
  Reinicios y apagado
</h2>

Los trabajos se registran en la base de datos de trabajos del volumen de salida, por lo que sobreviven a un reinicio mientras el volumen también lo haga. Los trabajos pendientes se retoman. Un trabajo que estaba en curso cuando el proceso se interrumpió se marca como `failed`, porque el proceso no puede saber qué parte del resultado llegó a escribirse.

En una detención limpia, FineParser deja de aceptar solicitudes, permite que finalicen las que están en curso y finaliza sin esperar al documento que se está reconociendo. El reconocimiento no se puede interrumpir, y esperarlo podría prolongar el apagado durante varios minutos. Ese trabajo se marca como `cancelled` y su resultado parcial se elimina. Vuelva a enviarlo si todavía lo necesita.

Asigne al contenedor un tiempo de espera de detención de 60 segundos, mediante `--stop-timeout 60` en Docker o `terminationGracePeriodSeconds: 60` en Kubernetes. El valor predeterminado de Docker, de 10 segundos, corta el vaciado antes de tiempo y hace que se pierda la telemetría de uso de todo lo ocurrido desde la última exportación.

Solo un contenedor puede usar un mismo directorio de salida a la vez. La base de datos de trabajos permanece bloqueada mientras está abierta, por lo que un segundo contenedor en el mismo volumen no arrancará. Dos contenedores requieren dos volúmenes.
