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.

In questa pagina
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. 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:/, 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?
La tua risposta
0400
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:
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.

Usa l'id completo del modello dalla documentazione CheapTokens, deepseek- 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:
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:
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 NoneL'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:

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.: 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
| Modello | Al giorno | Al mese |
|---|---|---|
| GLM-5.3-Flash$0.02$0.04 | $0.046 | $1.38 |
| DeepSeek-V4-Flash$0.015$0.10 | $0.0625 | $1.88 |
| MiniMax-M2.7$0.05$0.15 | $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



