Salta ai contenuti

Errori & Rate Limit

Factum Parse usa codici HTTP standard e application/problem+json per gli errori strutturati dell’API.

Status Significato Causa tipica
200 Successo Risposta normale di parse/upload/status
202 Accettato Parse asincrono (async_mode=true)
400 Richiesta malformata Body upload vuoto, JSON malformato
401 Non autorizzato X-API-Key mancante o non valida (fail-closed, constant-time)
404 Non trovato job_id upload inesistente
413 Payload troppo grande Body/file oltre 10 MiB (max_upload_bytes / max_body_bytes)
415 Tipo media/documento non supportato File non supportato (non XML/PDF)
422 Entità non processabile XML FatturaPA non valido, limiti text non rispettati, violazione XOR, conflitto async/text
429 Troppe richieste Rate-limit middleware, quando attivo
500 Errore interno Errore imprevisto provider/privacy/database/runtime
503 Servizio non disponibile Sidecar Rizzo non raggiungibile (blocco privacy fail-closed)

Tutti gli errori business restituiscono Content-Type: application/problem+json:

{
"type": "about:blank",
"title": "Tipo di file non supportato",
"status": 415,
"detail": "contenuto non riconosciuto: attesi PDF o FatturaPA XML",
"instance": "7c6f2d1c-..."
}
Campo Tipo Descrizione
type string URI del tipo di problema ("about:blank" per default)
title string Titolo leggibile dell’errore
status integer HTTP status code
detail string Descrizione specifica del problema
instance string | null Request-ID o UUID generato
{
"type": "about:blank",
"title": "Richiesta malformata",
"status": 400,
"detail": "Il corpo della richiesta non è un JSON valido: Expecting value: line 1 column 1 (char 0)",
"instance": "a1b2c3d4-..."
}

401 — Unauthorized (API Key mancante o non valida)

Sezione intitolata “401 — Unauthorized (API Key mancante o non valida)”
{
"type": "about:blank",
"title": "Non autorizzato",
"status": 401,
"detail": "X-API-Key mancante o non valida",
"instance": null
}

Il confronto usa secrets.compare_digest (constant-time). Il body non rivela se la chiave era solo sbagliata vs. formattata male.

422 — Unprocessable Entity (Schema documentale non riconosciuto)

Sezione intitolata “422 — Unprocessable Entity (Schema documentale non riconosciuto)”
{
"type": "about:blank",
"title": "Richiesta non processabile",
"status": 422,
"detail": "serve esattamente una sorgente: job_id oppure text",
"instance": "b2c3d4e5-..."
}
{
"type": "about:blank",
"title": "Troppe richieste",
"status": 429,
"detail": "Rate limit superato. Attendi 45 secondi.",
"instance": "c3d4e5f6-..."
}

Header inclusi:

Retry-After: 45
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 45

500 — Internal Server Error (Errore computazionale non recuperabile)

Sezione intitolata “500 — Internal Server Error (Errore computazionale non recuperabile)”
{
"type": "about:blank",
"title": "Errore interno del server",
"status": 500,
"detail": "Errore imprevisto durante l'elaborazione del documento",
"instance": "d4e5f6a7-..."
}

503 — Service Unavailable (Sidecar Rizzo non raggiungibile)

Sezione intitolata “503 — Service Unavailable (Sidecar Rizzo non raggiungibile)”
{
"type": "about:blank",
"title": "Servizio non disponibile",
"status": 503,
"detail": "Servizio di pseudonimizzazione non raggiungibile (fail-closed)",
"instance": "e5f6a7b8-..."
}

Il sistema è fail-closed: se il sidecar Rizzo non è disponibile, la richiesta viene rifiutata con 503. Mai testo scoperto all’LLM.

Dettaglio 422 — violazioni ParseRequest (testo diretto)

Sezione intitolata “Dettaglio 422 — violazioni ParseRequest (testo diretto)”
Condizione detail
text < 80 caratteri Testo troppo corto: minimo 80 caratteri
text > 200.000 caratteri Testo troppo lungo: massimo 200.000 caratteri
async_mode=true + text text supporta solo elaborazione sincrona (payload in RAM)
job_id e text entrambi serve esattamente una sorgente: job_id oppure text
job_id e text entrambi assenti serve esattamente una sorgente: job_id oppure text
callback_url senza async_mode=true callback_url richiede async_mode=true
Condizione detail
PDF senza layer di testo (< 80 char) scanned_document: il PDF non contiene testo estraibile
PDF > 50 pagine Il PDF supera il limite massimo di 50 pagine
XML non conforme a FatturaPA XML non valido o non conforme al formato FatturaPA
Content-Type non supportato contenuto non riconosciuto: attesi PDF o FatturaPA XML

Il rate limiting è configurabile tramite impostazioni backend (app/services/ratelimit.py, header X-Tier, valori free | basic | pro, default free). Non trattare le vecchie tabelle dei tier come un contratto API pubblico stabile a meno che la configurazione di deployment e il middleware non siano stati riverificati.

Limiti correnti configurati:

Tier Limite Finestra
free 60 req/min 1 min
basic 100 req/min 1 min
pro 1000 req/min 1 min

Quando il rate limiting è attivo, 429 Too Many Requests include header di rate limit:

Retry-After: 45
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 45

Note operative:

  • Il rate limit è fail-open: se Redis è irraggiungibile (RATE_LIMIT_REQUIRED=false), le richieste passano (non blocca il servizio). RATE_LIMIT_REQUIRED=true → Redis giù = 503.
  • Gira prima dell’auth (ASGI middleware): richieste non autenticate consumano quota.
  • Il retry loop wait_ready() (10 tentativi, 2s caduno) garantisce Redis connesso prima che il server inizi a servire.
  • Chiave di rate-limit: X-API-Key o, in assenza, IP del client (con anti-spoofing XFF).

POST /v1/uploads accetta:

X-Request-Id: 7c6f2d1c-...

Il valore è usato come campo instance RFC 7807. Se assente, il router upload genera un UUID per l’istanza errore.

Importante: il backend non implementa correntemente un header X-Request-ID globale in risposta su tutte le route. La documentazione non finge quindi che tale header sia universalmente disponibile. Un futuro middleware globale di request-ID dovrebbe essere trattato come funzionalità backend e testato prima di modificare questa pagina.

  • 400 / 401 / 404 / 415 / 422: correggi la richiesta; retry ciechi sono generalmente sbagliati.
  • 429: retry con backoff secondo la politica di rate-limit del deployment.
  • 500: retry solo quando l’operazione è sicura da ripetere; preserva il contesto della richiesta.
  • 503: trattalo come un fallimento del confine di privacy; retry dopo backoff piuttosto che bypassare il sidecar.