DEV Community

Cover image for Suivi des dépenses API OpenAI par fonctionnalité : guide d'attribution des coûts
Antoine Laurent
Antoine Laurent

Posted on • Originally published at apidog.com

Suivi des dépenses API OpenAI par fonctionnalité : guide d'attribution des coûts

Votre facture OpenAI indique que vous avez dépensé 4 237 $ le mois dernier. Elle ne vous dit pas que 3 100 $ venaient d’un endpoint de résumé incontrôlé, 700 $ d’un client qui vous paie 50 $/mois, et 437 $ d’une fonctionnalité que personne n’utilise. Le tableau de bord masque les dimensions dont vous avez besoin pour décider du pricing, de la capacité et de la roadmap.

Essayez Apidog aujourd’hui

Ce guide montre comment attribuer correctement les coûts de l’API OpenAI : ajouter des métadonnées à chaque requête, agréger les dépenses par fonctionnalité, route et client, définir des plafonds budgétaires par clé, puis tester le tout avant la mise en production.

💡 Apidog vous donne la visibilité au niveau requête et les tests de scénario nécessaires pour vérifier votre wrapper de suivi des coûts avant le déploiement. Utilisez-le pour rejouer des requêtes étiquetées, valider la forme des journaux et confirmer que chaque appel transporte les métadonnées attendues par votre entrepôt de données.

En bref

Étiquetez chaque appel OpenAI avec des métadonnées structurées :

  • feature
  • route
  • customer_id
  • environment

Émettez ensuite une ligne de log JSON par requête avec :

  • le modèle utilisé ;
  • les tokens d’entrée, de sortie, de cache et de raisonnement ;
  • la latence ;
  • le coût calculé ;
  • l’identifiant de requête.

Agrégez ces événements dans votre entrepôt de données. Ajoutez des plafonds budgétaires par clé dans OpenAI, alertez sur les anomalies horaires, puis validez votre wrapper avec des scénarios Apidog.

Introduction

Vous livrez une nouvelle fonctionnalité IA le mardi. Le vendredi matin, votre directeur financier vous demande pourquoi la ligne OpenAI a bondi de 40 %. Vous ouvrez le tableau de bord OpenAI : il montre une hausse globale, mais pas la fonctionnalité, le client ou la route responsable.

C’est le problème récurrent des équipes qui exécutent des workloads LLM en production. L’interface de facturation OpenAI est utile pour la comptabilité, pas pour l’attribution technique. Vous voyez les totaux quotidiens et les modèles utilisés, mais pas le contexte applicatif.

La solution : encapsuler chaque appel OpenAI dans une couche d’attribution.

Le flux cible :

  1. ajouter des métadonnées à chaque appel ;
  2. enregistrer chaque requête dans un format structuré ;
  3. calculer le coût au moment de l’écriture ;
  4. agréger par dimension produit ;
  5. créer des alertes et des plafonds ;
  6. tester le wrapper de bout en bout.

Pour le contexte de tarification, consultez la répartition des prix de GPT-5.5. Pour un problème d’attribution connexe côté outils de développement, consultez la facturation de l’utilisation de GitHub Copilot pour les équipes API. Pour les bases de l’API OpenAI, consultez la référence officielle de l’API OpenAI.

Pourquoi le tableau de bord de facturation OpenAI ne suffit pas

Le tableau de bord OpenAI fournit principalement :

  • les dépenses quotidiennes ;
  • une ventilation par modèle ;
  • une limite d’utilisation.

C’est suffisant pour une seule application, un seul client et une seule fonctionnalité. Dès que vous avez plusieurs fonctionnalités, clients, environnements ou équipes, il manque les dimensions utiles.

Dépenses totales sans contexte

Le tableau de bord peut indiquer 312 $ dépensés hier sur GPT-5.5. Il ne dit pas si cela vient :

  • d’un client qui a appelé votre endpoint de support 50 000 fois ;
  • d’un job de résumé lancé par erreur ;
  • d’un endpoint expérimental oublié.

Pas de ventilation par fonctionnalité

OpenAI étiquette l’utilisation par clé d’API et par modèle. Pas par :

  • fonctionnalité ;
  • route ;
  • client ;
  • environnement ;
  • job d’arrière-plan.

Ces dimensions doivent être ajoutées dans votre application.

Délai de reporting

Les données d’utilisation OpenAI arrivent avec un délai de plusieurs dizaines de minutes à quelques heures. Pour détecter une boucle incontrôlée ou un endpoint qui explose, ce délai est trop long.

Pas d’alertes opérationnelles fines

OpenAI fournit des limites globales et des notifications, mais pas d’alerte du type :

Préviens-moi si /api/v1/chat/answer dépasse 50 $ sur une heure.

Cette logique doit vivre dans votre propre pipeline d’observabilité.

Pas d’attribution client

Si vous vendez une fonctionnalité IA en SaaS B2B, vous devez savoir combien coûte chaque client. Sans attribution par customer_id, impossible de calculer correctement la marge brute par compte.

Les clés projet aident, mais ne suffisent pas

Les clés de projet OpenAI donnent une première dimension d’attribution. Elles ne remplacent pas les métadonnées applicatives. L’API d’utilisation OpenAI renvoie des données agrégées par projet, pas par requête.

Le problème est bien résumé par le fil Dev.to “OpenAI Tells You What You Spent. Not Where. So I Built a Dashboard” : vous ne pouvez pas piloter ce que vous ne mesurez pas.

Le modèle de données d’attribution des coûts

Chaque requête OpenAI doit produire un événement structuré. Cet événement devient l’unité d’analyse dans votre entrepôt de données.

Schéma minimal :

Colonne Type Exemple Pourquoi c’est important
request_id uuid 7a91... Idempotence, déduplication, réessais
timestamp timestamptz 2026-05-06T14:23:01Z Requêtes temporelles, anomalies
feature text support-chat Surface produit responsable
route text /api/v1/chat/answer Route HTTP ou job
customer_id text cust_4291 Dépenses par client
environment text prod, staging, dev Séparer prod et dev
model text gpt-5.5 La tarification dépend du modèle
prompt_tokens int 15234 Tokens d’entrée
completion_tokens int 812 Tokens de sortie
reasoning_tokens int 4500 Tokens de raisonnement facturés en sortie
cached_tokens int 12000 Tokens mis en cache
latency_ms int 2341 Corrélation coût / UX
cost_usd numeric(10,6) 0.045672 Coût calculé
prompt_cache_key text system-v3 Suivi du cache
error_code text null, 429 Gestion des erreurs et réessais

Calculez le coût au moment de l’écriture, pas au moment de la requête analytique. Les prix changent ; vous voulez figer le taux utilisé au moment réel de l’appel.

Exemple en Python :

PRICING = {  # USD par 1M de tokens, en mai 2026
    "gpt-5.5":      {"input": 5.00,  "cached": 2.50,  "output": 30.00},
    "gpt-5.5-pro":  {"input": 30.00, "cached": 15.00, "output": 180.00},
    "gpt-5.4":      {"input": 2.50,  "cached": 1.25, "output": 15.00},
    "gpt-5.4-mini": {"input": 0.25,  "cached": 0.125, "output": 2.00},
}

def compute_cost_usd(
    model,
    prompt_tokens,
    cached_tokens,
    completion_tokens,
    reasoning_tokens
):
    rates = PRICING[model]

    uncached = max(0, prompt_tokens - cached_tokens)

    input_cost = (uncached * rates["input"]) / 1_000_000
    cache_cost = (cached_tokens * rates["cached"]) / 1_000_000
    output_cost = (
        (completion_tokens + reasoning_tokens) * rates["output"]
    ) / 1_000_000

    return round(input_cost + cache_cost + output_cost, 6)
Enter fullscreen mode Exit fullscreen mode

Les tokens de raisonnement comptent comme sortie. L’API OpenAI les renvoie dans :

usage.completion_tokens_details.reasoning_tokens
Enter fullscreen mode Exit fullscreen mode

Si vous les traitez comme des tokens d’entrée, vos coûts seront sous-estimés. Pour le calcul complet des prix, consultez la répartition des prix de GPT-5.5.

Implémenter un wrapper OpenAI avec attribution

Centralisez tous les appels OpenAI dans une fonction. Cette fonction :

  1. reçoit les métadonnées ;
  2. exécute l’appel OpenAI ;
  3. récupère response.usage ;
  4. calcule le coût ;
  5. écrit un log JSON.
import time
import uuid
import json
import logging
from openai import OpenAI

client = OpenAI()
logger = logging.getLogger("llm.cost")

def call_with_attribution(
    *,
    feature,
    route,
    customer_id,
    environment,
    model,
    messages,
    **openai_kwargs
):
    request_id = str(uuid.uuid4())
    started = time.time()
    error_code = None
    response = None

    try:
        response = client.chat.completions.create(
            model=model,
            messages=messages,
            **openai_kwargs
        )
    except Exception as e:
        error_code = getattr(e, "code", "unknown_error")
        raise
    finally:
        latency_ms = int((time.time() - started) * 1000)
        usage = response.usage if response else None

        prompt_tokens = getattr(usage, "prompt_tokens", 0)
        completion_tokens = getattr(usage, "completion_tokens", 0)

        cached_tokens = getattr(
            getattr(usage, "prompt_tokens_details", None),
            "cached_tokens",
            0
        ) or 0

        reasoning_tokens = getattr(
            getattr(usage, "completion_tokens_details", None),
            "reasoning_tokens",
            0
        ) or 0

        cost_usd = compute_cost_usd(
            model,
            prompt_tokens,
            cached_tokens,
            completion_tokens,
            reasoning_tokens
        )

        logger.info(json.dumps({
            "event": "openai.request",
            "request_id": request_id,
            "feature": feature,
            "route": route,
            "customer_id": customer_id,
            "environment": environment,
            "model": model,
            "prompt_tokens": prompt_tokens,
            "completion_tokens": completion_tokens,
            "reasoning_tokens": reasoning_tokens,
            "cached_tokens": cached_tokens,
            "latency_ms": latency_ms,
            "cost_usd": cost_usd,
            "error_code": error_code,
        }))

    return response
Enter fullscreen mode Exit fullscreen mode

Ce wrapper devient votre surface d’attribution. Chaque fonctionnalité doit l’utiliser, au lieu d’appeler directement le SDK OpenAI.

Ensuite, envoyez ces logs vers votre entrepôt de données via votre pipeline existant :

  • Vector ;
  • Fluent Bit ;
  • Logstash ;
  • collecteur OTLP ;
  • Kafka ;
  • NATS ;
  • Pub/Sub.

Pas besoin d’un deuxième pipeline si votre stack de logs existe déjà.

Connecter le suivi des coûts et le tester avec Apidog

1. Remplacer les appels OpenAI directs

Cherchez dans votre codebase :

OpenAI(
client.chat.completions.create
Enter fullscreen mode Exit fullscreen mode

Chaque occurrence doit passer par :

call_with_attribution(...)
Enter fullscreen mode Exit fullscreen mode

Rendez feature, route, customer_id et environment obligatoires. Ne mettez pas de valeur par défaut comme unknown. Une attribution manquante doit échouer vite.

2. Émettre des logs structurés

Une ligne JSON par requête :

{
  "event": "openai.request",
  "request_id": "7a91...",
  "feature": "support-chat",
  "route": "/api/v1/chat/answer",
  "customer_id": "cust_4291",
  "environment": "prod",
  "model": "gpt-5.5",
  "prompt_tokens": 15234,
  "completion_tokens": 812,
  "reasoning_tokens": 4500,
  "cached_tokens": 12000,
  "latency_ms": 2341,
  "cost_usd": 0.045672,
  "error_code": null
}
Enter fullscreen mode Exit fullscreen mode

Si vous utilisez déjà structlog, pino ou winston, branchez le wrapper dessus.

3. Agréger par fonctionnalité

Exemple SQL :

SELECT
  feature,
  DATE_TRUNC(timestamp, DAY) AS day,
  COUNT(*) AS requests,
  SUM(cost_usd) AS spend_usd,
  SUM(prompt_tokens + completion_tokens) AS tokens,
  AVG(latency_ms) AS avg_latency_ms,
  SUM(cached_tokens) / NULLIF(SUM(prompt_tokens), 0) AS cache_hit_rate
FROM openai_events
WHERE environment = 'prod'
  AND timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY feature, day
ORDER BY day DESC, spend_usd DESC;
Enter fullscreen mode Exit fullscreen mode

Créez aussi des vues par :

  • route ;
  • client ;
  • modèle ;
  • environnement ;
  • job d’arrière-plan.

4. Construire un tableau de bord opérationnel

Dans Grafana, Metabase, Looker ou Superset, ajoutez au minimum :

  1. dépenses par fonctionnalité dans le temps ;
  2. dépenses par client dans le temps ;
  3. top 20 des routes les plus coûteuses hier ;
  4. taux de cache par fonctionnalité ;
  5. latence moyenne par modèle ;
  6. erreurs et réessais.

5. Tester le wrapper avec Apidog

Avant de déployer, testez le comportement de bout en bout avec Apidog.

Scénario recommandé :

  1. Créez une requête vers votre endpoint IA.
  2. Passez un customer_id et une feature connus.
  3. Exécutez la requête.
  4. Capturez la réponse et l’événement de log.
  5. Ajoutez des assertions sur le log :
feature == "support-chat"
route == "/api/v1/chat/answer"
customer_id == "cust_4291"
cost_usd > 0
prompt_tokens > 0
request_id is not null
Enter fullscreen mode Exit fullscreen mode
  1. Rejouez le scénario en staging et en production avec les variables d’environnement Apidog.
  2. Vérifiez que les réessais ne doublent pas les coûts si le même request_id est réutilisé.

Pour des approches de test plus larges, consultez les outils de test d’API pour les ingénieurs QA. Pour une approche contract-first, consultez le développement d’API contract-first.

6. Définir des plafonds et des alertes

Utilisez une clé de projet par environnement ou fonctionnalité :

  • prod-support-chat
  • prod-summarization
  • staging-all

Définissez des plafonds stricts dans OpenAI pour limiter les dégâts en cas de boucle incontrôlée.

Ajoutez ensuite une alerte côté entrepôt de données :

Si une fonctionnalité dépasse 3× sa dépense horaire moyenne sur 7 jours, envoyer une alerte.

Le déclencheur peut être :

  • une requête planifiée ;
  • un job dbt ;
  • un cron ;
  • un workflow Airflow ;
  • un webhook Slack ;
  • PagerDuty ;
  • Opsgenie.

Les plafonds OpenAI protègent contre la catastrophe. Les alertes dans votre entrepôt détectent la dérive avant d’atteindre le plafond.

Techniques avancées

Mise en cache des prompts

GPT-5.5 facture les tokens mis en cache à 50 % du taux d’entrée. Structurez vos prompts pour maximiser le cache :

  • préfixe système stable ;
  • instructions longues au début ;
  • variables utilisateur à la fin ;
  • version explicite du prompt, par exemple system-v3.

Suivez :

SUM(cached_tokens) / NULLIF(SUM(prompt_tokens), 0)
Enter fullscreen mode Exit fullscreen mode

par fonctionnalité. Une chute brutale du taux de cache signifie souvent qu’un changement de prompt a cassé la stabilité du préfixe.

La documentation officielle d’OpenAI sur la mise en cache des prompts détaille les règles d’éligibilité.

API Batch pour le travail hors ligne

Tout ce qui n’a pas besoin d’une réponse synchrone peut passer par l’API Batch :

  • résumés nocturnes ;
  • évaluations ;
  • backfills d’embeddings ;
  • retraitement de documents.

Gardez le même wrapper de coût et ajoutez une dimension :

batch_job_id
Enter fullscreen mode Exit fullscreen mode

Ajuster l’effort de raisonnement

GPT-5.5 Thinking utilise un effort de raisonnement plus élevé. Plus l’effort est important, plus les tokens de sortie augmentent.

À auditer :

  • utilisez-vous medium là où low suffit ?
  • la qualité change-t-elle réellement ?
  • quel est le coût par réponse utile ?

Exécutez un test A/B et comparez qualité, coût et latence. Pour des calculs plus détaillés, consultez comment utiliser l’API GPT-5.5.

Discipline de fenêtre de contexte

Les prompts longs coûtent cher. Préférez un RAG avec budget strict plutôt que d’injecter toute la base de connaissances dans le contexte.

Surveillez :

AVG(prompt_tokens)
Enter fullscreen mode Exit fullscreen mode

par fonctionnalité. Si cette métrique augmente sans changement produit, votre prompt grossit silencieusement.

Surveiller la limite des 272 000 tokens

OpenAI applique un multiplicateur d’entrée de 2× et un multiplicateur de sortie de 1,5× pour les requêtes dépassant 272 000 tokens.

Ajoutez une garde :

if prompt_tokens > 250_000:
    logger.warning("large_prompt_near_pricing_threshold", extra={
        "prompt_tokens": prompt_tokens,
        "request_id": request_id,
        "feature": feature,
        "route": route,
    })
Enter fullscreen mode Exit fullscreen mode

Pour les détails, consultez l’article sur la tarification de GPT-5.5.

Plafonds par client

Pour un SaaS B2B, ajoutez une vérification avant chaque appel :

def assert_customer_budget_available(customer_id):
    spend = get_month_to_date_ai_spend(customer_id)
    limit = get_customer_ai_budget(customer_id)

    if spend >= limit:
        raise AiQuotaExceeded(customer_id)
Enter fullscreen mode Exit fullscreen mode

Réponse API possible :

{
  "error": "monthly_ai_quota_exceeded",
  "message": "Quota AI mensuel dépassé."
}
Enter fullscreen mode Exit fullscreen mode

Cela transforme une fonctionnalité IA à risque de marge en fonctionnalité monétisable et contrôlée.

Erreurs courantes à éviter

  • Compter les tokens de raisonnement comme entrée. Ils sont facturés en sortie.
  • Faire confiance au tableau de bord OpenAI pour les alertes temps réel.
  • Étiqueter trop bas dans le SDK au lieu du site d’appel.
  • Oublier les jobs d’arrière-plan.
  • Échantillonner les événements. Enregistrez chaque requête.
  • Laisser customer_id à null. Utilisez internal ou system si nécessaire.
  • Utiliser une valeur par défaut unknown pour feature ou route.
  • Ne pas dédupliquer les réessais idempotents.

Alternatives et outils

Approche Points forts Coût Quand l’utiliser
API d’utilisation OpenAI Native, sans configuration, précise Gratuit Un projet, une fonctionnalité, pas d’attribution client
Helicone Proxy direct, dashboards, cache, coûts par utilisateur Tier gratuit ; payant à partir de 20 $/mois Besoin d’un dashboard hébergé rapidement
Langfuse Open source, traces + coûts Auto-hébergé gratuit ; cloud à partir de 29 $/mois Besoin de traces et coûts dans un outil
LangSmith Intégration LangChain, évaluation + coût À partir de 39 $/utilisateur/mois Déjà sur LangChain
Entrepôt personnalisé Contrôle total, dimensions custom, pas de proxy Temps d’ingénierie Gros volume, résidence des données stricte

Compromis :

  • Un proxy ajoute un saut dans le chemin critique.
  • Une stack auto-hébergée donne du contrôle, mais doit être opérée.
  • Un entrepôt personnalisé demande du travail, mais s’intègre mieux à votre stack de données.
  • L’API d’utilisation native suffit pour la réconciliation, pas pour l’attribution produit fine.

Pour approfondir, le guide Helicone sur le suivi des coûts LLM explique l’approche proxy. La documentation Langfuse sur le suivi des coûts couvre l’approche open source.

Si vous opérez à l’échelle plateforme, consultez aussi les plateformes API pour l’architecture de microservices.

Cas d’utilisation réels

SaaS B2B avec coûts LLM par client

Une entreprise vend un produit d’intelligence commerciale. Chaque client déclenche des appels GPT-5.5 lorsqu’il demande un résumé.

Sans attribution, l’entreprise sait seulement qu’elle dépense 80 000 $/mois. Avec attribution par client, elle découvre que 12 % des clients génèrent 71 % des dépenses.

Actions possibles :

  • pricing par niveau ;
  • quotas souples sur l’offre basse ;
  • frais de dépassement ;
  • upsell vers des plans plus élevés.

Résultat : la marge brute de la fonctionnalité IA devient mesurable et pilotable.

Outils internes pour développeurs

Une équipe plateforme donne à chaque développeur accès à un assistant GPT-5.5 privé. En remplaçant customer_id par dev_email, elle identifie que trois développeurs représentent 50 % des dépenses.

Deux avaient laissé tourner des boucles d’agents. Les arrêter économise 1 800 $/mois. Le troisième cas est légitime ; les données justifient un quota plus élevé.

Prévision des coûts d’une nouvelle fonctionnalité IA

Une équipe produit veut lancer une fonctionnalité de résumé. Avec les données historiques, elle peut estimer :

coût par utilisateur actif par jour =
appels par utilisateur
× tokens moyens par appel
× prix du modèle
Enter fullscreen mode Exit fullscreen mode

Si la prévision donne 0,04 $ par utilisateur actif par jour, soit 1,20 $/mois, l’équipe pricing peut fixer un prix cohérent, par exemple 5 $/mois, avec une marge visible.

Conclusion

Le tableau de bord OpenAI répond à une question financière : combien avez-vous dépensé ? L’attribution applicative répond à la question produit : où, pourquoi et pour qui avez-vous dépensé ?

À retenir :

  • Étiquetez chaque requête avec feature, route, customer_id et environment.
  • Calculez le coût au moment de l’écriture.
  • Utilisez une clé de projet par environnement ou fonctionnalité.
  • Ajoutez des plafonds stricts dans OpenAI.
  • Créez des alertes depuis votre entrepôt de données.
  • Testez le wrapper avec Apidog avant de le déployer.
  • Auditez régulièrement l’effort de raisonnement, la taille des prompts et le taux de cache.

Téléchargez Apidog et utilisez-le pour vérifier votre wrapper d’attribution des coûts de bout en bout : requêtes étiquetées, assertions sur les logs, rejeu de scénarios et validation multi-environnements.

Pour aller plus loin, consultez la répartition des prix de GPT-5.5 et la facturation de l’utilisation de GitHub Copilot pour les équipes API.

FAQ

Les tokens de raisonnement comptent-ils comme entrée ou sortie ?

Ils sont facturés au taux de sortie. L’API OpenAI les renvoie sous :

usage.completion_tokens_details.reasoning_tokens
Enter fullscreen mode Exit fullscreen mode

Ajoutez-les à completion_tokens dans votre calcul de coût. Pour les multiplicateurs par effort, consultez la répartition des prix de GPT-5.5.

Quelle est la précision de response.usage par rapport au tableau de bord OpenAI ?

Le nombre de tokens dans response.usage correspond au tableau de bord au token près. La dérive vient surtout d’une table de prix obsolète. Versionnez vos tarifs par modèle et mettez-les à jour le jour où OpenAI change un prix.

Puis-je faire l’attribution uniquement avec les clés de projet OpenAI ?

Non, sauf si vos besoins sont très simples. Les clés de projet donnent une attribution par projet, pas par fonctionnalité, client ou route. Utilisez-les pour les environnements et les plafonds ; utilisez les métadonnées applicatives pour le reste.

Les réessais comptent-ils double ?

Une requête qui échoue avant l’exécution du modèle ne renvoie pas d’objet usage, donc aucun coût n’est enregistré. Une requête réussie puis réessayée au niveau applicatif peut être comptée deux fois si vous générez un nouveau request_id.

Pour les réessais idempotents, réutilisez le même request_id et dédupliquez à l’écriture.

À quelle vitesse l’API d’utilisation OpenAI renvoie-t-elle les données ?

Elle a un délai de plusieurs dizaines de minutes. Utilisez-la pour la réconciliation mensuelle. Pour les alertes et coupe-circuits, utilisez vos propres événements en temps quasi réel.

Dois-je échantillonner les logs ?

Non. Une ligne JSON par requête représente un volume faible. L’échantillonnage casse l’attribution par client et par route.

Cette approche fonctionne-t-elle avec d’autres fournisseurs LLM ?

Oui. Ajoutez une colonne :

provider
Enter fullscreen mode Exit fullscreen mode

Exemples :

openai
anthropic
google
deepseek
Enter fullscreen mode Exit fullscreen mode

Le wrapper change par fournisseur, mais le modèle d’attribution reste le même. Pour comparaison, consultez la tarification de l’API DeepSeek V4.

Cela fonctionne-t-il pour les embeddings et la génération d’images ?

Oui, avec des calculs de coût spécifiques :

  • embeddings : coût par token d’entrée ;
  • images : coût par image et résolution.

Ajoutez une colonne :

endpoint
Enter fullscreen mode Exit fullscreen mode

Exemples :

chat
embeddings
image
Enter fullscreen mode Exit fullscreen mode

Top comments (0)