---
seo_title: Codici errore API spiegati: 400, 401, 402, 403, 413 | EnConvert
meta_desc: Ogni codice di stato HTTP dell'API EnConvert: 401 chiave API non valida, 402 limite mensile di ops raggiunto, 413 file troppo grande, con messaggi e soluzioni.
keywords: errore 401 api chiave non valida, errore 402 limite mensile ops superato, errore 413 file troppo grande upload, codici errore api enconvert, formato risposta errore api json detail, jwt token scaduto errore 401, errore 403 forbidden upgrade piano, errore 429 rate limit api, api restituisce 503 converter non disponibile
---

# Codici di errore dell'API EnConvert

Questo riferimento elenca i codici di stato HTTP e i messaggi di errore restituiti dall'API EnConvert, da `200 OK` per le conversioni sincrone e `202 Accepted` per i job asincroni e batch, fino alle risposte di errore documentate di seguito. Ogni sezione di errore elenca le stringhe di messaggio esatte, la condizione che le attiva e come correggere la richiesta. I corpi di errore non hanno tutti la stessa forma: ne esistono sei, e la sezione [Formato della risposta di errore](#error-response-format) le mostra una per una.

Un job che fallisce *dopo* essere stato accettato non è un errore HTTP. Il `202` resta valido e il fallimento compare nel payload di stato del job quando lo interroghi in polling, come descritto in [Job sincroni e asincroni](/it/docs/concepts/sync-and-async.md).

---

## Codici di stato HTTP

| Code | Status | Description |
|------|--------|-------------|
| `200` | OK | Conversione completata con successo (modalità sincrona). |
| `202` | Accepted | Il job batch o asincrono è stato accettato per l'elaborazione in background. |
| `400` | Bad Request | Parametri non validi, campi obbligatori mancanti, corpo della richiesta malformato o contenuto del file non valido. |
| `401` | Unauthorized | Chiave API o token JWT mancante, non valido o scaduto. |
| `402` | Payment Required | Quota mensile di ops esaurita, nessun periodo di fatturazione attivo, tetto dei watcher raggiunto, limite di archiviazione raggiunto oppure un endpoint V2 disattivato sul tuo piano. |
| `403` | Forbidden | Restrizione su tipo di chiave, dominio o allowlist degli endpoint, un gate di funzionalità V1 (async, webhook, output ZIP, autenticazione di base, batch) oppure accesso a una risorsa di un altro progetto. |
| `404` | Not Found | La risorsa richiesta (job, batch, operation, file, watcher o widget) non esiste, oppure il percorso non è una route. |
| `405` | Method Not Allowed | Il percorso esiste, ma non per il metodo HTTP che hai usato. |
| `409` | Conflict | Un `job_id` fornito dal client è già in uso, oppure è stato richiesto un nuovo invio del webhook per un job di ingest non ancora completato. |
| `410` | Gone | Un artefatto V2 o un archivio batch ha superato la finestra di conservazione dei file del tuo piano e non è più nell'archiviazione. |
| `413` | Payload Too Large | Il file caricato supera il limite di dimensione del tuo piano di abbonamento. |
| `415` | Unsupported Media Type | L'URL di destinazione ha restituito un contenuto che questo convertitore non può renderizzare (ad es. JSON su `url-to-pdf`). |
| `422` | Unprocessable Entity | Il corpo della richiesta non ha superato la validazione dello schema (inclusi i campi sconosciuti sugli endpoint V2), oppure una precondizione di rendering non è stata soddisfatta, ad esempio un `wait_for_selector` che non è mai comparso. |
| `429` | Too Many Requests | È scattato un limite di frequenza delle richieste su finestra breve. Non è il codice della quota: l'esaurimento della quota mensile risponde `402`. |
| `500` | Internal Server Error | Errore imprevisto durante la conversione (il nostro motore ha avuto un guasto). |
| `502` | Bad Gateway | Non è stato possibile raggiungere il sito di destinazione, un provider upstream ha avuto un guasto, oppure la destinazione ha restituito una challenge anti-bot senza contenuto di pagina (`/v2/perceive`, a meno che non sia impostato `allow_degraded`). |
| `503` | Service Unavailable | Un convertitore non è disponibile, il pool di rendering o il gate di ammissione delle conversioni è al massimo della capacità, oppure una dipendenza upstream è fuori servizio. |
| `504` | Gateway Timeout | Il sito di destinazione ha impiegato troppo tempo a rispondere o a completare il caricamento, oppure la richiesta ha superato il budget di 300 secondi del gateway. |

---

## Formato della risposta di errore {: #error-response-format }

Esistono sei forme di corpo. Quale ricevi dipende da dove si è verificato il guasto, non dal solo codice di stato: controlla quindi il tipo di `detail` prima di leggerlo.

**1. `detail` di tipo stringa.** Il caso comune, e l'unica forma che la maggior parte delle integrazioni incontra.

```json
{
    "detail": "Authentication required"
}
```

**2. `detail` di tipo oggetto.** Il `413` sulla dimensione del file prevista dal piano. L'oggetto strutturato è il valore di `detail`, quindi leggi `body.detail.max_size`, non `body.max_size`. Consulta [413 Payload Too Large](#413-payload-too-large).

```json
{
    "detail": {
        "error": "File too large",
        "file_size": 10485760,
        "max_size": 5242880,
        "tier": "free",
        "key_type": "private"
    }
}
```

**3. `detail` di tipo array più `errors`.** La validazione dello schema (`422`) restituisce entrambi: `detail` è l'output grezzo del validatore, `errors` è un array parallelo di stringhe leggibili dalle persone. Consulta [422 Unprocessable Entity](#422-unprocessable-entity).

**4. Envelope di conversione tipizzato.** `{"error", "code", "detail"}`, con un `code` leggibile dalla macchina. Emesso solo dai tre endpoint V1 di conversione da URL. Consulta [Errori di conversione del browser](#browser-conversion-errors-415-422-502-504).

**5. Eccezione non gestita.** Un `500` che non proviene da un convertitore non ha alcun `detail`:

```json
{
    "error": "Internal server error",
    "event_id": "a1b2c3d4"
}
```

**6. Timeout della richiesta al gateway.** Il budget di 300 secondi del gateway stesso produce un `504` senza `detail` e senza `code`:

```json
{
    "error": "Request timeout"
}
```

---

## 400 Bad Request

Restituito quando la richiesta contiene parametri non validi, campi mancanti o dati malformati.

### Validazione dell'input

| Message | Condition |
|---------|-----------|
| `'url' must be provided` | Campo `url` mancante o vuoto sugli endpoint basati su URL. |
| `Invalid file format '{ext}' for {endpoint}. Allowed: {list}` | L'estensione del file caricato non corrisponde ai formati accettati dall'endpoint. |
| `File content does not match the '{endpoint}' input type.` | L'estensione è stata accettata, ma i magic byte del file appartengono a un formato diverso. |
| `Invalid pdf_options: {error}` | JSON malformato nel campo form `pdf_options`. |

### Validazione di batch e modalità

| Message | Condition |
|---------|-----------|
| `Public keys only support a single URL input` | Una chiave pubblica/dashboard ha tentato di inviare più URL. |
| `output_format=True requires multiple URLs` | Bundling ZIP richiesto con un solo URL. |
| `direct_download not supported for multiple URLs` | `direct_download=true` con un array di URL. |
| `direct_download only works in sync mode` | `direct_download=true` combinato con `async_mode=true`. |

### Validazione di auth, cookie e header

| Message | Condition |
|---------|-----------|
| `'auth' must be an object with 'username' and 'password'` | Il parametro `auth` ha una struttura errata. |
| `'cookies' must be an array of cookie objects` | `cookies` non è un array. |
| `'cookies' array must not exceed 50 entries` | Sono stati forniti più di 50 cookie. |
| `Cookie at index {i} must be an object` | La voce del cookie non è un dizionario. |
| `Cookie at index {i} must have 'name' and 'value'` | Al cookie mancano campi obbligatori. |
| `Cookie at index {i} must have 'domain' or 'url'` | Al cookie mancano sia `domain` che `url`. |
| `'headers' must be an object of header name/value pairs` | `headers` non è un dizionario. |
| `'headers' must not exceed 20 entries` | Più di 20 header personalizzati. |
| `Header '{name}' cannot be overridden` | Tentativo di impostare un header bloccato. L'insieme bloccato è `host`, `content-length`, `transfer-encoding`, `connection`, `upgrade`, `te`, `trailer`. |
| `Header '{name}' value must be a string` | Il valore dell'header non è una stringa. |
| `Cannot use both 'auth' and an 'Authorization' header. Use 'auth' for HTTP Basic Auth or 'headers' for Bearer/custom auth, not both.` | Forniti sia l'oggetto `auth` sia un header personalizzato `Authorization`. |

### Sicurezza degli URL (SSRF)

Ogni endpoint basato su URL esamina l'`url` di destinazione prima di recuperarlo. Questi messaggi vengono restituiti come `400` quando l'URL non è un indirizzo pubblico `http(s)`.

| Message | Condition |
|---------|-----------|
| `Only http:// and https:// URLs are supported.` | L'URL usa uno schema diverso da `http` o `https`. |
| `URLs with embedded credentials are not allowed. Use the 'auth' field for HTTP Basic Auth.` | L'URL incorpora un nome utente/password (`https://user:pass@host/`). |
| `URL has no hostname.` | Non è stato possibile analizzare l'URL per estrarre un host. |
| `This hostname is not allowed.` | L'host è `localhost` o un hostname di metadati cloud. |
| `URLs resolving to private or internal addresses are not allowed.` | L'URL è, o si risolve in, un IP privato, di loopback, link-local, riservato o comunque non pubblico. |
| `Non-standard IP address notation is not allowed.` | L'host usa una notazione IP ottale, esadecimale o a intero compatto che potrebbe risolversi in modo ambiguo. |
| `Could not resolve hostname '{hostname}'.` | La risoluzione DNS per l'host è fallita. |
| `This URL is blocked by the site's threat policy.` | L'host di destinazione è nella denylist della threat policy, verificata insieme al controllo SSRF. |

### Validazione delle opzioni di rendering

| Message | Condition |
|---------|-----------|
| `'wait_for_selector' must be a string` | `wait_for_selector` non era una stringa. |
| `'wait_for_selector' is too long (max 1000 chars)` | Il selettore supera i 1000 caratteri. |
| `'wait_for_selector_timeout' must be a positive integer (ms)` | Il timeout è mancante, zero, negativo o non è un intero. |
| `'wait_for_selector_timeout' must not exceed 60000 ms` | Il timeout supera il limite massimo di 60 secondi. |
| `'block_ads' must be a boolean` / `'block_media' must be a boolean` | Il flag di blocco non era un boolean. |

### Errori di sitemap e crawl

| Message | Condition |
|---------|-----------|
| `No URLs found in sitemap: {url}` | La sitemap è stata analizzata ma non contiene URL. |
| `Timeout fetching sitemap: {url}` | Il recupero della sitemap ha superato il timeout di 30 secondi. |
| `Could not fetch sitemap: {url} returned {status}` | L'URL della sitemap ha restituito uno stato HTTP diverso da 200. |
| `Invalid XML in sitemap: {url}` | Non è stato possibile analizzare l'XML della sitemap. |
| `Unrecognized sitemap format at {url}: root element is <{tag}>` | L'elemento radice della sitemap non è `<urlset>` né `<sitemapindex>`. |
| `No pages discovered on {base_url}` | Il crawl completo è terminato ma non ha trovato alcuna pagina. |

### Errori di contenuto nella conversione

| Message | Condition |
|---------|-----------|
| `Invalid JSON: {error}` | Il file JSON contiene una sintassi JSON non valida. |
| `Invalid YAML: {error}` | Il file YAML contiene una sintassi YAML non valida. |
| `Invalid TOML: {error}` | Il file TOML contiene una sintassi TOML non valida. |
| `Invalid HTML encoding (expected UTF-8)` | Il file HTML non è codificato in UTF-8. |
| `Invalid Markdown encoding (expected UTF-8)` | Il file Markdown non è codificato in UTF-8. |
| `JSON must be an array of objects for CSV conversion` | L'input di json-to-csv non è un array. |
| `JSON array is empty` | L'input di json-to-csv è un array vuoto. |
| `CSV file is empty or has no valid rows` | Il file CSV non contiene righe di dati. |
| `XML structure cannot be converted to CSV` | L'XML non è tabellare (xml-to-csv). |
| `Turnstile verification failed` | La sfida bot Cloudflare Turnstile non è andata a buon fine. |
| `Turnstile token required` | Richiesta del widget senza un token Turnstile. |

---

## 401 Unauthorized

Restituito quando l'autenticazione è mancante o non valida.

| Message | Condition |
|---------|-----------|
| `Authentication required` | Nessuna chiave API e nessun token JWT fornito nella richiesta. |
| `Invalid API Key format` | La chiave API è troppo corta o non inizia con `sk_` o `pk_`. |
| `Invalid API Key` | L'hash della chiave API non è stato trovato nel database. |
| `API Key revoked` | La chiave API è stata disattivata dalla dashboard. |
| `Token has expired` | Il token di accesso JWT è scaduto (durata di 1 ora). |
| `Invalid token` | Il JWT è malformato, manomesso o comunque non valido. |
| `Refresh token has expired` | Il refresh token è scaduto (durata di 7 giorni). |
| `Invalid refresh token` | Il refresh token è malformato o non valido. |
| `Invalid token type` | Il token è stato decodificato con successo ma non è del tipo previsto (refresh). |
| `No refresh token` | L'endpoint di refresh del widget è stato chiamato senza un cookie refresh_token. |
| `Refresh token not found` | Il refresh token presentato non è memorizzato per nessuna sessione. |
| `User not found or invalid` | Il token è stato decodificato, ma il suo subject non corrisponde più a un account utilizzabile. |
| `Project not found` | Non è stato possibile analizzare l'id del progetto sulla chiave o sul token durante la verifica della quota di ops. |

---

## 402 Payment Required

Restituito quando un limite di utilizzo viene superato. La divisione tra `402` e `403` non è simmetrica, e coglie molti di sorpresa. **Ogni condizione di quota risponde `402`**, e lo stesso vale per un endpoint V2 disattivato sul tuo piano. **I gate di funzionalità V1 rispondono `403`** (async, webhook, output ZIP, autenticazione di base, batch). Il rate limiting è un meccanismo separato che risponde [`429`](#429-too-many-requests), mai `402`.

| Message | Condition |
|---------|-----------|
| `Monthly operations limit reached ({used}/{limit}). Upgrade your plan to continue.` | Il contatore mensile unificato di ops ha raggiunto la quota del piano. Piano Founding: 500 ops. Ogni endpoint attinge da questo unico contatore. Su qualsiasi piano a pagamento con eccedenza attivata, le richieste proseguono a $0.02/op invece di fallire. |
| `This request needs {units} operations but only {remaining} of your {limit} monthly operations remain. Upgrade your plan to continue.` | La richiesta batch supererebbe la quota mensile di ops residua. L'intero batch viene rifiutato in anticipo. |
| `No active billing period found for this project. Contact support to restore your subscription.` | Il progetto non ha un periodo di utilizzo e non è stato possibile crearne uno dal suo abbonamento. Il gate fallisce in modo chiuso invece di concedere un'operazione gratuita. |
| `Storage limit reached. Delete files or upgrade your storage plan to continue.` | L'utilizzo dell'archiviazione del progetto ha raggiunto l'allocazione di archiviazione del piano. |
| `Active watcher limit reached ({active_count}/{limit}). Delete an existing watcher or upgrade your plan to add more.` | Il progetto ha già il numero massimo di watcher attivi previsto dal suo piano. I watcher non consumano ops: questo è un tetto su quanti possono esistere contemporaneamente. |
| `{Feature} is not available on your current plan. Upgrade to a V2-inclusive plan to access this endpoint.` | Un endpoint V2 è disabilitato per il piano. |

Esaurire i crediti AI mensili non produce un `402`. L'estrazione tramite schema ricade sul risultato euristico e CSS e la richiesta va comunque a buon fine. Quote, prezzi e cosa conta come una singola operazione sono in [Rate limit e quote](/it/docs/reference/rate-limits.md).

---

## 403 Forbidden

Restituito quando l'accesso è negato a causa di restrizioni su tipo di chiave, dominio, funzionalità del piano V1 o endpoint. I gate degli endpoint V2 sono l'eccezione: rispondono [`402`](#402-payment-required), non `403`.

### Restrizioni su chiavi API e token

| Message | Condition |
|---------|-----------|
| `Private API keys cannot be used from browsers` | Una chiave privata (`sk_...`) è stata usata in una richiesta con un header `Origin` del browser. Usa invece una chiave pubblica con JWT. |
| `Domain {origin} not authorized` | L'origine della richiesta non corrisponde ad alcun dominio nell'elenco dei domini consentiti della chiave API. |
| `Public API keys can only be used to generate JWT tokens or fetch widget branding. Please exchange your public key for a JWT token at /v1/auth/token, then use the token for API calls.` | Una chiave pubblica è stata usata su un percorso diverso da `/auth/token` o `/auth/branding`. Scambiala prima per un JWT. |
| `Endpoint '{path}' not allowed for this API key` | L'elenco `allowed_endpoints` della chiave API non include il percorso richiesto. |
| `Endpoint '{path}' not allowed for this token` | L'elenco `allowed_endpoints` del token JWT non include il percorso richiesto. |
| `Token issued for different origin` | L'origine della richiesta non corrisponde all'origine registrata nel JWT (previene il furto del token). |
| `Parent origin does not match token` | L'header `X-Parent-Origin` non corrisponde a quanto convalidato all'emissione del token. |

### Restrizioni delle funzionalità del piano

| Message | Condition |
|---------|-----------|
| `Async processing is not available on your current plan. Please upgrade to access this feature.` | `async_mode=true` su un piano senza accesso asincrono. |
| `Webhook callbacks is not available on your current plan. Please upgrade to access this feature.` | `callback_url` fornito su un piano senza accesso ai webhook. |
| `ZIP output bundling is not available on your current plan. Please upgrade to access this feature.` | `output_format=true` su un piano senza accesso all'output ZIP. |
| `Basic authentication, cookies & custom headers is not available on your current plan. Please upgrade to access this feature.` | `auth`, `cookies` o `headers` usati su un piano senza accesso all'autenticazione di base. |
| `Batch processing is not available on your current plan. Please upgrade to access this feature.` | URL multipli inviati su un piano con batch_limit pari a 0. |
| `Batch size {N} exceeds your plan's limit of {M} URLs per batch.` | Il numero di URL supera il limite di dimensione del batch del piano. |
| `Website crawling is not available on your current plan. Please upgrade to access this feature.` | Endpoint di acquisizione del sito web usato su un piano con crawl_mode "none" (piano Founding). |
| `Full website crawling requires a Studio plan or higher. Your plan supports sitemap-based crawling only.` | `crawl_mode=full` richiesto su un piano Indie che supporta solo il crawling basato su sitemap. |

### Restrizioni dei widget

| Message | Condition |
|---------|-----------|
| `Widget API key has been revoked` | La chiave API interna collegata al widget è stata disattivata. |
| `Domain {origin} is not authorized for this widget` | Il dominio di embedding del widget non è nell'elenco dei domini consentiti del widget. |
| `Refresh token does not match widget` | L'ID progetto del refresh token non corrisponde al progetto del widget. |
| `Batch status requires a private API key` | Una chiave pubblica o dashboard ha tentato di accedere a `GET /v1/convert/batch/{batch_id}`. |
| `Access denied` | Tentativo di accedere a una risorsa (stato del job, file) appartenente a un progetto diverso. |

Altri due messaggi `403` non riguardano né le chiavi né i piani: `Account suspended`, restituito per ogni richiesta quando l'account dietro la chiave o il token è sospeso, e `robots.txt disallows fetching this URL (request sent respect_robots=true).`, restituito da perceive quando hai chiesto la conformità a robots e la destinazione non consente quel percorso.

---

## 404 Not Found

| Message | Condition |
|---------|-----------|
| `Job not found` | ID del job di conversione non trovato nel database (polling dello stato). |
| `Batch not found` | L'ID del batch non ha righe di attività corrispondenti per questo progetto. |
| `File not found` | Il file richiesto non esiste nell'archiviazione (endpoint di download). |
| `Widget not found` | ID del widget non trovato o widget disattivato. |
| `Operation not found`, `Ingest job not found`, `Watcher not found` | Un id di risorsa V2 che non esiste, oppure appartiene a un altro progetto. L'esistenza non viene mai rivelata tra progetti diversi. |
| `Not Found` | Il percorso non è una route dell'API. Controlla il percorso e il prefisso di versione. |

---

## 409 Conflict

| Message | Condition |
|---------|-----------|
| `job_id already in use` | Un `job_id` fornito dal client è già assegnato a un altro progetto. Scegli un id diverso, oppure lascia che sia l'API a generarlo. |
| `A completion webhook is only delivered for completed jobs.` | È stato richiesto un nuovo invio del webhook per un job di ingest che non ha raggiunto lo stato `completed`. |

---

## 410 Gone

L'artefatto esisteva, ma ha superato la finestra di conservazione dei file del tuo piano e non è più nell'archiviazione. La conservazione dipende dal piano; consulta [Rate limit e quote](/it/docs/reference/rate-limits.md).

| Message | Condition |
|---------|-----------|
| `The artifact is no longer in storage (it may have passed your plan's file-retention window). Re-run the perceive request to regenerate it.` | Download dell'artefatto su `GET /v2/perceive/{operation_id}`. |
| `The batch archive is no longer in storage (it may have passed your plan's file-retention window).` | Download dello ZIP su `GET /v2/perceive/batch/{job_id}`. |

Considera il `410` come definitivo per quell'oggetto. Rieseguire la richiesta produce un artefatto nuovo; ritentare il download no.

---

## 413 Payload Too Large

Restituito quando il file caricato supera la dimensione massima del file del piano.

<div class="alert alert-warning">
<strong>Corpo annidato:</strong> l'oggetto strutturato è il valore di <code>detail</code>, non un oggetto di primo livello. Leggi <code>body.detail.max_size</code>.
</div>

```json
{
    "detail": {
        "error": "File too large",
        "file_size": 10485760,
        "max_size": 5242880,
        "tier": "free",
        "key_type": "private"
    }
}
```

| Field | Description |
|-------|-------------|
| `error` | Sempre `"File too large"`. |
| `file_size` | La dimensione del file caricato in byte. |
| `max_size` | La dimensione massima del file consentita per il tuo piano in byte. |
| `tier` | Lo slug del tuo piano di abbonamento (ad es. `"free"`, `"starter"`, `"pro"`), con ripiego su `"free"` quando nessun piano viene risolto. Gli slug sono identificatori API stabili; i nomi commerciali sono Founding (`free`), Indie (`starter`), Studio (`pro`) e Production (`business`). |
| `key_type` | Il tipo di chiave API usata: `"private"`, `"public"` o `"unknown"`. |

Il limite viene verificato sul conteggio esatto dei byte della parte caricata prima che inizi qualsiasi lavoro di conversione. Un file di dimensione esattamente pari a `max_size` viene accettato; viene rifiutato solo un file più grande. L'header `Content-Length` è un ripiego per i vecchi punti di chiamata che non passano al controllo il proprio oggetto di upload.

`POST /v2/ingest/files` non usa questa forma. Risponde `413` con un `detail` di tipo stringa semplice: `File '{filename}' exceeds the {max_size}-byte limit.`

I tetti per piano sono elencati in [Rate limit e quote](/it/docs/reference/rate-limits.md), e i percorsi di upload a cui questo si applica sono in [Ingestione dei file](/it/docs/guides/file-ingestion.md).

---

## Errori di conversione del browser (415 / 422 / 502 / 504) {: #browser-conversion-errors-415-422-502-504 }

Le conversioni da URL distinguono un guasto nel **sito di destinazione o nell'input** (un `4xx`, `502` o `504` su cui puoi intervenire) da un guasto nel **nostro motore** (un `500`). I tre endpoint V1 di conversione da URL (`url-to-pdf`, `url-to-screenshot`, `url-to-markdown`) restituiscono questi guasti tipizzati con un `code` leggibile dalla macchina accanto a `detail`:

```json
{
    "error": "Gateway Timeout",
    "code": "upstream_timeout",
    "detail": "The target site took too long to respond while rendering PDF for https://example.com."
}
```

| Code | `code` field | Condition |
|------|--------------|-----------|
| `415` | `unsupported_content_type` | La destinazione ha restituito un contenuto che il convertitore non può renderizzare, ad esempio `application/json` inviato su `url-to-pdf` o `url-to-screenshot`. Usa `url-to-markdown` per il JSON. |
| `422` | `selector_not_found` | Un `wait_for_selector` fornito dal chiamante non è mai comparso entro `wait_for_selector_timeout`. |
| `502` | `upstream_unreachable` | Non è stato possibile raggiungere il sito di destinazione (guasto DNS o di connessione). |
| `502` | `empty_render` | La navigazione è terminata ma la pagina non ha prodotto alcun contenuto catturabile. |
| `504` | `upstream_timeout` | Il sito di destinazione ha impiegato troppo tempo a rispondere o a completare il caricamento. |

Questi cinque sono l'intero vocabolario. Nessun'altra famiglia di endpoint emette un `code`, V2 inclusa: un guasto V2 torna come una semplice stringa `detail`. La classe base dell'envelope definisce un sesto slug, `conversion_error`, ma nulla lo solleva, quindi non ti raggiunge mai. Ramifica sui cinque qui sopra e tratta qualsiasi altro valore come sconosciuto.

<div class="alert alert-info">
Un `500` ora significa che il nostro motore ha avuto un guasto, quindi ritentare una richiesta identica difficilmente sarà d'aiuto. Un `502`/`504` significa che è stata la <em>destinazione</em> a comportarsi male: riprova, oppure controlla l'URL.
</div>

Un `504` può arrivare anche in due forme non tipizzate: `{"error": "Request timeout"}` quando la richiesta supera il budget di 300 secondi del gateway, e un `detail` di tipo stringa semplice che porta il messaggio di timeout quando una conversione di documento (LibreOffice) va in timeout. Nessuna delle due porta un `code`.

---

## 422 Unprocessable Entity

I fallimenti di validazione dello schema restituiscono due array paralleli. `detail` è l'output grezzo del validatore, ed è quello che rimappi sui campi del form. `errors` è una stringa leggibile per ogni problema, ed è quella che mostri all'utente.

```json
{
    "detail": [
        {
            "loc": ["body", "max_pages"],
            "msg": "Input should be a valid integer, unable to parse string as an integer",
            "type": "int_parsing"
        }
    ],
    "errors": [
        "body.max_pages: Input should be a valid integer, unable to parse string as an integer (you sent 'ten')"
    ]
}
```

Vale la pena gestire per nome tre valori di `type`:

| `type` | Meaning |
|--------|---------|
| `extra_forbidden` | Campo sconosciuto. Gli schemi di richiesta V2 rifiutano le chiavi sconosciute invece di ignorarle, quindi un parametro scritto male è un `422` che nomina il campo, invece di un'opzione scartata in silenzio. |
| `missing` | Un campo obbligatorio non è stato inviato. |
| `json_invalid` | Il corpo della richiesta non era JSON valido. |

<div class="alert alert-warning">
<strong>Un'eccezione:</strong> la validazione per singolo elemento su <code>POST /v2/perceive/batch</code> restituisce un <code>422</code> il cui <code>detail</code> è un semplice elenco di oggetti <code>{"loc", "msg"}</code>, senza chiave <code>type</code> e senza array <code>errors</code> di primo livello. I parser che danno per scontata la presenza di <code>errors</code> si rompono lì.
</div>

Un `422` con code `selector_not_found` è un'altra cosa: una precondizione di rendering non soddisfatta, trattata in [Errori di conversione del browser](#browser-conversion-errors-415-422-502-504).

---

## 429 Too Many Requests

Il rate limiting è un controllo di equità su finestra breve ed è separato dalla quota mensile di ops. Esaurire la quota risponde [`402`](#402-payment-required); solo il rate limiter risponde `429`.

| Message | Condition |
|---------|-----------|
| `Rate limit exceeded. Please slow down and retry shortly.` | È stata superata una finestra di frequenza delle richieste per il progetto. I bucket sono per progetto e hanno un namespace per tipo di chiave, quindi il traffico pubblico e quello privato non ne condividono uno. |

Un `429` porta con sé quattro header:

| Header | Meaning |
|--------|---------|
| `RateLimit-Limit` | Richieste consentite nella finestra che è scattata. |
| `RateLimit-Remaining` | Richieste rimaste in quella finestra, `0` in caso di rifiuto. |
| `RateLimit-Reset` | Secondi che mancano al reset della finestra. |
| `Retry-After` | Lo stesso valore di `RateLimit-Reset`. Attendi questo tempo prima di riprovare. |

Questi header compaiono solo sul `429`. Le risposte andate a buon fine non portano header di rate limit né header di ops residue, quindi non puoi leggere il budget rimanente da una risposta: controlla l'utilizzo nella dashboard. Consulta [Rate limit e quote](/it/docs/reference/rate-limits.md).

---

## 500 Internal Server Error

| Message | Condition |
|---------|-----------|
| `Conversion failed: {error}` | Un errore imprevisto durante la conversione di un **file caricato**. Le conversioni da URL non usano questo messaggio: emergono come envelope tipizzato qui sopra, oppure come corpo generico qui sotto. |
| `Perception failed. Reference operation_id '{id}' when contacting support.` | Un guasto imprevisto all'interno di un'esecuzione di `/v2/perceive`. Gli altri endpoint V2 hanno equivalenti, come `Distillation failed. Reference operation_id ...` e `Could not start ingest job. Reference job_id ...`. Cita l'id quando contatti il supporto. |

Tutto ciò che va in errore fuori da un convertitore non ti raggiunge mai come testo. Torna del tutto privo di `detail`:

```json
{
    "error": "Internal server error",
    "event_id": "a1b2c3d4"
}
```

Un caso coglie di sorpresa: un corpo JSON malformato inviato a un endpoint URL V1 (`url-to-pdf`, `url-to-screenshot`, `url-to-markdown`, `website-to-pdf`, `website-to-screenshot`) restituisce questo `500` invece di un `422`, perché quegli endpoint leggono il corpo grezzo. Lo stesso corpo malformato su un endpoint V2 restituisce un `422` con `type: json_invalid`.

Se riscontri errori 500 persistenti, il problema è probabilmente legato al file o all'URL di input. Prova con un input diverso per isolare il problema.

---

## 503 Service Unavailable

| Message | Condition |
|---------|-----------|
| `Converter not available: {endpoint}` | Il convertitore richiesto non è registrato o non è in esecuzione. |
| `Converter not available` | Il convertitore basato su URL per l'endpoint richiesto non è disponibile. |
| `The conversion service is at capacity. Please retry shortly.` | Il pool di rendering del browser non ha slot liberi. Inviato con `Retry-After: 30`. |
| `Server is at capacity. Please retry shortly.` | Il gate di ammissione delle conversioni CPU è pieno: troppe conversioni di file, o troppi byte, già in corso. Inviato con `Retry-After: 10`. |
| `Search is temporarily unavailable. Please try again later.` | Il provider di ricerca upstream dietro lookup è irraggiungibile o configurato in modo errato. |
| `Turnstile verification unavailable` | Il servizio di verifica Cloudflare Turnstile è irraggiungibile. |

Esistono due gate di capacità diversi che chiedono attese diverse, quindi leggi `Retry-After` invece di darne per scontata una. Per il resto questi errori sono transitori: riprova dopo una breve attesa.

---

## Errori degli endpoint V2

Gli [endpoint di web intelligence V2](/it/docs/concepts/v1-and-v2.md) riutilizzano i codici di stato sopra indicati, con alcune condizioni specifiche di V2 che vale la pena segnalare.

### Quota e piano (402 / 403)

Le operazioni V2 vengono conteggiate sulla stessa quota mensile unificata di ops delle conversioni V1: una op per unità di lavoro. Qualsiasi piano a pagamento con eccedenza attivata ($0.02/op) permette di superare la quota; altrimenti il limite è rigido.

| Code | Condition |
|------|-----------|
| `402` | La quota mensile di ops è esaurita. Tutti gli endpoint V2 ([perceive](/it/docs/endpoints/perceive.md), [discover](/it/docs/coming-soon/discover.md), [lookup](/it/docs/coming-soon/lookup.md), [distill](/it/docs/coming-soon/distill.md), [ingest](/it/docs/endpoints/ingest.md)) addebitano questo unico contatore insieme alle conversioni V1. |
| `402` | Il limite di watcher attivi (`max_watchers`) è stato raggiunto ([watch](/it/docs/coming-soon/watch.md)). I watcher sono un tetto separato e non consumano mai ops. |
| `402` | L'endpoint è disattivato per il piano. I gate V2 rispondono `402`, a differenza dei gate di funzionalità V1, che rispondono `403`. |
| `403` | L'endpoint non è nell'allowlist `allowed_endpoints` della chiave API. |

Due comportamenti V2 non sono errori, deliberatamente. Esaurire il saldo di crediti AI non fa fallire la richiesta: l'estrazione tramite schema ricade sul risultato euristico e CSS. E un'esecuzione [distill](/it/docs/coming-soon/distill.md) su più URL che supera il limite di ops a metà strada non produce nemmeno un `402`: si ferma lì, restituisce gli URL che ha completato e aggiunge un avviso che indica quanti sono stati saltati.

### Validazione (422)

Ogni schema di richiesta V2 rifiuta le chiavi sconosciute, quindi un parametro scritto male è un `422` che nomina il campo. La forma del corpo è descritta in [422 Unprocessable Entity](#422-unprocessable-entity).

| Endpoint | Condition |
|----------|-----------|
| [perceive](/it/docs/endpoints/perceive.md) | È stato inviato `proxy_url`, `geolocation` o `action_chain`; questi campi sono riservati a una release successiva. |
| [distill](/it/docs/coming-soon/distill.md) | Non è stato fornito né `schema` né `prompt` (inviarli entrambi va bene, vince `schema`); nessuno o entrambi tra `urls` e `discover_from` sono stati forniti; un campo CSS non valido, un tipo di campo non supportato o una regex che rischia un backtracking catastrofico. |
| [watch](/it/docs/coming-soon/watch.md) | `frequency_minutes` al di sotto del minimo orario di 60 minuti; un corpo `PATCH` vuoto. |
| [ingest](/it/docs/endpoints/ingest.md) | Il `mode` non corrisponde alla sorgente (modalità `urls` senza `urls`, oppure `sitemap`/`crawl` senza un `url` di partenza). |

### Provider di ricerca (502 / 503)

L'endpoint [lookup](/it/docs/coming-soon/lookup.md) dipende da un provider di ricerca upstream. Il testo grezzo dell'errore del provider non raggiunge mai il client.

| Code | Message | Condition |
|------|---------|-----------|
| `502` | `The search provider returned an error. Please try again.` | Il provider ha restituito una risposta di errore o un guasto di trasporto non ritentabile. |
| `503` | `Search is temporarily unavailable. Please try again later.` | Il provider è configurato in modo errato (chiave mancante) o temporaneamente irraggiungibile. Riprova più tardi. |

### Non trovato (404)

`GET` e `DELETE` su un `operation_id`, `job_id` o `watcher_id` V2 che non esiste, o che appartiene a un progetto diverso, restituiscono `404`. L'esistenza non viene mai rivelata tra progetti diversi.

### Accettato (202)

[Ingest](/it/docs/endpoints/ingest.md) è sempre asincrono: `POST /v2/ingest` risponde `202` con un `job_id` da interrogare in polling. I batch di perceive con più di 10 URL rispondono `202` con stato `queued`. Un batch di 10 URL o meno normalmente risponde inline, ma se supera la finestra inline degrada a `202` con stato `processing` e un avviso: gestisci quindi il `202` per qualsiasi dimensione del batch.

Un `202` significa anche che i fallimenti successivi non sono errori HTTP. Interroga il job in polling e leggi il suo payload di stato, come descritto in [Job sincroni e asincroni](/it/docs/concepts/sync-and-async.md).

---

## Risoluzione dei problemi

### Problemi di autenticazione

- **Ricevi un 401?** Verifica che la tua chiave API sia valida e attiva nella dashboard. Se usi JWT, assicurati che il token non sia scaduto (durata di 1 ora).
- **Ricevi un 403 relativo all'uso nel browser?** Stai usando una chiave privata (`sk_...`) dal codice lato client. Passa a una chiave pubblica con JWT per le richieste dal browser.
- **Ricevi un 403 relativo al dominio?** Aggiungi il tuo dominio all'elenco dei domini consentiti della chiave API nella dashboard.

### Problemi di conversione

- **Ricevi un 400 relativo al formato del file?** Assicurati che l'estensione del file caricato corrisponda all'endpoint (ad es. `.json` per json-to-xml, `.docx` per doc-to-pdf).
- **Ricevi un 413?** Il tuo file supera il limite di dimensione del piano. Leggi `detail.max_size` dalla risposta, poi controlla la dimensione massima del file del tuo piano o esegui l'upgrade.
- **Ricevi un 402?** Hai raggiunto la quota mensile di ops, il tetto dei watcher o il limite di archiviazione, oppure il progetto non ha un periodo di fatturazione attivo. Controlla l'utilizzo nella dashboard e consulta [Rate limit e quote](/it/docs/reference/rate-limits.md).

### Problemi di accesso alle funzionalità

- **Ricevi un 403 relativo alle funzionalità del piano?** La funzionalità V1 che stai cercando di usare (async, batch, webhook, output ZIP, autenticazione di base) richiede un piano di livello superiore. Consulta la [tabella di gating delle funzionalità](/it/docs/reference/rate-limits.md#403-una-funzionalita-batch-o-v1-che-il-tuo-piano-non-ha).
- **Ricevi un 402 su un endpoint V2 che non riguarda la quota?** I gate degli endpoint V2 rispondono `402`, non `403`. Il messaggio recita `... is not available on your current plan. Upgrade to a V2-inclusive plan to access this endpoint.`

## Domande frequenti

### Perché l'API restituisce 402 Payment Required per una conversione di file?

Un `402` indica che un limite di utilizzo è esaurito: la tua quota mensile unificata di ops (500 ops sul piano Founding), il tetto dei watcher attivi o l'allocazione di archiviazione del tuo progetto. Copre anche due casi che non riguardano la quota: un progetto senza periodo di fatturazione attivo e un endpoint V2 disattivato sul tuo piano. Le richieste batch che supererebbero la quota mensile di ops residua vengono rifiutate in anticipo con un `402` per l'intero batch. Le conversioni V1 e le operazioni V2 attingono dalla stessa quota; qualsiasi piano a pagamento con eccedenza attivata ($0.02/op) permette di superarla. Il rate limiting è un meccanismo diverso e risponde `429`.

### Come risolvo un errore 401 Unauthorized dell'API di conversione?

Verifica che la chiave API sia presente, inizi con `sk_` o `pk_` e sia ancora attiva nella dashboard, perché le chiavi revocate restituiscono `API Key revoked`. Se ti autentichi con un JWT, tieni presente che i token di accesso scadono dopo 1 ora (`Token has expired`) e i refresh token dopo 7 giorni.

### Perché ricevo 413 Payload Too Large quando carico un file?

Il file caricato supera la dimensione massima del tuo piano, misurata sul conteggio esatto dei byte della parte caricata prima che inizi qualsiasi lavoro di conversione. Un file esattamente al limite viene accettato. Il corpo del `413` annida un oggetto strutturato sotto `detail`, con `file_size`, `max_size` (entrambi in byte), `tier` e `key_type`: leggilo quindi come `detail.max_size` e non come un campo di primo livello.

### Posso usare una chiave API privata da JavaScript nel browser?

No. Una chiave privata (`sk_...`) usata in una richiesta con un header `Origin` del browser restituisce `403 Private API keys cannot be used from browsers`. Scambia una chiave pubblica con un JWT su `/v1/auth/token` e usa quel token per le chiamate API dal browser.

### Un errore 503 Service Unavailable dell'API è permanente?

No, gli errori `503` come `Converter not available: {endpoint}` o `Turnstile verification unavailable` sono in genere transitori. Riprova la richiesta dopo una breve attesa.
