Choisissez Auto, TokenLab Verified ou Official pour chaque demande, avec les prix affichés à l'avance. Voir les nouveautés

Latence vs débit des LLM : comment les acheteurs d'API doivent mesurer la vitesse

·19 septembre 2026·18 min de lecture·Mis à jour 2 octobre 2026·2200 vues
#benchmark#API IA#infrastructure de modèle#TokenLab
Latence vs débit des LLM : comment les acheteurs d'API doivent mesurer la vitesse

Le modèle qui a affiché son premier jeton visible le plus rapidement lors de notre dernier test était également le plus lent à terminer. Le 03/10/2026, nous avons diffusé une invite (prompt) via quatre modèles. claude-sonnet-5-5 a pris la tête sur le premier jeton visible (2 981 ms) et a terminé en dernier sur le temps total (12 384 ms). Cet écart constitue le cœur du débat entre latence et débit des LLM : un chiffre indique combien de temps un utilisateur attend pour voir du texte, l'autre indique à quelle vitesse le travail est accompli. Cet article définit chaque métrique, présente nos mesures internes avec leurs limites, et fournit un outil de test que vous pouvez exécuter avec l'API TokenLab.

Points clés

  • Trois chiffres, trois rôles. Le temps jusqu'au premier jeton (TTFT), le temps total et les jetons de sortie par seconde répondent à des questions différentes. Le chat, les agents et le traitement par lots reposent chacun sur une métrique différente.
  • Séparez le premier événement du premier jeton visible. Sur deepseek-v4.1-flash, les deux médianes étaient espacées de 2 334 ms et 3 925 ms. Les deltas de raisonnement peuvent arriver bien avant tout texte visible.
  • Les jetons de raisonnement consomment votre budget max_tokens. Avec max_tokens à 400, deepseek-v4.1-flash a renvoyé environ 0 jeton visible et gemini-3.8-flash environ 14. À 2000, les deux ont répondu intégralement.
  • Lisez les nombres de jetons à partir du bloc d'utilisation (usage chunk). Ne les estimez pas à partir des caractères.
  • La vitesse du modèle n'augmente pas votre limite de requêtes. Les limites de débit, les limites de jetons et la vitesse du modèle sont des contraintes distinctes.
  • Notre échantillon est restreint. Il comprend cinq exécutions par modèle, un client, une invite et une journée. Il montre comment mesurer, pas qui est le plus rapide.

Trois chiffres de vitesse : latence vs débit des LLM en pratique

Divisez la « vitesse » en plusieurs parties, car chaque partie a un responsable différent : le réseau, le modèle et votre propre file d'attente.

envoyer la requête
  -> premier événement de flux (n'importe quel bloc SSE ; peut être un delta de raisonnement)
  -> premier jeton de contenu visible (premier delta.content non vide)
  -> dernier jeton + bloc d'utilisation final

Le temps jusqu'au premier jeton (TTFT) est le temps écoulé entre l'envoi de la requête et l'arrivée du premier jeton. Nous enregistrons deux versions. Le premier événement de flux est le premier bloc SSE de toute nature. Le premier jeton de contenu visible est le premier bloc dont choices[0].delta.content n'est pas vide. Pour les modèles qui raisonnent avant de répondre, le premier événement peut être un delta de raisonnement (delta.reasoning_content) qui arrive bien avant le texte visible. Un outil qui ne surveille que delta.content mesure le temps jusqu'au premier jeton visible, pas le temps jusqu'au premier octet. Les utilisateurs ressentent le jeton visible.

Le temps total s'écoule de l'envoi de la requête à la fin du flux. Il inclut le raisonnement, le temps réseau et tout délai de nouvelle tentative que vous ajoutez.

Les jetons de sortie par seconde correspondent aux jetons de sortie divisés par le temps total. Prenez le nombre de jetons depuis usage.completion_tokens, qui inclut les jetons de raisonnement. Nous divisons par le temps total, et non par la fenêtre visible. Certains modèles de notre échantillon ont fourni du texte visible en quelques gros blocs, donc un taux basé sur la fenêtre serait trompeur.

Charge de travail Surveiller en priorité Ensuite Pourquoi
Chat Premier jeton visible Temps total L'utilisateur regarde une zone vide jusqu'à ce que le texte apparaisse.
Agents Temps total Premier jeton visible Chaque étape attend généralement la précédente, donc les totaux par étape s'additionnent.
Extraction par lots ou résumés Jetons de sortie par seconde, et coût par tâche Marge de limite de requêtes Personne ne surveille le premier jeton, et la limite de requêtes du compte plafonne la vitesse de soumission.

Les appels sans streaming, comme l'extraction JSON structurée, n'exposent que le temps total. Utilisez le streaming lors des tests si vous souhaitez obtenir la répartition.

Ce que nous avons mesuré le 03/10/2026

Méthode. Il s'agit d'une mesure interne TokenLab, observée le 03/10/2026, via POST https://api.tokenlab.sh/v1/chat/completions (source). Chaque requête utilisait stream=true et stream_options.include_usage=true. Nous avons effectué 5 exécutions séquentielles par modèle depuis un client unique en Asie de l'Est, en utilisant fetch de Node 24. L'invite était : "Expliquez comment le keep-alive HTTP réduit la latence pour les clients API. Écrivez environ 200 mots." Chaque modèle utilisait les paramètres de raisonnement par défaut. Les temps sont mesurés côté client, et toutes les exécutions ont réussi (5/5) pour chaque ligne ci-dessous.

Modèle (max_tokens 2000) Médiane 1er événement flux Médiane 1er jeton visible Médiane temps total Médiane jetons complétion (raisonnement) Jetons/s estimés
deepseek-v4.1-flash 2 334 ms 3 925 ms 5 204 ms 453 (225) 87
gemini-3.8-flash 6 892 ms 6 892 ms 8 626 ms 826 (528) 96
claude-sonnet-5-5 2 981 ms 2 981 ms 12 384 ms 418 (non rapporté) 34
gpt-5.6-luna 3 262 ms 3 262 ms 8 183 ms 313 (42) 38

La colonne jetons/s est notre propre estimation : médiane des jetons de complétion divisée par la médiane des secondes totales, par exemple 453 ÷ 5,204 ≈ 87. Il s'agit d'un ratio de médianes, et non d'une médiane des taux par exécution.

Ce que montrent les chiffres, et rien de plus :

  • Le classement par premier jeton visible (claude-sonnet-5-5, gpt-5.6-luna, deepseek-v4.1-flash, gemini-3.8-flash) diffère du classement par temps total (deepseek-v4.1-flash, gpt-5.6-luna, gemini-3.8-flash, claude-sonnet-5-5). Une métrique ne peut pas remplacer l'autre.
  • deepseek-v4.1-flash présentait un écart de 1 591 ms entre le premier événement et le premier jeton visible. Sa sortie visible médiane était d'environ 228 jetons, livrés en 87 événements sur 1 304 ms.
  • Pour gemini-3.8-flash, le premier événement et le premier jeton visible partagent une médiane de 6 892 ms. Son texte visible est arrivé en une médiane de 11 événements sur 1 734 ms, le flux était donc par blocs plutôt que jeton par jeton.
  • claude-sonnet-5-5 a envoyé une médiane de 371 événements sur 9 186 ms, et gpt-5.6-luna a envoyé 265 événements sur 4 901 ms.

Le coût accompagne la vitesse. Les prix ci-dessous proviennent de l'API modèle TokenLab, observés le 03/10/2026, avec des tarifs mis à jour le 02/10/2026T16:53:30.068Z. La dernière colonne est une estimation : médiane des jetons de complétion multipliée par le taux de sortie, côté sortie uniquement. Vérifiez les enregistrements d'utilisation pour les frais réels.

Modèle Entrée par 1M Sortie par 1M Max jetons sortie Estimation côté sortie, exécution médiane Source
deepseek-v4.1-flash 0,15 $ creux, 0,30 $ pic 0,60 $ creux, 1,20 $ pic 384 000 453 × 0,60 $ / 1M ≈ 0,00027 $ creux, ≈ 0,00054 $ pic API modèle
gemini-3.8-flash 0,375 $ 1,875 $ 65 536 826 × 1,875 $ / 1M ≈ 0,00155 $ API modèle
claude-sonnet-5-5 0,60 $ 3 $ 128 000 418 × 3 $ / 1M ≈ 0,00125 $ API modèle
gpt-5.6-luna 0,06 $ 0,36 $ 128 000 313 × 0,36 $ / 1M ≈ 0,00011 $ API modèle

Pour gpt-5.6-luna, ces tarifs s'appliquent lorsque l'entrée est au maximum de 272K jetons. Au-delà, la requête complète utilise des tarifs d'entrée 2x et de sortie 1,5x.

Limites de cet échantillon. Cinq exécutions séquentielles depuis un client en Asie de l'Est constituent un échantillon restreint. L'invite était une demande d'essai courte, donc les résultats varieront selon la région, l'heure, l'invite et la longueur de sortie. Nous n'avons pas testé la concurrence, donc rien de ceci ne prédit ce qui se passe sous charge. Traitez le tableau comme un modèle pour votre propre test, exécuté depuis la région où se trouvent vos déploiements de production. Si vous souhaitez une vue plus large des modèles, le classement des API LLM TokenLab pour les développeurs est un point de départ. Vérifiez sa méthode et sa date avant de vous fier à un chiffre de vitesse. Ne remplacez pas non plus les modèles silencieusement : le guide de fiabilité, observé le 03/10/2026, note qu'un remplacement peut modifier la qualité, le prix, la longueur du contexte et les outils.

Jetons de raisonnement et max_tokens : pourquoi 400 n'a presque rien renvoyé

Nous avons également exécuté la même invite avec max_tokens réglé sur 400. Même méthode, mêmes cinq exécutions, même date (source).

Modèle (max_tokens 400) Médiane jetons complétion Médiane jetons raisonnement Sortie visible (approx.) Médiane 1er jeton visible Médiane temps total
deepseek-v4.1-flash 400 400 0 jetons 3 795 ms 4 693 ms
gemini-3.8-flash 396 382 14 jetons 4 199 ms 4 240 ms
claude-sonnet-5-5 400 non rapporté 400 jetons 3 088 ms 11 078 ms
gpt-5.6-luna 317 42 275 jetons 3 040 ms 8 083 ms

Les médianes sont calculées par colonne, donc les colonnes d'une ligne ne décrivent pas nécessairement une seule exécution. C'est pourquoi deepseek-v4.1-flash affiche un temps de premier jeton visible à côté d'une médiane de zéro événement visible.

Le constat est clair. deepseek-v4.1-flash et gemini-3.8-flash ont dépensé la quasi-totalité du budget de 400 jetons en raisonnement et n'ont renvoyé presque aucun texte visible. Augmenter max_tokens à 2000 a produit des réponses complètes. Les jetons de raisonnement comptent dans usage.completion_tokens. Ils sont rapportés séparément dans usage.completion_tokens_details.reasoning_tokens.

Lorsque nous avons vu le premier résultat de 400 jetons, l'échec ressemblait à une réponse vide, pas à un problème de budget. Trois vérifications le rendent visible :

  1. Lisez le bloc d'utilisation final, activé par stream_options={"include_usage": True}. Si reasoning_tokens est proche de completion_tokens et que le texte est vide, le budget est allé au raisonnement.
  2. Vérifiez finish_reason. Le guide de streaming (observé le 03/10/2026) liste length comme valeur lorsqu'une limite de jetons est atteinte.
  3. Comparez avec la sortie maximale du modèle. Par exemple, l'API modèle liste 384 000 pour deepseek-v4.1-flash et 65 536 pour gemini-3.8-flash, donc 400 était une minuscule fraction de ce que les modèles autorisent.

La référence Create Chat Completion (observée le 03/10/2026) liste également max_completion_tokens et reasoning_effort. Elle indique que les valeurs prises en charge dépendent du modèle, lisez donc la page du modèle avant de définir l'un ou l'autre. Nous ne les avons pas testés, et nous avons exécuté chaque ligne avec le raisonnement par défaut. Dimensionnez le plafond selon votre propre invite : dans notre exécution, une réponse de 200 mots a produit environ 228 à 418 jetons visibles, et jusqu'à 528 jetons de raisonnement en plus.

Un outil de mesure pour l'URL de base TokenLab

Ce script utilise l'URL de base TokenLab, lit les nombres de jetons depuis le bloc d'utilisation, et enregistre le premier événement de flux séparément du premier jeton de contenu visible. Il conserve également les en-têtes de réponse pour un débogage ultérieur. Il suit les paramètres client de la documentation (timeout=30.0, max_retries=0) afin que le SDK n'ajoute pas de nouvelles tentatives cachées à vos mesures. L'ID de modèle est celui que nous avons mesuré ; remplacez-le par tout ID dont les formats de requête acceptés incluent openai_chat_completions.

import json
import os
import statistics
import time

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["TOKENLAB_API_KEY"],
    base_url="https://api.tokenlab.sh/v1",
    timeout=30.0,
    max_retries=0,
)

PROMPT = "Expliquez comment le keep-alive HTTP réduit la latence pour les clients API. Écrivez environ 200 mots."

def measure(model: str, max_tokens: int = 2000) -> dict:
    start = time.perf_counter()
    raw = client.chat.completions.with_raw_response.create(
        model=model,
        messages=[{"role": "user", "content": PROMPT}],
        max_tokens=max_tokens,
        stream=True,
        stream_options={"include_usage": True},
    )
    headers = raw.headers
    first_event = first_visible = None
    visible_events = 0
    usage = None
    finish_reason = None

    for chunk in raw.parse():
        now = time.perf_counter() - start
        if first_event is None:
            first_event = now
        if chunk.usage is not None:
            usage = chunk.usage
        if not chunk.choices:
            continue
        choice = chunk.choices[0]
        if choice.delta.content:
            visible_events += 1
            if first_visible is None:
                first_visible = now
        if choice.finish_reason:
            finish_reason = choice.finish_reason

    total = time.perf_counter() - start
    details = getattr(usage, "completion_tokens_details", None)
    completion = usage.completion_tokens if usage else None
    return {
        "model": model,
        "request_id": headers.get("x-request-id"),
        "routing_time_ms": headers.get("x-routing-time-ms"),
        "first_event_s": first_event,
        "first_visible_s": first_visible,
        "total_s": total,
        "completion_tokens": completion,
        "reasoning_tokens": getattr(details, "reasoning_tokens", None),
        "visible_events": visible_events,
        "finish_reason": finish_reason,
        "tokens_per_s": completion / total if completion else None,
    }

if __name__ == "__main__":
    runs = [measure("gemini-3.8-flash") for _ in range(5)]
    print(json.dumps(runs[0], indent=2))
    print("médiane total_s:", statistics.median(r["total_s"] for r in runs))

Quelques notes sur la lecture de la sortie :

  • first_event_s se déclenche sur n'importe quel bloc, y compris un bloc de rôle seul ou de raisonnement. first_visible_s se déclenche uniquement sur un delta.content non vide. Si les deux diffèrent de plusieurs secondes, le modèle raisonne avant de répondre.
  • Les nombres de jetons proviennent du bloc d'utilisation final. Ce bloc a une liste choices vide, c'est pourquoi la boucle vérifie chunk.usage avant de l'ignorer.
  • Exécutez-le cinq fois ou plus, enregistrez la région, et rapportez les médianes ainsi que la taille de l'échantillon.
  • Pour les modèles qui listent openai_responses, le guide de streaming montre des réponses diffusées avec des événements response.output_text.delta et response.completed. La même séparation s'applique : chronométrez le premier événement, puis le premier delta de texte.
  • Traitez un flux qui se ferme avant response.completed ou une raison de fin stop comme incomplet, et non comme un succès rapide.

En-têtes et identifiants à journaliser pour le débogage de latence

Une requête lente est difficile à déboguer si vous n'avez conservé qu'une durée. La référence API et le guide de dépannage, observés le 03/10/2026, nomment les identifiants qui valent la peine d'être conservés.

Identifiant Où il apparaît Utilisation
X-Request-ID En-tête de réponse, si disponible Support et débogage pour une requête
request_id Corps d'erreur, logs tableau de bord, enregistrements d'utilisation Support au niveau de la requête et réconciliation
X-Routing-Time-MS En-tête de réponse, si disponible Temps passé à sélectionner la livraison, avant génération
X-Task-ID, task_id, poll_url Réponses de création asynchrone et de statut Tâches image, vidéo, musique et 3D
X-Billing-Transaction-ID En-tête après facturation finale, si disponible Réconciliation des coûts
Votre propre ID de travail ou utilisateur Votre application Trouver la requête derrière une plainte utilisateur

Pour chaque appel, journalisez le point de terminaison, la méthode, le modèle, le code de statut, l'horodatage et la latence. Ajoutez les noms de champs que vous avez envoyés (pas l'invite), votre nombre de tentatives, et si une nouvelle tentative a créé une nouvelle tâche ou en a repris une. Notre outil ajoute les temps de répartition et les nombres de jetons du bloc d'utilisation.

Deux mises en garde. Les réponses en streaming peuvent se stabiliser après la fin du flux, donc un en-tête de facturation peut être absent même lorsque l'utilisation est enregistrée plus tard. Pour les montants facturés, utilisez les enregistrements d'utilisation de l'API de gestion plutôt que les nombres de jetons de réponse seuls. Journalisez également la valeur X-TokenLab-Delivery-Policy si vous en définissez une. La référence indique qu'elle accepte auto, verified ou official, qu'une valeur invalide renvoie 400, et qu'une option indisponible renvoie 503 avec code: delivery_tier_unavailable et retryable: true. Ne journalisez jamais Authorization, les clés API, les URL signées, les invites complètes ou les données personnelles.

Les détails des requêtes de la console sont conservés pendant 30 jours à partir de la capture, copiez donc les ID dont vous avez besoin dans vos propres logs. Dans notre pipeline, nous alignons ces ID avec la vue des requêtes ; le post sur la console de requêtes TokenLab couvre cette vue.

Limites de débit, nouvelles tentatives et latence vs débit des LLM

La vitesse par flux et le débit du compte sont des plafonds différents. Le guide des limites de débit, observé le 03/10/2026, liste les valeurs de niveau standard ci-dessous. Il note qu'elles peuvent changer avec la configuration active et sont appliquées par clé API.

Niveau Requêtes par minute
Utilisateur 1 000
Partenaire 10 000
VIP 10 000

Lorsque vous dépassez la limite, l'API renvoie 429 rate_limit_exceeded avec un en-tête Retry-After. Le corps d'exemple du guide contient "retryable": true et "retry_after": 8. Le guide indique de se fier aux valeurs X-RateLimit-Limit et Retry-After renvoyées plutôt qu'à tout tableau copié. Il indique également qu'un modèle plus rapide n'augmente pas la limite de requêtes de votre compte.

Voici l'arithmétique pour le travail par lots. Il s'agit de notre estimation à partir des médianes ci-dessus, pas d'un test de charge. Les flux simultanés correspondent approximativement aux requêtes par seconde multipliées par les secondes par requête. À 1 000 requêtes par minute (environ 16,7 par seconde) :

  • Avec deepseek-v4.1-flash à une médiane totale de 5,204 s, environ 87 flux s'exécutent en même temps.
  • Avec claude-sonnet-5-5 à une médiane de 12,384 s, environ 206 flux s'exécutent en même temps.

Un flux plus lent signifie plus de concurrence pour atteindre le même taux de requêtes. Atteindre la limite ajoute alors du temps d'attente en plus du temps de génération. Les limites de jetons ne sont pas couvertes dans les pages que nous avons utilisées, vérifiez donc votre compte et la page du modèle pour celles-ci.

Comment réessayer sans ruiner vos chiffres :

  • Respectez Retry-After lorsqu'il est présent. Sinon, utilisez un backoff exponentiel avec une gigue aléatoire et un plafond strict de tentatives.
  • Réglez le SDK sur max_retries=0 et réessayez à un seul endroit. Sinon, deux couches réessayeront le même appel et vos mesures seront comptées en double.
  • Ne réessayez jamais les erreurs de validation, d'authentification, de solde ou de permission. Réessayez les réponses transitoires 5xx avec un court backoff.
  • Une fois que le premier événement arrive, un flux interrompu est incomplet et n'est pas redémarré automatiquement. Répéter la requête peut produire une sortie différente et une autre facturation.
  • Journalisez le nombre de tentatives et le temps d'attente séparément du temps du modèle, afin qu'une exécution limitée ne ressemble pas à un modèle lent.

Voici le modèle de nouvelle tentative borné du guide :

import random
import time
from openai import RateLimitError

def chat_with_rate_limit_retry(client, model, messages, attempts=4):
    for attempt in range(attempts):
        try:
            return client.with_options(max_retries=0).chat.completions.create(
                model=model,
                messages=messages,
            )
        except RateLimitError as exc:
            if attempt == attempts - 1:
                raise
            header = exc.response.headers.get("Retry-After")
            wait = float(header) if header else min(30, 2 ** attempt + random.random())
            time.sleep(wait)

Limitez également la concurrence, pas seulement les requêtes moyennes par minute, et mettez en file d'attente les rafales. Suivez les réponses 429 répétées par clé API afin qu'un client ne puisse pas évincer le reste de votre application.

FAQ

Un nombre de jetons par seconde plus élevé est-il toujours une expérience utilisateur plus rapide ?

Non. Dans notre échantillon du 03/10/2026, claude-sonnet-5-5 a montré le premier jeton visible le plus tôt (2 981 ms) mais le temps total le plus long (12 384 ms). deepseek-v4.1-flash a commencé le texte visible plus tard (3 925 ms) et a terminé en premier (5 204 ms). Ce qui semble plus rapide dépend du fait que l'utilisateur regarde le flux ou attend la réponse complète. Cinq exécutions depuis un client ne peuvent pas trancher cela pour votre charge de travail.

Pourquoi ma réponse en streaming est-elle revenue avec presque pas de texte ?

Vous avez probablement dépensé le budget en raisonnement. À max_tokens 400, deepseek-v4.1-flash a utilisé 400 jetons de raisonnement et a renvoyé environ 0 jeton visible. Vérifiez usage.completion_tokens_details.reasoning_tokens dans le bloc d'utilisation final et le finish_reason. Augmentez ensuite le plafond ; à 2000, les deux modèles de raisonnement de notre test ont répondu intégralement.

Un modèle plus rapide augmente-t-il ma limite de débit ?

Non. Le guide des limites de débit indique que la vitesse du modèle, les limites de jetons et les limites de débit du compte sont des contraintes séparées. Un flux plus rapide se termine plus tôt et libère un emplacement de concurrence, mais votre limite de requêtes par clé reste la même. Demandez au support une limite différente si vous en avez besoin.

Quels ID dois-je envoyer au support lorsqu'un flux est lent ?

Envoyez le request_id (ou X-Request-ID), le point de terminaison, la méthode, le modèle, l'horodatage et le code de statut. Ajoutez task_id et poll_url pour le travail asynchrone et billing_transaction_id lorsqu'il est présent. Incluez vos temps de répartition et le corps d'erreur sûr, et rédigez d'abord les clés, les invites et les médias privés.

Exécutez l'outil ci-dessus sur votre propre invite, puis comparez ce que vous mesurez avec les classements des modèles TokenLab.

Sources

Prix observé le 2026-10-03

Prepared by Herald, TokenLab's AI growth lead. Facts are checked against the linked primary sources on the date shown.

Modèles liés

Modèles récemment publiés

Construire avec les modèles de ce guide

Comparez les prix, testez les routes et transformez la recherche en appel API fonctionnel.

Votre premier appel API

Obtenir votre clé API
curl https://api.tokenlab.sh/v1/chat/completions \
  -H "Authorization: Bearer $TOKENLAB_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"gemini-4-argon","messages":[{"role":"user","content":"Hello!"}]}'