Découvrez Eleven v4Découvrez Eleven v4, notre modèle le plus expressif à ce jour. Avec 3× plus de crédits inclus avec Creator+ jusqu’au 12 octobre

Aller au contenu

Intégration de l’API Text to Speech : streaming, traitement par lots, gestion des erreurs

Publié
Dernière mise à jour

ÉcouterÉcouter cet article

Intégrer une API Text to Speech est simple… après quelques décisions concrètes : quel mode de transfert utiliser, comment choisir un modèle et un format de sortie, comment diffuser en streaming, comment traiter de gros volumes sans dépasser votre limite de requêtes simultanées, comment mettre en cache et réessayer pour ne jamais payer deux fois le rendu du même audio, et comment comparer le délai avant le premier octet à celui d’un autre fournisseur.

Pour vous aider à intégrer une API Text to Speech, nous détaillons chacune de ces décisions d’architecture et les actions à mener. Ce guide vous aidera à intégrer l’API Text to Speech d’ElevenLabs API Text to Speech et à passer à l’échelle, grâce à des extraits de code prêts à intégrer en production.

Pour approfondir les concepts abordés ici, consultez nos guides sur la compréhension du streaming audio, l’optimisation de la latence, et la vue d’ensemble des modèles ElevenLabs. 

En résumé

  • L’API Text to Speech d’ElevenLabs possède un seul endpoint, accessible de trois façons : conversion par lots, streaming HTTP et WebSocket stream-input.
  • Avec HTTP, chaque requête en cours compte dans votre limite de requêtes simultanées ; avec WebSocket, seule la génération active est comptabilisée.
  • Limitez votre parallélisme juste en dessous de la limite de votre forfait et mettez en cache un hash de chaque paramètre qui influence la sortie afin de ne jamais facturer deux fois le même texte.
  • Réessayez les erreurs 429 et 5xx avec un backoff exponentiel et un jitter complet afin de réduire la cadence avant d’atteindre la limite de requêtes simultanées.

Trois façons d’intégrer l’API Text to Speech

Il existe un seul endpoint Text to Speech, mais son mode d’intégration détermine votre latence, votre complexité et vos coûts. 

Le même appel POST /v1/text-to-speech/{voice_id} peut prendre trois formes, chacune adaptée à un usage légèrement différent. Voici ces trois méthodes d’intégration de l’API Text to Speech :

  • La conversion par lots (convert) est l’intégration la plus simple : vous envoyez une requête et recevez une réponse audio. C’est l’option la moins complexe et celle dont le délai avant le premier audio est le plus élevé, car l’extrait complet est synthétisé avant le retour du moindre octet.
  • Le streaming HTTP (stream) conserve la même requête, mais segmente la réponse : ajoutez /stream au chemin, appelez la méthode stream et l’audio est renvoyé sous forme de réponse segmentée. Le code est presque identique et la latence perçue est bien plus faible.
  • Le WebSocket (stream-input) maintient une connexion persistante : vous envoyez le texte progressivement et recevez les segments audio au fil de l’eau. Il est conçu pour les agents interactifs et pour convertir en parole la sortie d’un LLM à mesure que les tokens sont produits, avant même la fin de la phrase.

Le streaming n’accélère pas la génération audio du modèle ; le temps d’inférence reste inchangé. Il modifie le moment où vous recevez le premier segment : celui-ci est envoyé avant la fin de l’extrait complet. L’attente perçue par l’utilisateur est donc plus courte, même si le travail total reste identique.

Text-to-speech API integration guide comparison of batch, HTTP streaming, and WebSocket methods and tradeoffs.

Tableau de décision : lots, streaming ou WebSocket

Plusieurs facteurs sont à prendre en compte pour choisir entre ces trois méthodes.

En bref : choisissez la conversion par lots pour le rendu hors ligne, le streaming HTTP pour un texte connu qu’un utilisateur attend, et le WebSocket pour les agents et la conversion en parole d’un LLM en direct. 

Le tableau ci-dessous détaille les compromis selon les critères importants à grande échelle.

Batch (convert)
Time-to-first-audio
Highest (wait for full clip)
Implementation complexity
Lowest
Text known up front?
Required
Streaming LLM output into TTS
Awkward
Concurrency cost
Each request counts fully
Best for
Offline rendering, audiobooks, caching
HTTP streaming
Time-to-first-audio
Low
Implementation complexity
Low
Text known up front?
Required
Streaming LLM output into TTS
Awkward
Concurrency cost
Each request counts fully
Best for
Web/app playback of known text
WebSocket (stream-input)
Time-to-first-audio
Lowest
Implementation complexity
Highest (connection lifecycle, framing)
Text known up front?
Not required - send incrementally
Streaming LLM output into TTS
Native fit
Concurrency cost
Only active generation counts
Best for
Voice agents, live LLM to speech

Avec HTTP, qu’il s’agisse de conversion par lots ou de streaming, chaque requête en cours compte dans la limite de requêtes simultanées de votre forfait pendant toute sa durée. Avec un WebSocket, seul le temps pendant lequel le modèle génère activement de l’audio est comptabilisé ; un socket ouvert mais inactif ne coûte presque rien.

Pour un agent vocal en cascade qui maintient une connexion ouverte durant toute une conversation, mais ne génère de l’audio que pendant les tours de parole de l’agent, cette différence est importante. C’est la principale raison d’utiliser les WebSockets pour créer des agents. Le protocole complet est décrit dans le guide WebSocket Text to Speech en temps réel.

Choisir un modèle et un format de sortie

Deux choix déterminent l’audio renvoyé par votre intégration d’API TTS. D’abord le modèle, qui définit la qualité et la vitesse. Ensuite le format de sortie, qui définit le conteneur, le débit binaire et la fréquence d’échantillonnage.

Bien les choisir dès le départ garantit que tout le reste, comme la latence et la compatibilité avec la téléphonie, suivra naturellement.

Modèles

Nous proposons plusieurs modèles Text to Speech. Ils ne sont pas classés du meilleur au moins bon : chacun présente des compromis différents.

Best for
eleven_flash_v2_5
Real-time, agents, bulk throughput (~75ms model inference)
eleven_flash_v2
Real-time, English only (~75ms)
eleven_multilingual_v2
Highest stable fidelity, narration
eleven_v4
Most expressive, widest language range
Languages
eleven_flash_v2_5
32
eleven_flash_v2
English
eleven_multilingual_v2
29
eleven_v4
90+
Character limit
eleven_flash_v2_5
40,000
eleven_flash_v2
30,000
eleven_multilingual_v2
10,000
eleven_v4
5,000

À noter : la valeur d’environ 75 ms correspond à l’inférence du modèle dans des conditions représentatives, hors latence réseau et applicative. Elle augmente avec des entrées plus longues et sous charge. Mesurez toujours depuis votre application, et non à partir d’un benchmark.

Les modèles Flash sont plus petits et utilisent des approximations plus poussées pour réduire le temps d’inférence. Eleven v4 et Multilingual v2 sont des modèles plus grands, qui consacrent plus de temps par caractère à produire une sortie plus riche.

Pour un parcours en temps réel ou destiné à un agent, utilisez eleven_flash_v2_5 : c’est l’option multilingue à la latence la plus faible. Pour la narration, les livres audio ou les voix off marketing, utilisez eleven_multilingual_v2 si vous recherchez une haute fidélité stable, ou eleven_v4 si vous avez besoin d’une expressivité et d’une gamme émotionnelle maximales. 

Lorsque la prononciation est importante, notamment pour les numéros de téléphone, les dates ou les devises, normalisez vous-même les nombres dans votre application avant que le texte n’atteigne l’API. Écrivez la forme parlée souhaitée. 

Cette normalisation garantit une prononciation prévisible entre les modèles et évite de dépendre de paramètres par défaut propres aux modèles qui peuvent changer.

Format de sortie

Le paramètre output_format contrôle le conteneur, la fréquence d’échantillonnage et le débit binaire de l’audio renvoyé. Voici les valeurs les plus courantes :

Use case
mp3_44100_128
General playback, downloads, highest mp3 quality shown here
mp3_22050_32
Lower-bandwidth playback, smaller files
pcm_24000 / pcm_16000
Raw PCM for your own audio pipeline or further processing
ulaw_8000
Telephony - the format used with Twilio and similar systems
Languages
mp3_44100_128
32
mp3_22050_32
English
pcm_24000 / pcm_16000
29
ulaw_8000
70+
Character limit
mp3_44100_128
40,000
mp3_22050_32
30,000
pcm_24000 / pcm_16000
10,000
ulaw_8000
5,000

Paramètres vocaux

Les paramètres suivants contrôlent le rendu de la parole générée :

  • Stability : contrôle la cohérence par rapport à l’expressivité. Des valeurs faibles produisent une parole plus variée et expressive, tandis que des valeurs élevées assurent un rendu plus stable et prévisible.
  • SimilarityBoost : contrôle la fidélité de la sortie à la voix de référence.
  • Style : accentue le style de parole naturel de la voix lorsqu’il augmente.
  • useSpeakerBoost : renforce la ressemblance avec le locuteur d’origine, au prix d’une légère augmentation de la latence.
  • Speed : ajuste le rythme de parole autour de la valeur par défaut 1.0.

Parmi ces paramètres, Stability a généralement le plus d’impact sur la qualité perçue. Des valeurs faibles produisent une sortie plus expressive mais moins cohérente, tandis que des valeurs élevées privilégient la cohérence et la prévisibilité.

Pour choisir une voix, la combinaison offrant la plus faible latence est Flash avec un Clonage de voix instantané ou une voix par défaut ; les clonages de voix professionnels offrent un excellent rendu, mais ajoutent une surcharge à chaque génération dont vous devez tenir compte.

Dans ce guide, l’identifiant de voix utilisé dans les exemples est JBFqnCBsd6RMkjVDRZzb (George).

Intégration du streaming (HTTP et WebSocket)

Cette section aborde les aspects pratiques de l’intégration d’une API Text to Speech. Nous couvrons l’installation du SDK, l’ouverture d’un flux et la consommation de l’audio à mesure de son arrivée. La voie HTTP couvre la plupart des lectures web et applicatives, tandis que la voie WebSocket couvre les agents et les sorties de LLM en direct.

Ces deux méthodes supposent que vous avez initialisé le client ElevenLabs ci-dessous.

npm install @elevenlabs/elevenlabs-js
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

La voie streaming ouvre un flux et consomme les segments à mesure de leur arrivée. voiceId est le premier argument positionnel, suivi d’un objet d’options avec des clés en camelCase (modelId, outputFormat, voiceSettings) :

const stream = await elevenlabs.textToSpeech.stream("JBFqnCBsd6RMkjVDRZzb", {
  text,
  modelId: "eleven_flash_v2_5",
  outputFormat: "mp3_44100_128",
  voiceSettings: { stability: 0, similarityBoost: 1.0, style: 0, useSpeakerBoost: true, speed: 1.0 },
});

for await (const chunk of stream) {
  // chunk is a Buffer; feed it to the player as it arrives
}

Pour la variante WebSocket, connectez-vous à wss://api.elevenlabs.io/v1/text-to-speech/{voice_id}/stream-input, envoyez un premier message contenant vos paramètres vocaux et un espace initial, puis envoyez les messages texte à mesure qu’ils sont disponibles et lisez les trames JSON dont le champ audio contient des segments encodés en base64.

Traitement par lots et limites de requêtes simultanées pour un débit élevé

L’intégration à haut débit est régie par les requêtes simultanées, c’est-à-dire le nombre de requêtes qui génèrent de l’audio au même instant. Chaque forfait comporte une limite par famille de modèles. 

Chaque forfait inclut une limite de requêtes simultanées distincte :

  • Free : 4 requêtes Flash simultanées.
  • Starter : 6 requêtes Flash simultanées.
  • Creator : 10 requêtes Flash simultanées.
  • Pro : 20 requêtes Flash simultanées.
  • Scale et Business : 30 requêtes Flash simultanées, avec des limites Enterprise sur mesure.

Les limites de Multilingual v2 sont environ deux fois plus faibles.

Un pool borné résout ce problème en limitant le nombre de requêtes exécutées simultanément :

// Set MAX_CONCURRENCY at or below your plan's Flash concurrency limit.
const MAX_CONCURRENCY = 8;

async function synthMany(texts: string[]): Promise<Buffer[]> {
  const results: Buffer[] = [];
  for (let i = 0; i < texts.length; i += MAX_CONCURRENCY) {
    const batch = texts.slice(i, i + MAX_CONCURRENCY);
    results.push(...(await Promise.all(batch.map(eachSingleRequest)))); // never more than MAX_CONCURRENCY in flight
  }
  return results;

Définissez MAX_CONCURRENCY légèrement en dessous de la limite de votre forfait, plutôt qu’exactement à cette limite. Cette marge absorbe tout autre trafic utilisant la même clé et vous maintient sous le seuil de renvoi d’une erreur 429.

Limites de caractères et découpage des textes longs

Chaque modèle limite le nombre de caractères qu’il accepte dans une requête unique. Toute intégration de contenu long doit découper le texte puis réassembler l’audio. 

Voici les limites de caractères par requête pour chaque modèle :

  • Flash v2.5 : accepte jusqu’à 40 000 caractères par requête.
  • Flash v2 : accepte jusqu’à 30 000 caractères par requête.
  • Multilingual v2 : accepte jusqu’à 10 000 caractères par requête.
  • Eleven v3 : accepte jusqu’à 5 000 caractères par requête.
  • Eleven v4 : accepte jusqu’à 10 000 caractères par requête.

Tout texte plus long doit être réparti sur plusieurs requêtes. Essayez de le découper aux frontières des phrases afin de préserver la prosodie à la jonction des segments.

function splitText(text: string, maxChars: number): string[] {
  const sentences = text.trim().split(/(?<=[.!?])\s+/);
  const chunks: string[] = [];
  let current = "";
  for (let sentence of sentences) {
    if (current.length + sentence.length + 1 > maxChars) {
      if (current) chunks.push(current.trim());
      // A single sentence longer than the limit is hard-split.
      while (sentence.length > maxChars) {
        chunks.push(sentence.slice(0, maxChars));
        sentence = sentence.slice(maxChars);
      }
      current = sentence;
    } else {
      current = `${current} ${sentence}`.trim();
    }
  }
  if (current) chunks.push(current.trim());
  return chunks;
}

Générez les segments dans l’ordre et concaténez l’audio. Pour une narration longue où chaque segment est indépendant, les deux éléments s’assemblent directement : transmettez la sortie de splitText au pool borné ci-dessus et laissez-le gérer le reste.

Mise en cache et idempotence

La sortie Text to Speech est suffisamment déterministe pour que régénérer le même texte avec la même voix, le même modèle et les mêmes paramètres soit inutile. Mettez le résultat en cache à l’aide d’un hash des entrées qui influencent l’audio ; cette même clé sert aussi de jeton d’idempotence lors des nouvelles tentatives.

Voici comment procéder.

import { createHash } from "node:crypto";

function cacheKey(text: string, voiceId: string, modelId: string,
                  outputFormat: string, settings: object): string {
  // Every parameter that changes the audio must be in the key.
  const payload = JSON.stringify({ text, voiceId, modelId, outputFormat, settings });
  return createHash("sha256").update(payload).digest("hex");
}

async function cachedSynth(text: string, voiceId: string, modelId: string,
                           outputFormat: string, settings: object): Promise<Buffer> {
  const key = cacheKey(text, voiceId, modelId, outputFormat, settings);
  const cached = await cacheGet(key);          // e.g. read from disk or S3
  if (cached) return cached;

  const audio = await elevenlabs.textToSpeech.convert(voiceId, { text, modelId, outputFormat });
  await cachePut(key, audio);                   // store the bytes under the key
  return audio;
}

La règle essentielle est que chaque paramètre modifiant l’audio doit être inclus dans la clé, y compris outputFormat et les paramètres vocaux. Correctement appliquée, cette même clé sert de jeton d’idempotence. Lorsqu’un client réessaie une requête déjà réussie, vous renvoyez les octets mis en cache au lieu de générer à nouveau l’audio.

Gestion des erreurs et limites de débit (429)

Un client de production nécessite des nouvelles tentatives avec backoff et jitter, ainsi qu’un traitement qui varie selon le code d’état, car certaines erreurs méritent une nouvelle tentative et d’autres non. 

Le tableau ci-dessous associe chaque état à l’action appropriée, et cette section explique pourquoi une erreur 429 est une limite souple plutôt qu’un mur infranchissable.

Meaning
401
Authentication failed
422
Invalid request
429
Concurrency exceeded
5xx
Transient server error
Action
401
Do not retry. Check the xi-api-key header and key validity.
422
Do not retry. Fix the payload (bad voice id, unsupported format, text over limit).
429
Retry with exponential backoff and jitter.
5xx
Retry with backoff.
Character limit
401
40,000
422
30,000
429
10,000
5xx
5,000

Une erreur 429 n’est pas un mur infranchissable, et il est utile d’en comprendre le mécanisme. Lorsque vous dépassez la limite de requêtes simultanées, les requêtes sont d’abord mises en file d’attente par priorité, ce qui ajoute généralement environ 50 ms. Vous ne recevez une erreur 429 que si vous restez ensuite au-delà de la capacité. 

La réponse contient également les en-têtes current-concurrent-requests et maximum-concurrent-requests, qui indiquent votre marge disponible en temps réel. Vous pouvez donc les lire et réduire la cadence avant d’atteindre la limite.

const RETRYABLE = new Set([429, 500, 502, 503, 504]);

async function synthWithRetry(text: string, voiceId: string, maxRetries = 5): Promise<Buffer> {
  let delay = 500; // ms, base for exponential backoff
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await elevenlabs.textToSpeech.convert(voiceId, {
        text, modelId: "eleven_flash_v2_5", outputFormat: "mp3_44100_128",
      });
    } catch (err: any) {
      const status = err.statusCode;
      // 401/422 and exhausted retries are not recoverable here.
      if (!RETRYABLE.has(status) || attempt === maxRetries) throw err;
      // Exponential backoff with full jitter.
      await new Promise((r) => setTimeout(r, Math.random() * delay));
      delay = Math.min(delay * 2, 8000);
    }
  }
  throw new Error("unreachable");
}

Si vous avez besoin de plus de marge plutôt que d’un meilleur comportement de nouvelle tentative, effectuez un upgrade de votre forfait. Les clients Enterprise peuvent demander des limites plus élevées auprès de leur gestionnaire de compte.

Mesurer la latence et le délai avant le premier octet

La latence dépend de votre région, de votre entrée et de la charge actuelle. Le seul chiffre de latence fiable est donc celui que vous mesurez depuis votre propre environnement. 

Cette section présente le délai avant le premier octet (TTFB) pour l’endpoint de streaming Flash. Elle est structurée pour vous permettre d’utiliser le même banc d’essai avec un autre fournisseur et de les comparer dans des conditions identiques.

Considérez ceci comme une méthodologie, non comme un résultat publié. Une seule exécution ne garantit rien. 

Voici quelques points importants pour mesurer la latence d’une intégration d’API Text to Speech :

  • Incluez l’aller-retour réseau : le TTFB dépend de votre emplacement géographique et du cluster le plus proche du fournisseur. Exécutez donc le test depuis l’emplacement habituel de vos serveurs.
  • Écartez une exécution de préchauffage : la première requête sur une connexion froide est plus lente et peut fausser vos chiffres.
  • Conservez des entrées identiques : la longueur de l’entrée, la voix, le modèle et la charge influencent tous le résultat. Gardez-les donc identiques chez tous les fournisseurs.
  • Publiez une distribution : les chiffres varient d’une exécution à l’autre. Publiez donc la médiane et le p95 plutôt qu’une valeur unique.

Ces éléments pris en compte, vous êtes prêt à mesurer les performances.

const TEXT = "This is a fixed benchmark sentence used for every provider.";

async function measureElevenLabs(): Promise<number> {
  const start = performance.now();
  const res = await fetch(
    "https://api.elevenlabs.io/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb/stream?output_format=mp3_44100_128",
    {
      method: "POST",
      headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY!, "Content-Type": "application/json" },
      body: JSON.stringify({ text: TEXT, model_id: "eleven_flash_v2_5" }),
    },
  );
  for await (const _ of res.body!) {
    return performance.now() - start; // first chunk received
  }
  throw new Error("no audio returned");
}

Pour comparer avec un autre fournisseur, écrivez une fonction de même structure. Exécutez ensuite les deux via un petit lanceur qui ignore un appel de préchauffage, effectue environ 20 échantillons chronométrés espacés pour éviter qu’ils n’entrent en concurrence, puis calcule la médiane et le p95 en millisecondes.

Une comparaison équitable repose sur le contrôle des variables. 

Exécutez les deux fournisseurs depuis la même machine et le même réseau, idéalement depuis un serveur situé dans la région où vous déployez réellement, plutôt que depuis un ordinateur portable sur une connexion résidentielle. Utilisez le même texte d’entrée et gardez l’audio court afin que l’inférence du modèle domine le résultat plutôt que la durée de génération. Communiquez la médiane et le p95 sur de nombreuses exécutions, car une mesure unique n’est que du bruit. 

Gardez à l’esprit que le TTFB sur l’internet public inclut 20 à 200 ms d’aller-retour réseau, sans rapport avec le modèle. Nous opérons des clusters en Amérique du Nord, en Europe et en Asie du Sud-Est, puis acheminons les requêtes vers le plus proche. Placez donc votre client de test à proximité, sinon vous mesurez surtout la distance jusqu’au centre de données.

Points clés pour votre intégration d’API Text to Speech

Une intégration d’API Text to Speech en production repose sur quelques décisions déterminantes.

Si vous les prenez correctement, tout le reste suit :

  • Choisissez le modèle selon l’usage : utilisez Flash v2.5 pour tout usage interactif et un modèle à plus haute fidélité comme Multilingual v2 ou Eleven v4 pour le rendu hors ligne, où la latence importe moins.
  • Diffusez en streaming dès qu’un utilisateur attend : utilisez le streaming HTTP pour les textes connus et un WebSocket pour les agents, afin que le temps d’inactivité ne pèse pas sur votre budget de requêtes simultanées.
  • Limitez votre parallélisme à la limite de votre forfait : plafonnez les requêtes simultanées juste sous la limite de votre forfait et mettez en cache un hash de chaque paramètre influençant la sortie afin que le même audio ne soit jamais facturé deux fois.
  • Réessayez les erreurs 429 et 5xx avec un backoff exponentiel et un jitter complet : réduisez la cadence sur les erreurs 429 et 5xx avec un jitter complet, et surveillez les en-têtes de requêtes simultanées pour voir à quelle distance vous êtes de la limite.
  • Découpez les textes longs aux frontières des phrases : découpez aux frontières des phrases, dans la limite de caractères de chaque modèle, afin que la prosodie survive à la jonction.

Pour aller plus loin, consultez le guide pratique du streaming, le concept de streaming audio, l’authentification, et les jetons à usage unique pour une utilisation côté client.

Créez votre intégration Text to Speech avec ElevenAPI

Après avoir lu ce guide, vous disposez de tous les modèles nécessaires pour une intégration d’API Text to Speech en production. Streaming, traitement par lots, mise en cache, nouvelles tentatives et même benchmarking : vous êtes prêt à passer à l’opérationnel. 

Commencez par en savoir plus sur l’API Text to Speech ou S’inscrire pour effectuer dès aujourd’hui votre premier appel avec ElevenAPI.

FAQ sur l’intégration de l’API Text to Speech

Rédigé par

Tadas Petra est ingénieur en relations développeurs et aide les développeurs à utiliser la suite de produits ElevenLabs. Il est aussi le fondateur de Hungrimind, une plateforme d’apprentissage pour développeurs. Auparavant, il était Lead Developer Advocate chez Agora et formateur chez Zero To Mastery.

Articles similaires

Créez avec l'audio IA de la plus haute qualité