Pular para o conteúdo

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

Publicado
Última atualização

OuvirOuça este artigo

Integrar uma API de Text to Speech é simples... isto é, depois de algumas decisões concretas: qual modo de transferência usar, como escolher um modelo e formato de saída, como fazer streaming, como processar um alto volume sem exceder seu limite de simultaneidade, como armazenar em cache e repetir tentativas para nunca pagar pela geração do mesmo áudio duas vezes e como comparar o tempo até o primeiro byte com outro provedor.

Para ajudar você com a integração da API de Text to Speech, detalhamos cada uma dessas decisões de arquitetura e o que fazer. Este guia vai ajudar você a integrar a API de Text to Speech da ElevenLabs e escalar, com trechos de código que você pode colar em produção para começar a operar.

Para uma explicação completa dos conceitos mencionados aqui, consulte nossos guias sobre como entender o streaming de áudio, como otimizar a latência e a visão geral dos modelos da ElevenLabs

Resumo

  • Há um endpoint da API de Text to Speech da ElevenLabs, que você pode acessar de três formas: conversão em lote, streaming HTTP e WebSocket stream-input.
  • Em HTTP, cada solicitação em andamento conta para seu limite de simultaneidade, enquanto no WebSocket apenas a geração ativa é contabilizada.
  • Limite seu paralelismo a um valor um pouco abaixo do limite do seu plano e armazene em cache um hash de cada parâmetro que afeta a saída, para nunca cobrar pelo mesmo texto duas vezes.
  • Repita tentativas de erros 429 e 5xx com backoff exponencial e jitter completo para reduzir o ritmo antes de atingir o limite de simultaneidade.

Três formas de integrar a API de Text to Speech

Há um endpoint de Text to Speech, mas a forma como você o integra define sua latência, complexidade e custo. 

A mesma chamada POST /v1/text-to-speech/{voice_id} funciona de três formas, e cada uma é adequada para uma tarefa ligeiramente diferente. Confira as três formas de integrar a API de Text to Speech:

  • Lote (convert) é a integração mais simples: Você envia uma solicitação e recebe uma resposta de áudio. É a opção menos complexa e tem o maior tempo até o primeiro áudio, porque o clipe completo é sintetizado antes que qualquer byte seja retornado.
  • O streaming HTTP (stream) mantém a mesma solicitação, mas divide a resposta em partes: Você adiciona /stream ao caminho, chama o método stream e o áudio retorna como uma resposta em partes. O código é quase idêntico, e a latência percebida é bem menor.
  • O WebSocket (stream-input) mantém uma conexão persistente: Você envia texto gradualmente e recebe partes de áudio conforme avança. Ele foi criado para agentes interativos e para converter a saída de um LLM em fala à medida que os tokens são produzidos, antes de a frase terminar.

O streaming não faz o modelo gerar áudio mais rápido; o tempo de inferência não muda. O que o streaming altera é quando você recebe a primeira parte: ela é enviada antes de o clipe completo ficar pronto, então a espera percebida pelo usuário é menor, mesmo que o trabalho total seja o mesmo.

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

Ao decidir entre esses três métodos, há vários fatores que você deve considerar.

Como guia rápido: escolha lote para geração offline, streaming HTTP para texto conhecido pelo qual um usuário está esperando e WebSocket para agentes e conversão ao vivo da saída de LLM em fala. 

A tabela abaixo detalha as vantagens e desvantagens nas dimensões que 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

Em HTTP, seja em lote ou streaming, cada solicitação em andamento conta para o limite de simultaneidade do seu plano durante toda a sua duração. Em um WebSocket, apenas o período em que o modelo está gerando áudio ativamente é contabilizado; um socket aberto, mas ocioso, praticamente não tem custo.

Para um agente de voz em cascata que mantém uma conexão aberta durante toda uma conversa, mas só gera áudio nos turnos do agente, essa diferença é grande e é a principal razão para usar WebSockets ao criar agentes. O protocolo completo está documentado no guia de WebSocket de Text to Speech em tempo real.

Como escolher um modelo e formato de saída

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

Acertar essas duas escolhas desde o início garante que todos os aspectos seguintes, como latência e compatibilidade com telefonia, funcionem bem.

Modelos

Oferecemos vários modelos de Text to Speech. Eles não são classificados 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

Vale observar que o valor de ~75 ms corresponde à inferência do modelo em condições representativas, sem incluir a latência da rede e da aplicação. Ele aumenta com entradas mais longas e sob carga. Sempre meça a partir da sua aplicação, não de um número de benchmark.

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 dedicam mais tempo por caractere para produzir uma saída mais rica. Não há configuração que ofereça a qualidade do Eleven v3 na velocidade do Flash, porque essa qualidade exige computação adicional.

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

Quando a pronúncia for importante, como em números de telefone, datas ou valores monetários, normalize os números na sua aplicação antes que o texto chegue à API. Escreva por extenso a forma falada que você deseja. 

Fazer a normalização você mesmo mantém a pronúncia previsível entre modelos e evita depender de padrões específicos de cada modelo, que podem mudar.

Formato de saída

O parâmetro output_format controla o contêiner, a taxa de amostragem e a taxa de bits do áudio retornado. Os valores que você mais vai usar são:

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 a seguir controlam como a fala gerada é entregue:

  • Stability: Controla a consistência em relação à expressividade. Valores mais baixos produzem uma fala mais variada e expressiva, enquanto valores mais altos oferecem uma entrega mais estável e previsível.
  • SimilarityBoost: Controla o quanto a saída acompanha a voz de referência.
  • Style: Exagera o estilo natural de fala da voz quando elevado.
  • useSpeakerBoost: Aumenta a semelhança com o locutor original com um pequeno impacto na latência.
  • Speed: Ajusta o ritmo da fala em torno do padrão de 1.0.

Entre essas configurações, Stability costuma ter o maior impacto na qualidade percebida. Valores mais baixos criam uma saída mais expressiva, mas menos consistente, enquanto valores mais altos priorizam consistência e previsibilidade.

Ao escolher uma voz, a combinação de menor latência é Flash com um Clone de Voz Instant ou uma voz padrão; Clones de Voz Profissionais têm ótima qualidade, mas adicionam uma sobrecarga por geração que você deve considerar.

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

Integração de streaming (HTTP e WebSocket)

Nesta seção, abordamos o núcleo prático da integração da API de Text to Speech. Explicamos como instalar o SDK, abrir um stream e consumir o áudio conforme ele chega. O caminho HTTP atende à maioria das reproduções na web e em apps, enquanto o WebSocket atende a agentes e saídas de LLM ao vivo.

Ambos os caminhos pressupõem que você inicializou o cliente da ElevenLabs abaixo.

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

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

O caminho de streaming abre um stream e consome as partes conforme elas chegam. voiceId é o primeiro argumento posicional, seguido por 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 variante WebSocket, conecte-se a wss://api.elevenlabs.io/v1/text-to-speech/{voice_id}/stream-input, envie uma primeira mensagem com suas configurações de voz e um espaço inicial, depois envie mensagens de texto conforme elas estiverem disponíveis e leia os frames JSON retornados, cujo campo audio contém partes codificadas em base64.

Processamento em lote e limites de simultaneidade para alto volume

A integração de alto volume é regida pela simultaneidade, ou seja, o número de solicitações que geram áudio no mesmo instante. Cada plano tem um limite por família de modelos. 

Cada plano inclui um limite de simultaneidade distinto:

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

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

Um pool limitado ajuda a resolver isso ao restringir quantas solicitações são executadas de uma vez:

// 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 MAX_CONCURRENCY um pouco abaixo do limite do seu plano, em vez de exatamente nele. Essa margem absorve qualquer outro tráfego que compartilhe a mesma chave e mantém você abaixo do limite em que um 429 é retornado.

Limites de caracteres e divisão de textos longos

Cada modelo limita o número de caracteres que aceita em uma única solicitação. Qualquer integração com conteúdo longo precisa dividir o texto e unir o áudio novamente. 

Estes são os limites de caracteres por solicitação de cada modelo:

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

Qualquer texto maior precisa ser dividido em várias solicitações. Procure dividir nos limites de frases para que a prosódia seja preservada entre as partes.

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 as partes em ordem e concatene o áudio. Para narrações longas em que cada parte é independente, as duas etapas se combinam diretamente: envie a saída de splitText para o pool limitado acima e deixe-o cuidar do restante.

Cache e idempotência

A saída de Text to Speech é suficientemente determinística para que gerar novamente o mesmo texto com a mesma voz, modelo e configurações seja desperdício. Armazene o resultado em cache usando um hash das entradas que afetam o áudio, e a mesma chave também funciona como um token de idempotência nas novas 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 que faz isso funcionar é que todos os parâmetros que alteram o áudio precisam estar na chave, incluindo outputFormat e as configurações de voz. Quando feito corretamente, a mesma chave também funciona como um token de idempotência. Quando um cliente repete uma solicitação que já teve sucesso, você retorna os bytes em cache em vez de gerar novamente.

Tratamento de erros e limites de taxa (429)

Um cliente em produção precisa repetir tentativas com backoff e jitter, além de tratamento que varia conforme o código de status, pois algumas falhas merecem uma nova tentativa e outras não. 

A tabela abaixo relaciona cada status à ação correta, e a seção explica por que um 429 é um limite flexível, não uma barreira rígida.

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 é uma barreira rígida, e é útil entender o mecanismo. Quando você ultrapassa o limite de simultaneidade, as solicitações primeiro entram em fila por prioridade, o que normalmente acrescenta cerca de 50 ms. Você só recebe um 429 se ainda estiver acima da capacidade depois disso. 

A resposta também traz os cabeçalhos current-concurrent-requests e maximum-concurrent-requests, que mostram sua margem disponível em tempo real. Assim, você pode consultá-los e reduzir o ritmo 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");
}

Quando você precisa de mais margem, e não de um comportamento de novas tentativas melhor, faça upgrade do seu plano. Clientes Enterprise podem solicitar limites maiores por meio do gerente de conta.

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

A latência depende da sua região, da sua entrada e da carga atual. Isso significa que o único número de latência em que vale confiar é aquele que você mediu 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 que você possa usar o mesmo teste com outro provedor e compará-los em condições idênticas.

Trate isso como uma metodologia, não como um resultado publicado. Uma única execução não garante nada. 

Veja alguns pontos importantes ao comparar a latência de uma integração da API de Text to Speech:

  • Inclua o tempo de ida e volta da rede: O TTFB depende da sua localização geográfica e do cluster mais próximo do provedor. Portanto, execute o teste de onde seus servidores normalmente operam.
  • Descarte uma execução de aquecimento: A primeira solicitação em uma conexão fria é mais lenta e pode distorcer seus números.
  • Mantenha as entradas fixas: O tamanho da entrada, a voz, o modelo e a carga influenciam o resultado, então mantenha-os idênticos entre os provedores.
  • Informe uma distribuição: Os números variam entre execuções, então publique a mediana e o p95 em vez de um único valor.

Com esses pontos em mente, você está pronto para fazer o benchmark.

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 com a mesma estrutura. Depois, execute ambas com um pequeno script que descarte uma chamada de aquecimento, colete cerca de 20 amostras cronometradas e espaçadas para que não concorram entre si, e informe a mediana e o p95 em milissegundos.

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

Execute ambos os provedores na mesma máquina e rede, idealmente em um servidor na região em que você realmente faz a implantação, e não em um notebook com internet residencial. Use o mesmo texto de entrada e mantenha o áudio curto para que a inferência do modelo tenha mais peso no número que a duração da geração. Informe a mediana e o p95 em várias execuções, pois uma única medição é ruído. 

Lembre-se de que o TTFB pela internet pública inclui de 20 a 200 ms de ida e volta pela rede, algo que não tem relação com o modelo. Atendemos a partir de clusters na América do Norte, Europa e Sudeste Asiático, direcionando para o mais próximo. Portanto, posicione seu cliente de teste de acordo; caso contrário, você estará principalmente medindo a distância até o data center.

Principais pontos para sua integração da API de Text to Speech

Uma integração de API de Text to Speech em produção se resume a algumas decisões importantes.

Se você acertar essas decisões, todo o resto se encaixa:

  • Escolha o modelo conforme a tarefa: Use Flash v2.5 para qualquer uso interativo e um modelo de maior fidelidade, como Multilingual v2 ou Eleven v3 para geração offline, em que a latência importa menos.
  • Use streaming sempre que um usuário estiver esperando: Use streaming HTTP para textos conhecidos e WebSocket para agentes, para que o tempo ocioso não consuma seu orçamento de simultaneidade.
  • Limite seu paralelismo ao limite do seu plano: Restrinja as solicitações simultâneas a um valor um pouco abaixo do limite do plano e armazene em cache um hash de todos os parâmetros que afetam a saída, para que o mesmo áudio nunca seja cobrado duas vezes.
  • Repita tentativas de 429 e 5xx com backoff exponencial e jitter completo: Reduza o ritmo em respostas 429 e 5xx com jitter completo e acompanhe os cabeçalhos de simultaneidade para saber o quão perto você está do limite.
  • Divida textos longos nos limites de frases: Divida nos limites de frases dentro do limite de caracteres de cada modelo para que a prosódia seja preservada entre as partes.

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

Crie sua integração de Text to Speech com a ElevenAPI

Depois de ler este guia, você terá todos os padrões necessários para uma integração de API de Text to Speech em produção. Com streaming, processamento em lote, cache, novas tentativas e até benchmarking, você está pronto para colocar tudo em operação. 

Comece sabendo mais sobre a API de Text to Speech ou cadastre-se para fazer hoje sua primeira chamada com a ElevenAPI.

Perguntas frequentes sobre a integração da API de Text to Speech

Artigos relacionados

Crie com o áudio de IA da mais alta qualidade