Pular para o conteúdo

Integração com a API de Transformar Texto em Áudio: streaming, processamento em lote e tentativas

Publicado

OuvirOuça este artigo

Integrar uma API de Transformar Texto em Áudio é simples… depois de algumas decisões importantes: qual modo de transferência usar, como escolher o modelo e o formato de saída, como fazer streaming, como processar grandes volumes sem ultrapassar o limite de concorrência, como fazer cache e tentativas para nunca pagar duas vezes pelo mesmo áudio, e como comparar o tempo de resposta com outros provedores.

Para ajudar na integração com a API de Transformar Texto em Áudio, detalhamos cada uma dessas decisões de arquitetura e o que fazer em cada caso. Este guia vai ajudar você a integrar a ElevenLabs API de Transformar Texto em Áudio e escalar, com exemplos de código prontos para usar em produção.

Para entender melhor os conceitos citados aqui, veja nossos guias sobre como funciona o streaming de áudio, otimização de latência e a visão geral dos modelos da ElevenLabs.

Resumo

  • Existe um único endpoint da API de Transformar Texto em Áudio da ElevenLabs, que pode ser acessado de três formas: conversão em lote, streaming HTTP e WebSocket com entrada em streaming.
  • No HTTP, cada requisição em andamento conta para o seu limite de concorrência, enquanto no WebSocket só conta o tempo em que o áudio está sendo gerado.
  • Mantenha o paralelismo um pouco abaixo do limite do seu plano e faça cache de um hash de todos os parâmetros que afetam o áudio, assim você nunca paga duas vezes pelo mesmo texto.
  • Tente novamente erros 429 e 5xx usando backoff exponencial e jitter para evitar atingir o limite de concorrência.

Três formas de integrar a API de Transformar Texto em Áudio

Existe um endpoint de Transformar Texto em Áudio, mas a forma como você integra afeta latência, complexidade e custo.

A mesma chamada POST /v1/text-to-speech/{voice_id} funciona de três maneiras, cada uma indicada para um tipo de uso. Veja um resumo das três formas de integrar a API de Transformar Texto em Áudio:

  • Lote (convert) é a integração mais simples: Você envia uma requisição e recebe um áudio de volta. É a opção mais simples, mas tem o maior tempo até o primeiro áudio, pois o clipe completo é gerado antes de retornar qualquer byte.
  • Streaming HTTP (stream) mantém a mesma requisição, mas divide a resposta em partes: Você adiciona /stream ao caminho, chama o método de streaming e o áudio retorna em partes. O código é quase igual, mas a latência percebida é bem menor.
  • O WebSocket (stream-input) mantém uma conexão aberta: Você envia o texto aos poucos e recebe partes do áudio conforme são geradas. É ideal para agentes interativos e para transformar a saída de um LLM em voz enquanto os tokens são produzidos, antes da frase terminar.

O streaming não faz o modelo gerar áudio mais rápido; o tempo de inferência é o mesmo. O que muda é quando você recebe o primeiro pedaço: ele chega antes do clipe completo, então o usuário espera menos, mesmo que o trabalho total seja igual.

Tabela de decisão: lote vs. streaming vs. WebSocket

Ao escolher entre esses três métodos, alguns fatores devem ser considerados.

Como dica rápida: use lote para renderização offline, streaming HTTP para texto conhecido que o usuário está esperando e WebSocket para agentes e LLM ao vivo.

A tabela abaixo mostra os prós e contras de cada método nos pontos que mais importam em escala.

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

No HTTP, seja em lote ou streaming, cada requisição em andamento conta para o limite de concorrência do seu plano durante todo o tempo de execução. No WebSocket, só conta o tempo em que o modelo está gerando áudio; uma conexão aberta, mas ociosa, praticamente não consome recursos.

Para um agente de voz em cascata que mantém a conexão aberta durante toda a conversa, mas só gera áudio na vez do agente, essa diferença é grande — e é o principal motivo para usar WebSockets ao criar agentes. O protocolo completo está documentado no guia de WebSocket para Transcrição de Voz em Tempo Real.

Escolhendo modelo e formato de saída

Duas escolhas definem o áudio que você recebe da integração com a API de TTS. Primeiro, o modelo, que determina qualidade e velocidade. Segundo, o formato de saída, que define o container, bitrate e taxa de amostragem.

Acertar essas duas escolhas desde o início garante que tudo depois, como latência e compatibilidade com telefonia, funcione bem.

Modelos

Oferecemos vários modelos de Transformar Texto em Áudio. Não existe um ranking do melhor ao pior; cada um faz escolhas diferentes.

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

O valor de ~75ms é o tempo de inferência do modelo em condições típicas, sem contar latência de rede e aplicação. Esse tempo aumenta com entradas maiores e sob carga. Sempre meça a partir da sua aplicação, não de um número de referência.

Os modelos Flash são menores e usam aproximações mais agressivas para reduzir o tempo de inferência. Eleven v3 e Multilingual v2 são modelos maiores, que gastam mais tempo por caractere para gerar um áudio mais rico. Não existe configuração que dê a qualidade do Eleven v3 com a velocidade do Flash, pois a qualidade exige mais processamento.

Para uso em tempo real ou com agentes, use o eleven_flash_v2_5, que é a opção multilíngue de menor latência. Para narração, audiolivros ou locução de marketing, use o eleven_multilingual_v2 quando quiser alta fidelidade estável, ou o eleven_v3 quando precisar de máxima expressividade e emoção.

Quando a pronúncia for importante, como em números de telefone, datas ou valores, faça a normalização dos números na sua aplicação antes de enviar para a API. Escreva o texto exatamente como quer que seja falado.

Normalizar por conta própria garante pronúncia previsível entre modelos e evita depender de padrões específicos que podem mudar.

Formato de saída

O parâmetro output_format controla o container, taxa de amostragem e bitrate do áudio gerado. Os valores mais usados:

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

Configurações de voz

As configurações abaixo controlam como a fala gerada é entregue:

  • Stability: Controla consistência versus expressividade. Valores baixos geram fala mais variada e expressiva; valores altos deixam a entrega mais estável e previsível.
  • SimilarityBoost: Controla o quanto a saída se aproxima da voz de referência.
  • Style: Exagera o estilo natural de fala da voz quando aumentado.
  • useSpeakerBoost: Aumenta a semelhança com o locutor original, com um pequeno custo de latência.
  • Speed: Ajusta a velocidade da fala em torno do padrão 1.0.

Entre essas configurações, Stability costuma ter o maior impacto na qualidade percebida. Valores baixos criam resultados mais expressivos, mas menos consistentes; valores altos priorizam consistência e previsibilidade.

Ao escolher uma voz, a combinação de menor latência é Flash com uma Voz Instantânea Clonada ou uma voz padrão; Vozes Profissionais Clonadas têm ótima qualidade, mas adicionam um tempo extra que você deve considerar.

Neste guia, o id de voz de exemplo é JBFqnCBsd6RMkjVDRZzb (George).

Integração com streaming (HTTP e WebSocket)

Nesta seção, mostramos na prática como integrar a API de Transformar Texto em Áudio. Vamos instalar o SDK, abrir um stream e consumir o áudio conforme ele chega. O caminho HTTP cobre a maioria dos usos em web e app, enquanto o WebSocket é ideal para agentes e saída ao vivo de LLM.

Ambos os caminhos assumem que você já inicializou o cliente ElevenLabs conforme abaixo.

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

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

No caminho de streaming, você abre um stream e consome os pedaços conforme chegam. voiceId é o primeiro argumento, seguido de um objeto de opções com chaves em 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
}

Na versão WebSocket, conecte em wss://api.elevenlabs.io/v1/text-to-speech/{voice_id}/stream-input, envie uma primeira mensagem com as configurações de voz e um espaço inicial, depois envie mensagens de texto conforme disponíveis e leia os frames JSON cujo campo audio traz os pedaços em base64.

Processamento em lote e limites de concorrência para alto volume

A integração de alto volume depende da concorrência, ou seja, o número de requisições gerando áudio ao mesmo tempo. Cada plano tem um limite por família de modelo.

Cada plano tem um limite de concorrência distinto:

  • Grátis: 4 requisições Flash simultâneas.
  • Starter: 6 requisições Flash simultâneas.
  • Creator: 10 requisições Flash simultâneas.
  • Pro: 20 requisições Flash simultâneas.
  • Scale e Business: 30 requisições Flash simultâneas, com limites personalizados para Enterprise.

Os limites do Multilingual v2 são cerca de metade dos valores acima.

Um pool limitado resolve isso ao restringir quantas requisições rodam ao mesmo tempo:

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

Defina o MAX_CONCURRENCY um pouco abaixo do limite do seu plano, não exatamente nele. Essa margem absorve outros acessos com a mesma chave e evita atingir o ponto em que um 429 é retornado.

Limites de caracteres e divisão de textos longos

Cada modelo tem um limite de caracteres por requisição. Qualquer integração de texto longo precisa dividir o texto e juntar o áudio depois.

Veja os limites de caracteres por requisição para cada modelo:

  • Flash v2.5: Aceita até 40.000 caracteres por requisição.
  • Flash v2: Aceita até 30.000 caracteres por requisição.
  • Multilingual v2: Aceita até 10.000 caracteres por requisição.
  • Eleven v3: Aceita até 5.000 caracteres por requisição.

Textos maiores precisam ser divididos em várias requisições. Sempre que possível, divida em limites de frases para manter a prosódia entre os trechos.

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

Gere os trechos em ordem e junte o áudio. Para narração longa em que cada parte é independente, basta alimentar a saída do splitText no pool limitado acima e deixar ele cuidar do resto.

Cache e idempotência

A saída de Transformar Texto em Áudio é suficientemente determinística para que gerar novamente o mesmo texto com a mesma voz, modelo e configurações seja desperdício. Faça cache do resultado usando um hash dos parâmetros que afetam o áudio; essa mesma chave serve como token de idempotência em tentativas.

Veja como fazer as duas coisas.

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

A regra é: todo parâmetro que muda o áudio deve estar na chave, incluindo outputFormat e configurações de voz. Feito corretamente, a mesma chave serve como token de idempotência. Se o cliente repetir uma requisição já bem-sucedida, você retorna o áudio em cache em vez de gerar de novo.

Tratamento de erros e limites de taxa (429)

Um cliente em produção precisa de tentativas com backoff e jitter, além de tratamento diferente para cada código de status, pois alguns erros valem a tentativa e outros não.

A tabela abaixo mostra a ação correta para cada status, e a seção explica por que o 429 é um limite flexível, não um bloqueio total.

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

Um 429 não é um bloqueio total, e entender o mecanismo ajuda. Quando você passa do limite de concorrência, as requisições entram em fila por prioridade, o que normalmente adiciona cerca de 50ms. Só se ainda estiver acima da capacidade depois disso é que você recebe um 429.

A resposta também traz os headers current-concurrent-requests e maximum-concurrent-requests, que mostram sua margem em tempo real — assim, você pode ajustar antes de atingir o 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");
}

Se precisar de mais margem, em vez de melhorar as tentativas, faça upgrade do seu plano. Clientes Enterprise podem solicitar limites maiores com o gerente de conta.

Benchmark de latência e tempo até o primeiro byte

A latência depende da sua região, do texto enviado e da carga do momento, então o único número confiável é o que você mede no seu próprio ambiente.

Esta seção mostra o tempo até o primeiro byte (TTFB) para o endpoint de streaming Flash, e foi estruturada para você comparar com outros provedores sob as mesmas condições.

Considere isso como uma metodologia, não como resultado publicado. Nenhuma execução única garante nada.

Veja alguns pontos importantes ao medir a latência de uma integração com API de Transformar Texto em Áudio:

  • Inclua o tempo de ida e volta da rede: O TTFB depende da sua localização e do cluster mais próximo do provedor, então rode o teste de onde seus servidores normalmente estão.
  • Descarte a primeira execução: A primeira requisição em uma conexão fria é mais lenta e pode distorcer os resultados.
  • Mantenha as entradas fixas: Tamanho do texto, voz, modelo e carga afetam o resultado, então mantenha tudo igual entre os provedores.
  • Reporte uma distribuição: Os números variam a cada execução, então publique a mediana e o p95 em vez de um valor só.

Com esses pontos em mente, você está pronto para medir.

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

Para comparar com outro provedor, escreva uma função igual para ambos. Depois, rode ambos em um script que descarta uma chamada de aquecimento, faz cerca de 20 amostras espaçadas para não colidir e reporta a mediana e o p95 em milissegundos.

Uma comparação justa depende de controlar as variáveis.

Rode ambos os provedores na mesma máquina e rede, de preferência em um servidor na região onde você realmente faz o deploy, não em um notebook residencial. Use o mesmo texto de entrada e mantenha o áudio curto para que a inferência do modelo seja o principal fator. Reporte a mediana e o p95 em várias execuções, pois uma medição isolada é ruído.

Lembre-se que o TTFB pela internet pública inclui 20-200ms de ida e volta de rede, que não têm relação com o modelo. Atendemos a partir de clusters na América do Norte, Europa e Sudeste Asiático, sempre roteando para o mais próximo, então rode o teste no local certo — senão, você estará medindo só a distância até o data center.

Principais pontos para sua integração com a API de Transformar Texto em Áudio

Uma integração de produção com a API de Transformar Texto em Áudio depende de algumas decisões importantes.

Se você acertar nesses pontos, o resto flui naturalmente:

  • Escolha o modelo conforme o uso: Use Flash v2.5 para interatividade e um modelo de maior fidelidade, como Multilingual v2 ou Eleven v3 para renderização offline, onde a latência não é tão crítica.
  • Use streaming sempre que o usuário estiver esperando: Use streaming HTTP para texto conhecido e WebSocket para agentes, assim o tempo ocioso não consome seu limite de concorrência.
  • Limite o paralelismo ao limite do seu plano: Restrinja as requisições simultâneas um pouco abaixo do limite do plano e faça cache com hash de todos os parâmetros que afetam o áudio, para nunca pagar duas vezes pelo mesmo áudio.
  • Tente novamente 429 e 5xx com backoff exponencial e jitter: Diminua o ritmo em 429 e 5xx com jitter, e monitore os headers de concorrência para saber o quão perto está do limite.
  • Divida textos longos em limites de frases: Quebre nos limites de frases dentro do limite de caracteres de cada modelo, para manter a prosódia entre os trechos.

Se quiser se aprofundar ainda mais, confira o guia prático de streaming, conceito de streaming de áudio, autenticação e tokens de uso único para uso no cliente.

Crie sua integração de Transformar Texto em Áudio com ElevenAPI

Depois de ler este guia, você tem todos os padrões necessários para uma integração de produção com a API de Transformar Texto em Áudio. Seja para streaming, processamento em lote, cache, tentativas ou até benchmark, você já pode colocar em prática.

Comece aprendendo mais sobre a API de Transformar Texto em Áudio ou crie sua conta para fazer sua primeira chamada com a ElevenAPI hoje mesmo.

Perguntas frequentes sobre integração com a API de Transformar Texto em Áudio

Artigos relacionados

Crie com o áudio de IA da mais alta qualidade