Retour au blog

API & SDKGuide

Timeout et tentatives du SDK Python OpenAI : les définir pour CheapTokens

Définissez timeout, max_retries et max_tokens dans le SDK Python OpenAI, interceptez les bonnes erreurs, et évitez qu'une requête bloquée tourne pendant 10 minutes.

Par CheapTokens TeamPublié le 5 min de lecture

Code Python avec timeout=30.0 et max_retries=2 sur fond sombre
Sur cette page
  1. Ce que fait le SDK quand vous ne définissez rien
  2. Définir timeout et max_retries sur le client
  3. Remplacer pour une seule requête
  4. Intercepter les erreurs qui comptent
  5. Plafonner max_tokens pour qu'une longue réponse ne s'emballe pas
  6. Ce que les tentatives font à la facture
  7. Avant de livrer

Votre script a appelé le modèle, rien n'est revenu, et dix minutes plus tard il attend toujours. C'est le comportement par défaut du SDK Python d'OpenAI : un timeout de 10 minutes et 2 tentatives. Pour le changer, passez timeout=30.0 et max_retries=2 à OpenAI(...), remplacez-les par appel avec client.with_options(...), et limitez la réponse avec max_tokens.

Le README du SDK (vérifié le 27 octobre 2026) documente tout cela. Ci-dessous, il est appliqué au point de terminaison CheapTokens, https://api.cheaptokens.dev/v1, qui parle le format OpenAI Chat Completions. Les détails de cette configuration se trouvent dans Votre première requête API avec CheapTokens et la documentation.

Ce que fait le SDK quand vous ne définissez rien#

Deux valeurs par défaut comptent. Le timeout est de 10 minutes. Les tentatives sont de 2, avec un court backoff exponentiel, pour les erreurs de connexion et pour les réponses 408, 409, 429 et 5xx, et uniquement lorsque le corps de la requête peut être renvoyé en toute sécurité. Une requête qui expire est également réessayée.

Ainsi, chaque tentative a son propre timeout, et le pire des cas est approximativement le timeout multiplié par le nombre de tentatives, plus les pauses entre elles :

  • par défaut : 600 × 3 = 1800 secondes, une demi-heure
  • timeout=30.0, max_retries=2 : 30 × 3 = 90 secondes

Supposons qu'un point de terminaison web doive répondre en 30 secondes. Avec les valeurs par défaut, il peut rester bloqué une demi-heure avant que votre code ne voie une erreur. Essayez avec un autre réglage avant de continuer la lecture.

Devinez le nombre

Un client a timeout=45.0 et max_retries=3. Quel est le temps d'attente maximal en secondes avant l'exception, sans compter les pauses entre les tentatives ?

Afficher la réponse

Réponse: 180

45 × (3 tentatives + 1 première tentative) = 180 secondes ; les pauses entre les tentatives ajoutent un peu plus.

Définir timeout et max_retries sur le client#

La solution la plus simple est de définir les deux une fois, lors de la création du 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 doit être un entier non négatif. timeout prend un float, ou un objet httpx Timeout si vous voulez des limites séparées pour la connexion et la lecture. Le README de la branche main actuelle importe httpx2 pour cela, les versions plus anciennes utilisaient httpx, donc exécutez pip show openai et vérifiez laquelle votre installation utilise. Un simple float ne nécessite aucun import et fonctionne dans les deux cas.

Le chemin de la requête avec timeout=30 et deux tentatives
Une requête : une tentative, un timeout, jusqu'à deux tentatives, puis une exception

Utilisez l'identifiant complet du modèle depuis la documentation CheapTokens, deepseek-ai/DeepSeek-V4-Flash-0731 ici : les identifiants diffèrent selon les fournisseurs. Ce modèle prend jusqu'à 32 768 jetons de sortie par réponse et a un contexte de 380K, c'est pourquoi la ligne max_tokens ci-dessus est là exprès. Plus de détails ci-dessous, et sur le modèle dans cheap DeepSeek.

Remplacer pour une seule requête#

Un client, différents travaux. Une réponse de chat doit abandonner rapidement, un résumé nocturne peut attendre. Au lieu de créer un second client, dérivez-en un pour l'appel :

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 renvoie une copie avec les nouvelles valeurs et laisse client intact. Définir max_retries=0 est le bon choix lorsque votre propre code réessaie déjà, par exemple une file d'attente de tâches qui remet la tâche en file. Sinon, les deux couches se multiplient : 3 tentatives SDK dans 3 tentatives de file d'attente, c'est 9 requêtes pour une tâche.

Intercepter les erreurs qui comptent#

Une fois les tentatives épuisées, le SDK lève une exception. Chaque classe d'erreur hérite de openai.APIError, et les requêtes échouées portent request_id, ce que vous journalisez :

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'ordre va du plus étroit au plus large. APIStatusError couvre toute réponse 4xx ou 5xx, donc s'il venait en premier, il avalerait RateLimitError. Même idée pour le timeout : il se situe au-dessus de l'erreur de connexion générale. Dans un script par lots, return None est tout l'intérêt : journalisez le prompt, passez au suivant, et relancez les échecs à la fin.

Lorsque le timeout se déclenche et que les tentatives sont épuisées, voici à quoi ressemble l'exception :

Le message APITimeoutError après épuisement des tentatives
À quoi ressemble un timeout intercepté dans le journal

Pour le cas 429 en détail, y compris l'attente entre les tentatives, voir 429 Too Many Requests d'une API IA.

Plafonner max_tokens pour qu'une longue réponse ne s'emballe pas#

Un timeout arrête l'attente. Il ne fait pas écrire moins au modèle. Sans max_tokens, un modèle avec une limite de réponse de 32 768 jetons est libre d'écrire une réponse très longue, et une longue réponse est lente. Définissez une limite adaptée à la tâche : 800 pour un résumé de ticket, 5 pour un oui/non.

Le coût est qu'une réponse peut être coupée. Vérifiez resp.choices[0].finish_reason : length signifie que le plafond a été atteint avant que le modèle ait fini. Pourquoi une réponse IA s'arrête-t-elle en milieu de phrase ? couvre cela. Si vous diffusez la réponse à la place, les premiers mots arrivent tôt et l'attente semble plus courte, voir Réponses en streaming en Python.

Ce que les tentatives font à la facture#

Une tentative est une nouvelle requête. Si une tentative a été traitée, c'est une requête distincte avec ses propres jetons. Supposons qu'un résumé de ticket fasse 1 500 jetons en entrée et 400 en sortie : une requête coûte $0.000063 aux prix actuels, et le tableau des tarifs complet contient les autres modèles. Les prix sont fixes par modèle, sans heures de pointe, donc une tentative à 3 h du matin coûte le même prix qu'à midi.

Déplacez les curseurs sur votre propre lot :

Calculateur de coût

Un lot de 1 000 résumés de tickets

Tokens d'entrée par requête: 1 500 · Tokens de sortie par requête: 400 · Requêtes par jour: 1 000

ModèlePar jourPar mois
GLM-5.3-Flash$0.046$1.38
DeepSeek-V4-Flash$0.0625$1.88
MiniMax-M2.7$0.135$4.05

Le tableau montre une passe. Si un dixième du lot nécessite une seconde tentative, ajoutez un dixième. Les tentatives sont une assurance bon marché lorsque les échecs sont rares, et une habitude coûteuse lorsque vous définissez max_retries=10 contre un point de terminaison qui échoue sans cesse.

Avant de livrer#

Liste de contrôle

Timeouts et tentatives, prêts pour la production