Votre premier appel API en production (attentif aux coûts)
- Nommer les quatre disciplines qui séparent un appel de production d'une commande jouet : secrets, streaming, coût et gestion d'erreurs
- Écrire un appel streamé résilient qui réessaie les échecs transitoires (429/5xx) avec backoff — et ne réessaie jamais un 400
- Garder l'ID du modèle en configuration pour que changer de modèle soit un changement d'une ligne, pas un rechercher-remplacer
- Surveiller le coût en tokens sur chaque appel et le plafonner délibérément
Un appel API jouet tient en une ligne. Un appel de production, lui, gère les erreurs, diffuse la sortie en streaming, surveille les coûts et protège les secrets. Construisons cela, étape par étape.
Étape 1 — Secrets et modèle depuis la configuration
export ANTHROPIC_API_KEY="sk-ant-..." # never in source control
Gardez l'ID du modèle dans la configuration, pas éparpillé en valeurs littérales, pour que la migration soit triviale (pourquoi). Choisissez-le délibérément — Choisir un modèle.
Étape 2 — Un appel résilient et en streaming
- Python
- TypeScript
import os, time, random, anthropic
client = anthropic.Anthropic()
MODEL = os.environ.get("CLAUDE_MODEL", "claude-sonnet-5")
def ask_stream(prompt, system=None, max_tokens=1024):
for attempt in range(5):
try:
with client.messages.stream(
model=MODEL, max_tokens=max_tokens,
system=system or anthropic.NOT_GIVEN,
messages=[{"role": "user", "content": prompt}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
print()
usage = final.usage
print(f"\n[tokens in/out: {usage.input_tokens}/{usage.output_tokens}]")
return final
except (anthropic.RateLimitError, anthropic.APIStatusError):
if attempt == 4: raise
time.sleep(min(2 ** attempt + random.random(), 30))
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const MODEL = process.env.CLAUDE_MODEL ?? "claude-sonnet-5";
export async function askStream(prompt: string, system?: string, maxTokens = 1024) {
for (let attempt = 0; attempt < 5; attempt++) {
try {
const stream = client.messages.stream({ model: MODEL, max_tokens: maxTokens, system,
messages: [{ role: "user", content: prompt }] });
for await (const e of stream)
if (e.type === "content_block_delta") process.stdout.write(e.delta.text ?? "");
const final = await stream.finalMessage();
console.error(`\n[tokens in/out: ${final.usage.input_tokens}/${final.usage.output_tokens}]`);
return final;
} catch (e: any) {
if (attempt === 4 || ![429, 500, 529].includes(e?.status)) throw e;
await new Promise(r => setTimeout(r, Math.min(2 ** attempt * 1000, 30000)));
}
}
}
Testez-le à froid avant de le câbler à quoi que ce soit — un petit appel prouve que le stream, la ligne de tokens et votre clé fonctionnent tous :
Premier smoke test (REPL Python)
ask_stream("Say hello in one sentence.", max_tokens=64)Étape 3 — Surveiller les coûts
- Journalisez l'utilisation des tokens (ci-dessus) pour voir ce que coûte chaque appel.
- Calibrez
max_tokenset le modèle ; limitez l'entrée avec des prompts ciblés. - Pour des préfixes stables répétés, ajoutez le cache de prompts.
- Voir Tokens et tarification et Coût et latence.
Étape 4 — Gérer les scénarios défavorables
- Réessayez les erreurs transitoires (429/5xx) avec un backoff (ci-dessus) ; ne réessayez pas les 400.
- Gérez les refus avec élégance.
- Fixez un timeout et un budget de coût/itérations pour tout ce qui est agentique.
Vérifier
Forcez chaque chemin et regardez ce qui se passe — un appel de production mérite son nom en échouant bien, pas seulement en réussissant :
Guided walkthrough1 of 4
- Le texte s'imprime progressivement à mesure qu'il est généré, pas en un bloc bloquant à la fin. C'est le gain de latence que les utilisateurs ressentent.
- Après la réponse vous voyez la ligne [tokens in/out: …]. C'est votre compteur de coût par appel, journalisé à chaque appel.
- Fixez ANTHROPIC_API_KEY à une mauvaise valeur et relancez — vous devriez obtenir une erreur propre, pas un plantage de stack-trace.
- Les erreurs 429/5xx réessaient avec backoff jusqu'à 5 tentatives ; une requête 400 malformée devrait remonter immédiatement au lieu de réessayer inutilement.
Vérifiez-vous
0/3- Un appel de production, ce sont quatre disciplines superposées à la commande d'une ligne : secrets gardés hors des sources, sortie streamée, coût surveillé, erreurs gérées
- Réessayez les échecs transitoires (429/5xx) avec backoff exponentiel plus jitter — et ne réessayez jamais un 400, qui est un bug de requête que vous devez corriger
- Gardez l'ID du modèle en configuration pour que changer de modèle soit une ligne, pas un rechercher-remplacer à travers la codebase
- Journalisez l'utilisation des tokens à chaque appel : c'est votre compteur de coût par requête et cela ne coûte rien à ajouter
- Le streaming améliore la latence perçue ; les timeouts et un budget de coût/itérations empêchent les boucles agentiques de s'emballer