Errors & Rate Limits
Factum Parse uses ordinary HTTP status codes and application/problem+json for structured
API errors.
Status matrix
Section titled “Status matrix”| Status | Meaning | Typical source |
|---|---|---|
200 |
Success | Normal parse/upload/status response |
202 |
Accepted | Async parse (async_mode=true) |
400 |
Bad request | Empty upload body, malformed JSON |
401 |
Unauthorized | Missing/invalid X-API-Key (fail-closed, constant-time compare) |
404 |
Not found | Unknown upload job_id |
413 |
Payload too large | Body/file above 10 MiB (max_upload_bytes / max_body_bytes) |
415 |
Unsupported media type/document | Unsupported file (non XML/PDF) |
422 |
Unprocessable entity | Invalid FatturaPA XML, invalid text bounds/encoding, XOR violation, async/text conflict |
429 |
Too many requests | Rate-limit middleware, when enabled |
500 |
Internal error | Unexpected provider/privacy/database/runtime failure |
503 |
Service unavailable | Required Rizzo sidecar unavailable (fail-closed privacy block) |
RFC 7807 format
Section titled “RFC 7807 format”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 |
Dettaglio 422 — violazioni ParseRequest (testo diretto)
Section titled “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 |
Dettaglio 422 — violazioni upload
Section titled “Dettaglio 422 — violazioni upload”| 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 |
Rate limiting
Section titled “Rate limiting”Rate limiting is configurable via backend settings (app/services/ratelimit.py header
X-Tier, values free | basic | pro, default free). Do not treat old tier tables
as a stable public API contract unless the deployment configuration and middleware are
re-verified.
Current configured limits:
| Tier | Limite | Finestra |
|---|---|---|
free |
60 req/min | 1 min |
basic |
100 req/min | 1 min |
pro |
1000 req/min | 1 min |
When rate limiting is enabled, 429 Too Many Requests includes rate limit headers:
Retry-After: 45X-RateLimit-Limit: 60X-RateLimit-Remaining: 0X-RateLimit-Reset: 45Note 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-Keyo, in assenza, IP del client (con anti-spoofing XFF).
Request IDs — current state
Section titled “Request IDs — current state”POST /v1/uploads accepts:
X-Request-Id: 7c6f2d1c-...The value is used as the RFC 7807 instance field. If absent, the upload router generates
a UUID for the error instance.
Important: the backend does not currently implement a global X-Request-ID response
header across all routes. Therefore the documentation does not pretend that such a header
is universally available. A future global request-ID middleware should be treated as a
backend feature and tested before this page is changed.
Client strategy
Section titled “Client strategy”400/401/404/415/422: fix the request; blind retries are usually wrong.429: retry with backoff according to the deployment’s rate-limit policy.500: retry only when the operation is safe to repeat; preserve request context.503: treat as a privacy boundary failure; retry after backoff rather than bypassing the sidecar.