API & SDKGuide
Réponses en streaming en Python
Affichez la réponse du modèle au fur et à mesure de sa génération avec le package openai officiel — exemples sync et async, sortie de raisonnement, usage des tokens et gestion des erreurs.

Sur cette page
Avec le streaming, la réponse arrive morceau par morceau pendant que le modèle la génère encore, vos utilisateurs voient donc le texte immédiatement au lieu d'attendre la réponse complète. L'API utilise le format de streaming standard d'OpenAI (server-sent events), et le package Python officiel openai s'en charge pour vous.
Chaque exemple ci-dessous est complet : copiez-le dans un fichier et exécutez-le. Les exemples ont été vérifiés avec Python 3.10.12 et openai 3.31.0 contre un serveur de test local qui reproduit le format de streaming de l'API (syntaxe et exécution).
Configuration#
pip install openai
export CHEAPTOKENS_API_KEY="paste-your-key-here"Exemple minimal#
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
stream = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-0731",
messages=[{"role": "user", "content": "Write a haiku about latency"}],
stream=True,
)
for chunk in stream:
if not chunk.choices: # service chunk without text, e.g. the final usage chunk
continue
text = chunk.choices[0].delta.content
if text:
print(text, end="", flush=True)
print()Deux détails comptent :
- Ignorez les chunks sans
choices. Certains chunks ne contiennent pas de texte — par exemple le chunk final avec l'usage des tokens. delta.contentpeut être vide. Le premier chunk ne contient souvent que le rôle, et le dernier seulementfinish_reason. N'affichez le texte que lorsqu'il est présent.
Modèles de raisonnement#
zai- raisonne avant de répondre. Le raisonnement arrive dans un champ séparé du delta, reasoning_, et la réponse elle-même dans content. Le SDK conserve les champs inconnus, vous pouvez donc le lire avec getattr. Voici un exemple complet avec son propre client et son propre stream :
import os
import sys
from openai import OpenAI
client = OpenAI(
base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
stream = client.chat.completions.create(
model="zai-org/GLM-5.3-Flash",
messages=[{"role": "user", "content": "Is 1013 a prime number?"}],
stream=True,
)
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
thinking = getattr(delta, "reasoning_content", None)
if thinking:
print(thinking, end="", flush=True, file=sys.stderr) # reasoning → stderr
if delta.content:
print(delta.content, end="", flush=True) # answer → stdout
print()Le raisonnement peut prendre un certain temps avant que le premier mot de la réponse n'apparaisse — une raison de plus de streamer.
Usage des tokens#
Demandez l'usage avec stream_options ; il arrive dans le dernier chunk, qui n'a pas de choices :
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
stream = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-0731",
messages=[{"role": "user", "content": "Explain idempotency in one paragraph"}],
stream=True,
stream_options={"include_usage": True},
)
usage = None
for chunk in stream:
if chunk.usage:
usage = chunk.usage
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if usage:
print(f"\ninput: {usage.prompt_tokens}, output: {usage.completion_tokens}")Multipliez ces nombres par les tarifs du modèle pour obtenir le coût de la requête — voir Comment estimer le coût d'un agent IA.
asyncio#
En code async, utilisez AsyncOpenAI et async for. Le client a son propre nom, async_client, pour ne pas entrer en conflit avec le client synchrone des autres exemples :
import asyncio
import os
from openai import AsyncOpenAI
async_client = AsyncOpenAI(
base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
async def main() -> None:
stream = await async_client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-0731",
messages=[{"role": "user", "content": "Name three uses of a message queue"}],
stream=True,
)
async for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()
asyncio.run(main())Arrêt anticipé et timeouts#
Quittez la boucle pour arrêter la génération. Ouvrir le stream avec with ferme la connexion à la fin du bloc. Le timeout du client est défini explicitement ; le SDK réessaie aussi certaines requêtes échouées de lui-même (max_retries, 2 par défaut).
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
timeout=120, # seconds; long answers take time
)
received = ""
with client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-0731",
messages=[{"role": "user", "content": "Write a long story about a lighthouse"}],
stream=True,
) as stream:
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
received += chunk.choices[0].delta.content
if len(received) > 300: # stop early: enough text received
break
print(received)Erreurs#
Les erreurs qui surviennent avant le premier chunk — mauvaise clé, solde vide, modèle inconnu — sont levées sous forme d'exceptions lorsque vous créez le stream :
import os
import openai
from openai import OpenAI
client = OpenAI(
base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
try:
stream = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-0731",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()
except openai.AuthenticationError: # 401: wrong or rotated key
print("Check the API key")
except openai.BadRequestError as e: # 400: e.g. unknown model ID
print("Bad request:", e.message)
except openai.APIStatusError as e: # other statuses, e.g. 402 when the balance is used up
print(e.status_code, e.message)Une liste complète des codes de statut et des correctifs se trouve dans Erreurs de connexion courantes et comment les corriger.
Ce qui circule sur le réseau#
Pour déboguer, examinez le stream brut avec curl (-N désactive la mise en tampon) :
curl -N https://api.cheaptokens.dev/v1/chat/completions \
-H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "deepseek-ai/DeepSeek-V4-Flash-0731", "stream": true,
"messages": [{"role": "user", "content": "Hi"}]}'Chaque événement est une ligne data: {…} avec un chunk JSON ; le stream se termine par data: [DONE]. Les lignes qui commencent par un deux-points sont des commentaires keep-alive envoyés pendant les longues pauses — les clients les ignorent.



