Passer au contenu

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

Publié

ÉcouterÉcouter cet article

Intégrer une API Text to Speech est simple… une fois que vous avez pris quelques décisions concrètes : quel mode de transfert utiliser, comment choisir un modèle et un format de sortie, comment gérer le streaming, comment traiter un gros volume sans dépasser votre limite de concurrence, comment mettre en cache et réessayer pour ne jamais payer deux fois pour le même audio, et comment comparer le temps de réponse avec un autre fournisseur.

Pour vous aider à intégrer l’API Text to Speech, nous avons détaillé chacune de ces décisions techniques et expliqué quoi faire. Ce guide vous aidera à intégrer l’API Text to Speech d’ElevenLabs Text to Speech API et à passer à l’échelle, avec des extraits de code prêts à l’emploi pour accélérer la mise en production.

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

Résumé

  • Il existe un seul point de terminaison pour l’API Text to Speech d’ElevenLabs, accessible de trois façons : conversion par lots, streaming HTTP et WebSocket en entrée continue.
  • En HTTP, chaque requête en cours compte dans votre limite de concurrence, alors qu’en WebSocket, seule la génération active est prise en compte.
  • Gardez votre parallélisme juste en dessous de la limite de votre offre, et mettez en cache un hash de chaque paramètre qui influence le rendu pour ne jamais facturer deux fois le même texte.
  • Réessayez les erreurs 429 et 5xx avec un backoff exponentiel et du jitter pour éviter d’atteindre la limite de concurrence.

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

Il n’y a qu’un seul point de terminaison Text to Speech, mais la façon dont vous l’intégrez influence la latence, la complexité et le coût.

L’appel POST /v1/text-to-speech/{voice_id} fonctionne sous trois formes, chacune adaptée à un usage différent. Voici un aperçu des trois méthodes d’intégration de l’API Text to Speech :

  • Le traitement 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 plus simple, mais le temps avant le premier audio est le plus long, car tout le clip est synthétisé avant de recevoir le moindre octet.
  • Le streaming HTTP (stream) garde la même requête mais découpe la réponse : Vous ajoutez /stream au chemin, appelez la méthode stream, et l’audio arrive en morceaux. Le code est presque identique, mais la latence perçue est bien plus faible.
  • Le WebSocket (stream-input) maintient une connexion persistante : Vous envoyez le texte progressivement et recevez les morceaux audio au fur et à mesure. C’est conçu pour les agents interactifs et pour transformer la sortie d’un LLM en voix dès que les tokens sont produits, avant la fin de la phrase.

Le streaming ne rend pas la génération audio plus rapide ; le temps d’inférence reste le même. Ce qui change, c’est le moment où vous recevez le premier morceau : il est envoyé avant que le clip complet soit prêt, donc l’attente perçue par l’utilisateur est plus courte même si le travail total reste identique.

Tableau comparatif : traitement par lots, streaming et WebSocket

Pour choisir entre ces trois méthodes, plusieurs critères sont à prendre en compte.

En résumé : choisissez le traitement 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 direct d’un LLM en voix.

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

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

En HTTP, que ce soit par lots ou en streaming, chaque requête en cours compte dans la limite de concurrence de votre offre pendant toute sa durée. En WebSocket, seul le temps de génération audio est compté ; une socket ouverte mais inactive ne consomme presque rien.

Pour un agent vocal en cascade qui garde une connexion ouverte pendant toute la conversation mais ne génère de l’audio que lors des tours de l’agent, la différence est importante, et c’est la principale raison d’utiliser les WebSockets pour les agents. Le protocole complet est documenté 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 que vous recevez de votre intégration TTS : le modèle, qui définit la qualité et la vitesse, et le format de sortie, qui définit le conteneur, le débit et la fréquence d’échantillonnage.

Bien choisir ces deux paramètres dès le départ garantit que tout le reste, comme la latence ou la compatibilité téléphonie, fonctionnera correctement.

Modèles

Nous proposons plusieurs modèles Text to Speech. Ils ne sont pas classés du meilleur au moins bon ; chacun fait 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_v3
Most expressive, widest language range
Languages
eleven_flash_v2_5
32
eleven_flash_v2
English
eleven_multilingual_v2
29
eleven_v3
70+
Character limit
eleven_flash_v2_5
40,000
eleven_flash_v2
30,000
eleven_multilingual_v2
10,000
eleven_v3
5,000

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

Les modèles Flash sont plus petits et utilisent des approximations plus poussées pour réduire le temps d’inférence. Eleven v3 et Multilingual v2 sont plus volumineux et passent plus de temps par caractère pour un rendu plus riche. Il n’existe pas de réglage permettant d’obtenir la qualité d’Eleven v3 à la vitesse de Flash, car cette qualité demande plus de calcul.

Pour un usage en temps réel ou pour un agent, utilisez eleven_flash_v2_5 ; c’est l’option multilingue la plus rapide. Pour la narration, les livres audio ou la voix off marketing, choisissez eleven_multilingual_v2 pour une fidélité stable, ou eleven_v3 pour un maximum d’expressivité et d’émotion.

Quand la prononciation est importante, par exemple pour les numéros de téléphone, dates ou montants, faites la normalisation des nombres dans votre application avant d’envoyer le texte à l’API. Écrivez la forme parlée souhaitée.

Faire la normalisation vous-même garantit une prononciation prévisible sur tous les modèles et évite de dépendre de valeurs par défaut qui pourraient changer.

Format de sortie

Le paramètre output_format contrôle le conteneur, la fréquence d’échantillonnage et le débit de l’audio généré. 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

Réglages de la voix

Les paramètres suivants contrôlent la façon dont la voix générée est rendue :

  • Stability : Gère l’équilibre entre cohérence et expressivité. Une valeur basse donne une voix plus expressive et variée, une valeur haute donne un rendu plus stable et prévisible.
  • SimilarityBoost : Contrôle la ressemblance avec la voix de référence.
  • Style : Accentue le style naturel de la voix quand la valeur est élevée.
  • useSpeakerBoost : Renforce la ressemblance avec le locuteur d’origine, avec un léger impact sur la latence.
  • Speed : Ajuste la vitesse de lecture autour de la valeur par défaut 1.0.

Parmi ces réglages, Stability a généralement le plus d’impact sur la qualité perçue. Une valeur basse donne un rendu plus expressif mais moins constant, une valeur haute privilégie la cohérence et la prévisibilité.

Pour une latence minimale, choisissez Flash avec un Instant Voice Clone ou une voix par défaut ; les Voice Clones professionnels offrent une excellente qualité mais ajoutent un délai supplémentaire à chaque génération.

Dans ce guide, l’identifiant de voix utilisé en exemple est JBFqnCBsd6RMkjVDRZzb (George).

Intégration en streaming (HTTP et WebSocket)

Dans cette section, nous abordons la partie pratique de l’intégration de l’API Text to Speech : installation du SDK, ouverture d’un flux et lecture de l’audio au fur et à mesure. Le chemin HTTP couvre la plupart des usages web et mobiles, tandis que le WebSocket est adapté aux agents et à la sortie LLM en direct.

Ces deux méthodes supposent que le client ElevenLabs est initialisé comme ci-dessous.

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

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

Le mode streaming ouvre un flux et lit les morceaux d’audio dès qu’ils arrivent. voiceId est le premier argument, 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 avec vos réglages de voix et un espace en tête, puis envoyez les textes au fur et à mesure et lisez les frames JSON dont le champ audio contient les morceaux encodés en base64.

Traitement par lots et limites de concurrence pour un débit élevé

L’intégration à haut débit dépend de la concurrence, c’est-à-dire du nombre de requêtes générant de l’audio en même temps. Chaque offre a une limite par famille de modèles.

Chaque offre inclut une limite de concurrence spécifique :

  • Gratuit : 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 personnalisées pour l’Enterprise.

Les limites pour Multilingual v2 sont environ la moitié de celles-ci.

Un pool limité permet de contrôler combien de requêtes sont lancées en même temps :

// 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;

Réglez MAX_CONCURRENCY un peu en dessous de la limite de votre offre, pas exactement dessus. Cette marge absorbe le trafic partagé sur la même clé et vous évite d’atteindre la limite où une erreur 429 est renvoyée.

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

Chaque modèle limite le nombre de caractères acceptés par requête. Toute intégration de texte long doit découper le texte et recoller l’audio ensuite.

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

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

Tout texte plus long doit être découpé en plusieurs requêtes. Essayez de couper aux fins de phrases pour que la prosodie reste naturelle entre les morceaux.

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;
}

Rendez les morceaux dans l’ordre et concaténez l’audio. Pour la narration longue où chaque morceau est indépendant, les deux étapes s’enchaînent simplement : envoyez le texte découpé dans le pool limité ci-dessus et laissez-le gérer la suite.

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 réglages soit inutile. Mettez le résultat en cache avec une clé basée sur un hash des entrées qui influencent l’audio, et cette même clé sert de jeton d’idempotence lors des réessais.

Voici comment faire les deux.

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 à suivre est que chaque paramètre qui modifie l’audio doit être inclus dans la clé, y compris outputFormat et les réglages de voix. Si c’est bien fait, la même clé sert de jeton d’idempotence. Quand un client réessaie une requête déjà réussie, vous renvoyez les octets mis en cache au lieu de régénérer.

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

Un client en production doit gérer les réessais avec backoff et jitter, et adapter le traitement selon le code de statut, car certaines erreurs méritent un nouvel essai et d’autres non.

Le tableau ci-dessous associe chaque statut à la bonne action, et la section explique pourquoi une erreur 429 est une limite souple et non 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 : il est utile de comprendre le mécanisme. Si vous dépassez la limite de concurrence, les requêtes sont d’abord mises en file d’attente selon leur priorité, ce qui ajoute généralement 50 ms. Ce n’est que si la capacité est toujours dépassée que vous recevez une 429.

La réponse inclut aussi les en-têtes current-concurrent-requests et maximum-concurrent-requests qui indiquent votre marge en temps réel, pour que vous puissiez ralentir 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 réessai, passez à une offre supérieure. Les clients Enterprise peuvent demander des limites plus élevées via leur gestionnaire de compte.

Mesurer la latence et le temps jusqu’au premier octet

La latence dépend de votre région, de votre entrée et de la charge actuelle, donc la seule valeur fiable est celle mesurée dans votre propre environnement.

Cette section vous donne le temps jusqu’au premier octet (TTFB) pour le point de terminaison Flash en streaming, et elle est conçue pour que vous puissiez comparer avec un autre fournisseur dans les mêmes conditions.

Considérez cela comme une méthode, pas comme un résultat officiel. Aucun test unique ne garantit quoi que ce soit.

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

  • Inclure l’aller-retour réseau : Le TTFB dépend de votre géolocalisation et du cluster le plus proche du fournisseur, donc lancez le test depuis l’endroit où tournent vos serveurs.
  • Écarter un appel de chauffe : La première requête sur une connexion froide est plus lente et peut fausser vos mesures.
  • Fixer les entrées : La longueur du texte, la voix, le modèle et la charge influencent le résultat, donc gardez-les identiques pour chaque fournisseur.
  • Publier une distribution : Les chiffres varient d’un test à l’autre, donc publiez la médiane et le p95 plutôt qu’une seule valeur.

Avec ces précautions, vous pouvez lancer vos mesures.

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 forme. Faites tourner les deux via un petit script qui écarte un appel de chauffe, prend environ 20 échantillons espacés pour éviter les collisions, et affiche la médiane et le p95 en millisecondes.

Une comparaison juste dépend du contrôle des variables.

Faites tourner les deux fournisseurs depuis la même machine et le même réseau, idéalement un serveur dans la région où vous déployez vraiment, plutôt qu’un ordinateur portable sur une connexion résidentielle. Utilisez le même texte d’entrée, et gardez l’audio court pour que l’inférence du modèle domine le résultat. Publiez la médiane et le p95 sur plusieurs essais, car une seule mesure n’est pas représentative.

Gardez en tête que le TTFB sur Internet public inclut 20 à 200 ms d’aller-retour réseau qui n’ont rien à voir avec le modèle. Nous servons depuis des clusters en Amérique du Nord, Europe et Asie du Sud-Est et nous routons vers le plus proche, donc placez votre client de test en conséquence, sinon vous mesurez surtout la distance au data center.

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

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

Si vous faites les bons choix, tout le reste s’enchaîne :

  • Choisissez le modèle selon l’usage : Utilisez Flash v2.5 pour tout ce qui est interactif et un modèle plus fidèle comme Multilingual v2 ou Eleven v3 pour le rendu hors ligne où la latence est moins critique.
  • Utilisez le streaming dès qu’un utilisateur attend : Utilisez le streaming HTTP pour un texte connu et le WebSocket pour les agents, afin que le temps d’inactivité ne compte pas dans votre budget de concurrence.
  • Adaptez votre parallélisme à la limite de votre offre : Limitez les requêtes simultanées juste en dessous de la limite de votre offre, et mettez en cache sur un hash de chaque paramètre qui influence l’audio pour ne jamais facturer deux fois le même rendu.
  • Réessayez les erreurs 429 et 5xx avec backoff exponentiel et jitter : Faites un backoff sur les erreurs 429 et 5xx avec du jitter, et surveillez les en-têtes de concurrence pour savoir où vous en êtes par rapport à la limite.
  • Découpez les textes longs aux fins de phrases : Coupez aux fins de phrases dans la limite de caractères de chaque modèle pour que la prosodie reste naturelle.

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

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

Après avoir lu ce guide, vous avez toutes les bases pour une intégration API Text to Speech en production. Que ce soit pour le streaming, le traitement par lots, la mise en cache, la gestion des erreurs ou même la mesure des performances, vous êtes prêt à passer à l’action.

Commencez par en savoir plus sur l’API Text to Speech ou inscrivez-vous pour passer votre premier appel avec ElevenAPI dès aujourd’hui.

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

Articles similaires

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