Monitoring coûts et usage
Mis à jour le 29 juillet 2026
Monitoring coûts et usage
Les coûts API peuvent exploser rapidement si vous ne les surveillez pas. Cette leçon vous apprend à suivre, estimer et contrôler vos dépenses en production. L’histoire se répète de projet en projet : une fonctionnalité passe en production un jeudi, personne ne regarde le tableau de bord du week-end, et la facture du lundi matin correspond au budget mensuel prévu. Ce qui suit vous permet de détecter la dérive au moment où elle se produit, et non trente jours plus tard.
Comprendre la facturation
Les coûts sont calculés par million de tokens, avec des prix différents pour l’input et l’output. Cette asymétrie a des conséquences directes sur vos choix de conception : envoyer un long document en entrée coûte nettement moins cher que d’en faire produire un long au modèle. Chaque réponse vous rend le décompte exact des deux directions, ce qui vous dispense de toute estimation.
from openai import OpenAI
client = OpenAI()
# Chaque réponse inclut les métriques d'usage
response = client.responses.create(
model="gpt-5.6-terra",
input="Expliquez la facturation de l'API OpenAI."
)
usage = response.usage
print(f"Input tokens : {usage.input_tokens}")
print(f"Output tokens : {usage.output_tokens}")
print(f"Total tokens : {usage.total_tokens}")
# L'output coûte plus cher que l'input !
Calculateur de coûts
Convertir ces compteurs en euros demande une grille tarifaire. Isolez-la dans une structure dédiée plutôt que de disséminer des constantes dans votre code : le jour où les prix bougent ou où vous ajoutez un modèle, une seule ligne est à modifier. Les tarifs ci-dessous sont indicatifs et méritent une vérification à chaque revue de budget.
from dataclasses import dataclass
@dataclass
class TarifModele:
input_par_million: float
output_par_million: float
# Prix approximatifs en USD par million de tokens
# Vérifiez toujours les prix actuels sur platform.openai.com/pricing
TARIFS = {
"gpt-5.6-luna": TarifModele(input_par_million=0.20, output_par_million=1.20),
"gpt-5.6-terra": TarifModele(input_par_million=2.0, output_par_million=12.0),
"gpt-5.6-sol": TarifModele(input_par_million=5.0, output_par_million=30.0),
}
def calculer_cout(modele: str, input_tokens: int,
output_tokens: int) -> float:
"""Calcule le coût d'un appel en USD."""
tarif = TARIFS.get(modele)
if not tarif:
raise ValueError(f"Modèle inconnu : {modele}")
cout_input = (input_tokens / 1_000_000) * tarif.input_par_million
cout_output = (output_tokens / 1_000_000) * tarif.output_par_million
return cout_input + cout_output
# Exemple
cout = calculer_cout("gpt-5.6-terra", input_tokens=1000, output_tokens=500)
print(f"Coût : ${cout:.6f}")
# Résultat : Coût : $0.008000
Huit dixièmes de centime pour un appel : c’est rassurant jusqu’à ce que vous le multipliiez par les cinquante mille appels quotidiens d’un chatbot en production, soit quatre cents dollars par jour. Prenez toujours l’habitude de raisonner en volume mensuel, jamais à l’appel.
Tracker de coûts en temps réel
Le calcul unitaire ne sert à rien s’il n’est pas cumulé. Ce tracker agrège les dépenses selon trois axes — par jour, par modèle et en nombre d’appels — et compare le total du jour à un budget que vous fixez. L’alerte est graduée : un avertissement dès qu’il reste moins de 20 % de l’enveloppe, puis une alerte franche au dépassement. Cette marche intermédiaire est précieuse, car elle vous laisse le temps d’intervenir avant la coupure.
import json
from datetime import datetime, date
from collections import defaultdict
class CostTracker:
"""Suivi des coûts API en temps réel."""
def __init__(self, budget_quotidien_usd: float = 10.0):
self.budget_quotidien = budget_quotidien_usd
self.couts_par_jour: dict[str, float] = defaultdict(float)
self.couts_par_modele: dict[str, float] = defaultdict(float)
self.appels_par_jour: dict[str, int] = defaultdict(int)
self.total_tokens = 0
def enregistrer(self, modele: str, input_tokens: int,
output_tokens: int):
"""Enregistre les coûts d'un appel."""
cout = calculer_cout(modele, input_tokens, output_tokens)
aujourdhui = date.today().isoformat()
self.couts_par_jour[aujourdhui] += cout
self.couts_par_modele[modele] += cout
self.appels_par_jour[aujourdhui] += 1
self.total_tokens += input_tokens + output_tokens
return cout
def budget_restant(self) -> float:
"""Retourne le budget restant pour aujourd'hui."""
aujourdhui = date.today().isoformat()
return self.budget_quotidien - self.couts_par_jour[aujourdhui]
def alerte_budget(self) -> bool:
"""Vérifie si le budget est proche de la limite."""
restant = self.budget_restant()
if restant <= 0:
print("ALERTE : Budget quotidien dépassé !")
return True
elif restant < self.budget_quotidien * 0.2:
print(f"ATTENTION : Il reste ${restant:.2f} sur le budget quotidien")
return True
return False
def rapport(self) -> dict:
"""Génère un rapport de coûts."""
return {
"cout_total_usd": sum(self.couts_par_jour.values()),
"cout_aujourdhui_usd": self.couts_par_jour.get(
date.today().isoformat(), 0
),
"budget_restant_usd": self.budget_restant(),
"couts_par_modele": dict(self.couts_par_modele),
"total_tokens": self.total_tokens,
}
# Utilisation
tracker = CostTracker(budget_quotidien_usd=5.0)
response = client.responses.create(
model="gpt-5.6-terra",
input="Bonjour !"
)
cout = tracker.enregistrer(
modele="gpt-5.6-terra",
input_tokens=response.usage.input_tokens,
output_tokens=response.usage.output_tokens
)
print(f"Coût de cet appel : ${cout:.6f}")
print(json.dumps(tracker.rapport(), indent=2))
Client avec contrôle budgétaire
Surveiller ne suffit pas toujours : face à une boucle défaillante qui appelle l’API en continu, il faut un coupe-circuit financier. Ce client vérifie le budget restant avant d’émettre la requête et refuse purement et simplement l’appel si l’enveloppe est épuisée. Le service se dégrade, mais de façon contrôlée et sans facture surprise. Le compte des coûts, lui, se fait après coup, une fois l’usage réel connu.
class BudgetControlledClient:
"""Client qui refuse les appels si le budget est dépassé."""
def __init__(self, budget_quotidien: float = 10.0):
self.client = OpenAI()
self.tracker = CostTracker(budget_quotidien_usd=budget_quotidien)
def create(self, model: str, input: str, **kwargs) -> object:
"""Appel API avec contrôle budgétaire."""
# Vérifier le budget avant l'appel
if self.tracker.budget_restant() <= 0:
raise RuntimeError(
"Budget quotidien épuisé. "
f"Dépensé : ${self.tracker.rapport()['cout_aujourdhui_usd']:.2f}"
)
response = self.client.responses.create(
model=model,
input=input,
**kwargs
)
cout = self.tracker.enregistrer(
modele=model,
input_tokens=response.usage.input_tokens,
output_tokens=response.usage.output_tokens
)
# Alerte si budget faible
self.tracker.alerte_budget()
return response
# Utilisation
budget_client = BudgetControlledClient(budget_quotidien=2.0)
try:
response = budget_client.create("gpt-5.6-terra", "Bonjour !")
print(response.output_text)
except RuntimeError as e:
print(f"Bloqué : {e}")
Optimiser les coûts
1. Choisir le bon modèle
Le levier le plus puissant reste le routage par complexité. Une classification en trois catégories n’a aucun besoin du modèle le plus coûteux : sur cette tâche, l’écart de facture atteint un facteur vingt-cinq pour une qualité identique. Faites de la complexité un paramètre explicite de votre fonction plutôt qu’une décision figée dans le code.
# Coût relatif par tâche (approximatif, ~300 tokens entrée / 20 sortie) :
# Classification simple : gpt-5.6-luna ~$0.0001 vs gpt-5.6-sol ~$0.002
# Le bon modèle fait une différence de 25x sur les coûts !
def appel_optimise(prompt: str, complexite: str) -> str:
"""Choisit le modèle le plus économique adapté."""
model = {
"simple": "gpt-5.6-luna",
"standard": "gpt-5.6-terra",
"complexe": "gpt-5.6-sol",
}.get(complexite, "gpt-5.6-terra")
response = client.responses.create(model=model, input=prompt)
return response.output_text
2. Caching des réponses
Le deuxième levier consiste à ne pas payer deux fois la même réponse. Une FAQ, un catalogue produit ou un formulaire type génèrent des prompts strictement identiques des dizaines de fois par jour. La clé de cache combine le modèle, l’entrée et les paramètres — les inclure tous est indispensable, sinon un changement de température vous rendrait silencieusement l’ancienne réponse.
import hashlib
class CachedClient:
"""Client avec cache pour éviter les appels redondants."""
def __init__(self):
self.client = OpenAI()
self.cache: dict[str, str] = {}
def create(self, model: str, input: str, **kwargs) -> str:
# Créer une clé de cache
cache_key = hashlib.md5(
f"{model}:{input}:{json.dumps(kwargs, sort_keys=True)}".encode()
).hexdigest()
if cache_key in self.cache:
print("Cache hit !")
return self.cache[cache_key]
response = self.client.responses.create(
model=model, input=input, **kwargs
)
self.cache[cache_key] = response.output_text
return response.output_text
3. Limiter les tokens de sortie
Enfin, plafonnez la sortie chaque fois que sa longueur est prévisible. Puisque l’output est la partie chère, demander un seul mot et autoriser deux mille tokens revient à laisser une porte ouverte sur une réponse bavarde que vous paierez sans l’utiliser.
# Toujours fixer max_output_tokens pour les tâches prévisibles
response = client.responses.create(
model="gpt-5.6-terra",
input="Classifiez ce texte : positif, négatif ou neutre.",
max_output_tokens=10 # On attend un seul mot
)
Tarifs relevés le 5 août 2026 — les prix évoluent régulièrement : avant tout calcul de budget, vérifiez la grille en vigueur sur la tarification officielle OpenAI.
Points clés à retenir
- Les coûts dépendent du modèle ET de la direction (input vs output)
- Implémentez un tracker de coûts avec alertes budgétaires
- Utilisez le bon modèle pour chaque tâche : Luna pour le volume, Terra par défaut
- Le caching peut réduire drastiquement les coûts pour les requêtes répétitives
- Fixez
max_output_tokensquand la longueur de réponse est prévisible - Consultez le dashboard OpenAI pour le suivi officiel de votre consommation