Torna al blog

API e SDKGuida

Timeout e tentativi dell'SDK Python di OpenAI: impostali per CheapTokens

Imposta timeout, max_retries e max_tokens nell'SDK Python di OpenAI, cattura gli errori giusti e impedisci a una richiesta bloccata di durare 10 minuti.

Di CheapTokens TeamPubblicato il 5 min di lettura

Codice Python con timeout=30.0 e max_retries=2 su sfondo scuro
In questa pagina
  1. Cosa fa l'SDK quando non imposti nulla
  2. Imposta timeout e max_retries sul client
  3. Sovrascrivilo per una singola richiesta
  4. Cattura gli errori che contano
  5. Limita max_tokens così una risposta lunga non può sfuggire al controllo
  6. Cosa fanno i tentativi alla fattura
  7. Prima di pubblicare

Il tuo script ha chiamato il modello, non è tornato nulla e dieci minuti dopo è ancora in attesa. Questo è il comportamento predefinito dell'SDK Python di OpenAI: un timeout di 10 minuti e 2 tentativi. Per modificarlo, passa timeout=30.0 e max_retries=2 a OpenAI(...), sovrascrivi i valori per singola chiamata con client.with_options(...) e limita la risposta con max_tokens.

Il README dell'SDK (verificato il 27 ottobre 2026) documenta tutto questo. Qui sotto viene applicato all'endpoint CheapTokens, https://api.cheaptokens.dev/v1, che parla il formato OpenAI Chat Completions. I dettagli di quella configurazione sono in La tua prima richiesta API con CheapTokens e nella documentazione.

Cosa fa l'SDK quando non imposti nulla#

Due valori predefiniti contano. Il timeout è di 10 minuti. I tentativi sono 2, con un breve backoff esponenziale, per errori di connessione e per risposte 408, 409, 429 e 5xx, e solo quando il corpo della richiesta può essere reinviato in sicurezza. Anche una richiesta che va in timeout viene ritentata.

Quindi ogni tentativo ha il proprio timeout, e il caso peggiore è all'incirca il timeout moltiplicato per il numero di tentativi, più le pause tra essi:

  • predefinito: 600 × 3 = 1800 secondi, mezz'ora
  • timeout=30.0, max_retries=2: 30 × 3 = 90 secondi

Supponiamo che un endpoint web debba rispondere entro 30 secondi. Con i valori predefiniti può bloccarsi per mezz'ora prima che il tuo codice veda un errore. Provalo con un'altra impostazione prima di continuare a leggere.

Indovina il numero

Un client ha timeout=45.0 e max_retries=3. Qual è l'attesa nel caso peggiore in secondi prima dell'eccezione, senza contare le pause tra i tentativi?

Mostra risposta

Risposta: 180

45 × (3 tentativi + 1 primo tentativo) = 180 secondi; le pause tra i tentativi aggiungono qualcosa in più.

Imposta timeout e max_retries sul client#

La soluzione più semplice è impostarli entrambi una volta sola, quando crei il client:

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
    timeout=30.0,      # seconds per attempt
    max_retries=2,     # 0 turns retries off
)

resp = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    messages=[{"role": "user", "content": "Summarize this ticket in two sentences: ..."}],
    max_tokens=800,
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)

max_retries deve essere un intero non negativo. timeout accetta un float, oppure un oggetto httpx Timeout se vuoi limiti separati per la connessione e la lettura. Il README del branch main attuale importa httpx2 per questo, le versioni precedenti usavano httpx, quindi esegui pip show openai e controlla quale usa la tua installazione. Un semplice float non richiede import e funziona in entrambi i casi.

Il percorso della richiesta con timeout=30 e due tentativi
Una richiesta: un tentativo, un timeout, fino a due tentativi, poi un'eccezione

Usa l'id completo del modello dalla documentazione CheapTokens, deepseek-ai/DeepSeek-V4-Flash-0731 qui: gli id differiscono tra i provider. Questo modello accetta fino a 32.768 token di output per risposta e ha un contesto di 380K, ed è per questo che la riga max_tokens qui sopra è presente di proposito. Ne parliamo più avanti, e del modello in DeepSeek economico.

Sovrascrivilo per una singola richiesta#

Un client, lavori diversi. Una risposta in chat dovrebbe arrendersi in fretta, un riepilogo notturno può aspettare. Invece di creare un secondo client, derivane uno per la chiamata:

python
quick = client.with_options(timeout=5.0, max_retries=0)
resp = quick.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    messages=[{"role": "user", "content": "Reply with one word: ok"}],
    max_tokens=5,
)

with_options restituisce una copia con i nuovi valori e lascia client invariato. Impostare max_retries=0 è la scelta giusta quando il tuo codice già ritenta, per esempio una coda di job che rimette in coda l'attività. Altrimenti i due livelli si moltiplicano: 3 tentativi dell'SDK dentro 3 tentativi della coda fanno 9 richieste per una sola attività.

Cattura gli errori che contano#

Dopo che i tentativi si esauriscono, l'SDK solleva un'eccezione. Ogni classe di errore eredita da openai.APIError, e le richieste fallite portano request_id, che è ciò che registri nei log:

python
import openai

def ask(prompt: str) -> str | None:
    try:
        r = client.chat.completions.create(
            model="deepseek-ai/DeepSeek-V4-Flash-0731",
            messages=[{"role": "user", "content": prompt}],
            max_tokens=800,
        )
        return r.choices[0].message.content
    except openai.APITimeoutError:
        print("timed out after retries")
    except openai.RateLimitError as e:
        print("429, slow down:", e.request_id)
    except openai.APIConnectionError:
        print("cannot reach the API")
    except openai.APIStatusError as e:
        print("HTTP", e.status_code, e.request_id)
    return None

L'ordine va dallo specifico al generale. APIStatusError copre qualsiasi risposta 4xx o 5xx, quindi se fosse per primo inghiottirebbe RateLimitError. Stessa idea per il timeout: sta sopra l'errore di connessione generico. In uno script batch, return None è il punto centrale: registra il prompt, passa al successivo e riesegui i fallimenti alla fine.

Quando il timeout scatta e i tentativi sono esauriti, ecco come appare l'eccezione:

Il messaggio di APITimeoutError dopo che i tentativi sono esauriti
Come appare un timeout catturato nel log

Per il caso 429 in dettaglio, inclusa l'attesa tra i tentativi, vedi 429 Too Many Requests da un'API AI.

Limita max_tokens così una risposta lunga non può sfuggire al controllo#

Un timeout smette di aspettare. Non fa scrivere meno al modello. Senza max_tokens, un modello con un limite di 32.768 token per risposta è libero di scrivere una risposta molto lunga, e una risposta lunga è lenta. Imposta un limite adatto al lavoro: 800 per un riepilogo di ticket, 5 per un sì/no.

Il costo è che una risposta può essere troncata. Controlla resp.choices[0].finish_reason: length significa che il limite è stato raggiunto prima che il modello finisse. Perché una risposta AI si interrompe a metà frase? lo tratta. Se invece trasmetti la risposta in streaming, le prime parole arrivano subito e l'attesa sembra più breve, vedi Risposte in streaming in Python.

Cosa fanno i tentativi alla fattura#

Un tentativo è una nuova richiesta. Se un tentativo è stato elaborato, è una richiesta separata con i propri token. Supponiamo che un riepilogo di ticket sia 1.500 token in input e 400 in output: una richiesta costa $0.000063 ai prezzi attuali, e la tabella dei prezzi completa ha gli altri modelli. I prezzi sono fissi per modello, senza ore di punta, quindi un tentativo alle 3 di notte costa come uno a mezzogiorno.

Sposta i cursori sul tuo batch:

Calcolatore di costi

Un batch di 1.000 riepiloghi di ticket

Token di input per richiesta: 1500 · Token di output per richiesta: 400 · Richieste al giorno: 1000

ModelloAl giornoAl mese
GLM-5.3-Flash$0.046$1.38
DeepSeek-V4-Flash$0.0625$1.88
MiniMax-M2.7$0.135$4.05

La tabella mostra una passata. Se un decimo del batch richiede un secondo tentativo, aggiungi un decimo. I tentativi sono un'assicurazione economica quando i fallimenti sono rari, e un'abitudine costosa quando imposti max_retries=10 contro un endpoint che continua a fallire.

Prima di pubblicare#

Lista di controllo

Timeout e tentativi, pronti per la produzione