Aller au contenu principal

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_tokens quand la longueur de réponse est prévisible
  • Consultez le dashboard OpenAI pour le suivi officiel de votre consommation