HiAPI
Blogue

Tutorial

Como chamar o gemini-3.1-flash-tts na HiAPI: curl, Python e uma requisição executável

Oct 31, 2026
深靛蓝色背景上多层嵌套的宽箭头依次向右上方推进,箭头前缘为杏橙色,构成规整的信号流示意图形,这是文章的封面图。

O que o gemini-3.1-flash-tts pode fazer por você na HiAPI

gemini-3.1-flash-tts é um modelo de texto para fala disponibilizado pela API unificada da HiAPI. Você envia o texto e a configuração de voz, e a plataforma retorna o áudio gerado. Como ele é exposto por meio de uma interface única e consistente, você não precisa de integrações separadas para cada modelo — os mesmos cabeçalhos de autenticação, a mesma URL base e o mesmo padrão de tarefa assíncrona se aplicam a todos os modelos de TTS da plataforma.

Escolha este modelo quando precisar de:

  • Narração com múltiplos locutores. Roteiros, diálogos ou conteúdo no estilo podcast, em que trechos diferentes devem soar como vozes diferentes.
  • Resposta rápida. A linha flash é voltada para geração de baixa latência, o que é especialmente importante para aplicações interativas ou tarefas em lote com SLA apertado.
  • Interface de integração previsível. Uma única forma de requisição cobre desde uma linha de texto até cenários com múltiplos locutores.

Se a aplicação que você está construindo já chama outros modelos da HiAPI, adicionar funcionalidade de voz exige apenas trocar o identificador do modelo e ajustar o corpo da requisição — sem novo SDK e sem novo fluxo de credenciais.

Pré-requisitos e configuração de autenticação

Antes da primeira chamada, garanta que você tem:

  1. Uma conta HiAPI e uma chave de API. Gere a chave no console. Trate-a como confidencial — não a envie para o controle de versão.
  2. A URL base da HiAPI. Todas as requisições vão para o mesmo host; apenas o caminho de cada endpoint varia.
  3. Uma ferramenta para fazer requisições HTTP. curl para testes rápidos e Python 3.9+ com a biblioteca requests para scripts.

A autenticação é feita com um token bearer no cabeçalho Authorization:

Authorization: Bearer $HIAPI_API_KEY

Armazene a chave em uma variável de ambiente, em vez de codificá-la diretamente:

export HIAPI_API_KEY="your_key_here"

Todos os endpoints descritos abaixo — envio de tarefa, verificação de status e obtenção de arquivo — usam o mesmo cabeçalho. Se você receber 401, o cabeçalho está ausente, malformado ou a chave foi revogada. Faça a rotação da chave no console em vez de compartilhar uma mesma chave entre ambientes.

Montando o corpo da requisição para voz com múltiplos locutores

O corpo da requisição é JSON. No mínimo, ele precisa conter o identificador do modelo e o conteúdo de entrada. Para saída com múltiplos locutores, você adiciona uma configuração de locutores que mapeia locutores nomeados para vozes e marca o texto de entrada com etiquetas de locutor.

Um corpo de requisição típico é assim:

{
  "model": "gemini-3.1-flash-tts",
  "input": {
    "text": "[Host] Welcome back to the show. [Guest] Thanks for having me.",
    "multi_speaker": true,
    "speakers": [
      { "name": "Host", "voice": "voice_a" },
      { "name": "Guest", "voice": "voice_b" }
    ]
  }
}

Pontos-chave que costumam causar problemas:

  • Os nomes dos locutores precisam corresponder exatamente. O name no array speakers deve ser idêntico à etiqueta usada no texto (entre colchetes neste exemplo). Diferenças de maiúsculas e minúsculas são a causa mais comum de falha na renderização com múltiplos locutores.
  • Cada etiqueta precisa ser declarada. Se o texto referencia [Narrator], mas não há um locutor definido com name: "Narrator", a requisição será rejeitada.
  • Os identificadores de voz vêm do catálogo de vozes da plataforma. Use os identificadores fornecidos pela HiAPI, em vez de adivinhar. Para tarefas com locutor único, omita multi_speaker e speakers e, em vez disso, defina um único campo voice.
  • O texto é enviado como uma única string. Roteiros longos não são problema, mas fique atento aos limites por requisição documentados na plataforma.

Depois que o corpo da requisição passa na validação, a tarefa é aceita e retorna um identificador de tarefa, que você usará para consultar o status.

Executando com curl

Salve o corpo da requisição acima como payload.json e envie:

curl -X POST "$HIAPI_BASE_URL/v1/audio/speech/jobs" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @payload.json

A resposta contém um objeto de tarefa com os campos id e status (normalmente queued ou processing):

{ "id": "job_abc123", "status": "queued" }

Para verificar o progresso, obtenha a tarefa pelo ID:

curl "$HIAPI_BASE_URL/v1/audio/speech/jobs/job_abc123" \
  -H "Authorization: Bearer $HIAPI_API_KEY"

Quando status passar a succeeded, a resposta incluirá uma referência ao arquivo de áudio (URL ou ID de arquivo), que você pode baixar por meio de uma requisição subsequente — veja a seção de polling abaixo. Se a tarefa falhar, status será failed e haverá um campo error explicando o motivo.

Para experimentação rápida, você pode pular o arquivo e enviar o JSON inline:

curl -X POST "$HIAPI_BASE_URL/v1/audio/speech/jobs" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gemini-3.1-flash-tts","input":{"text":"Hello from HiAPI.","voice":"voice_a"}}'

Executando com Python

A versão em Python usa requests e uma pequena função auxiliar para manter o loop de polling legível. O corpo da requisição é idêntico ao exemplo com curl.

import os
import time
import requests

API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"]
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

payload = {
    "model": "gemini-3.1-flash-tts",
    "input": {
        "text": "[Host] Welcome back to the show. [Guest] Thanks for having me.",
        "multi_speaker": True,
        "speakers": [
            {"name": "Host", "voice": "voice_a"},
            {"name": "Guest", "voice": "voice_b"},
        ],
    },
}

resp = requests.post(
    f"{BASE_URL}/v1/audio/speech/jobs",
    headers={**HEADERS, "Content-Type": "application/json"},
    json=payload,
    timeout=30,
)
resp.raise_for_status()
job = resp.json()
job_id = job["id"]
print("submitted", job_id)

while True:
    r = requests.get(f"{BASE_URL}/v1/audio/speech/jobs/{job_id}", headers=HEADERS, timeout=30)
    r.raise_for_status()
    status = r.json()
    if status["status"] in ("succeeded", "failed"):
        break
    time.sleep(2)

if status["status"] == "failed":
    raise RuntimeError(status.get("error"))

print("audio ready:", status["output"])

Observações:

  • Timeouts são importantes. Defina timeouts explícitos em cada requisição; um loop de polling travado é pior do que um loop que falha.
  • Não consulte com frequência excessiva. Um intervalo de dois segundos é razoável para roteiros curtos; para roteiros longos, use backoff.
  • Coloque o corpo da requisição em uma função. No código real, parametrize texto, locutores e vozes para poder reutilizar a mesma lógica de envio em tarefas com locutor único.

Consultando tarefas assíncronas e baixando o áudio

A geração de voz é assíncrona: você envia a tarefa e depois verifica o status até que ela atinja um estado terminal. Existem dois estados terminais — succeeded e failed. Todos os outros estados devem ser tratados como "continue aguardando".

Um programa de polling robusto deve:

  1. Limitar o tempo total de espera. Mesmo modelos rápidos podem ficar na fila atrás de outro trabalho. Pare após um limite razoável e exponha o ID da tarefa para que o chamador possa tentar novamente.
  2. Backoff exponencial. Comece com um ou dois segundos e dobre a cada tentativa até um teto. Isso evita sobrecarregar a API em tarefas longas.
  3. Tratar falhas explicitamente. Quando status == "failed", leia o campo error e registre-o — ele normalmente aponta para um problema no corpo da requisição (locutor não declarado, texto vazio, voz não suportada).

Quando a tarefa é bem-sucedida, a resposta de status inclui uma referência de saída. Baixe-a com os mesmos cabeçalhos de autenticação:

out = status["output"]
audio = requests.get(out["url"], headers=HEADERS, timeout=60)
audio.raise_for_status()
with open("narration.wav", "wb") as f:
    f.write(audio.content)

Se a resposta retornar um ID de arquivo em vez de uma URL, obtenha-o pelo endpoint de arquivos correspondente na HiAPI. Armazene o ID da tarefa original junto com o arquivo salvo — é a única maneira confiável de rastrear uma renderização ruim até sua requisição posteriormente.

Entendendo a cobrança por caractere

O gemini-3.1-flash-tts na HiAPI é cobrado pelo número de caracteres do texto de entrada. Não há componente de cobrança por requisição ou por minuto a modelar — o custo da tarefa escala com o volume de texto enviado, o que simplifica a estimativa: o total de caracteres de todos os roteiros multiplicado pela taxa atual por caractere.

Implicações práticas:

  • Estime antes de lançar. Some o volume mensal de texto esperado e confira com a taxa atual exibida no painel de preços. Não codifique a taxa diretamente no código; leia-a da plataforma no momento da construção ou mantenha-a em configuração.
  • Conteúdo longo domina o custo. Dez minutos de narração são principalmente custo de texto. Se você for gerar audiolivros ou material de curso, modele o volume primeiro.
  • Novas tentativas geram custo. Tarefas que falham após a geração podem ainda ser cobradas, dependendo do modo de falha. Faça validação no cliente antes de enviar — verifique se cada etiqueta de locutor está declarada e se o texto não está vazio.
  • Faça cache ativamente. Se o mesmo roteiro for renderizado repetidamente (por exemplo, demonstrações ou fixtures de teste), salve o áudio gerado e reutilize-o, em vez de cobrar novamente pelos mesmos caracteres.

Para taxas atuais e eventuais faixas por volume, consulte o painel de preços no site — essa é a única fonte de verdade.

Pontos comuns de falha e como corrigi-los

A maioria dos problemas de integração se enquadra em algumas poucas categorias. Verifique-as em ordem.

401 Não autorizado

Seu cabeçalho Authorization está ausente ou malformado. Confirme que a chave está definida no ambiente e que o conteúdo do cabeçalho é exatamente Bearer <key>. Se a chave foi rotacionada, atualize seu ambiente.

400 Requisição inválida — locutor desconhecido

O texto contém uma etiqueta [Name] que não foi declarada em speakers, ou há diferença de maiúsculas e minúsculas. Faça a etiqueta e o campo name serem idênticos byte a byte.

400 Requisição inválida — entrada vazia ou grande demais

Texto vazio falha imediatamente na validação. Roteiros muito longos podem exceder o limite por requisição; divida-os em várias tarefas em vez de truncar.

400 Requisição inválida — identificador de voz inválido

Os nomes de voz devem vir do catálogo da plataforma. Copie o identificador exatamente; não invente nomes amigáveis.

Tarefa presa em processing

Isso geralmente é fila, não um bug. Aplique backoff e timeout total. Se persistir muito além do tempo normal, registre o ID da tarefa e tente novamente uma vez com um corpo de requisição menor, para determinar se o tamanho é o problema.

Tarefa failed com erro de geração

Leia o campo error. Ele geralmente aponta para um problema de conteúdo ou configuração, não para uma falha transitória. Corrija o corpo da requisição, reenvie e evite loops de nova tentativa cegos — cada tentativa pode ser cobrada por caractere.

O áudio pode ser baixado, mas não reproduz

Antes de assumir que o arquivo está corrompido, verifique o tipo de conteúdo do arquivo baixado. Salve com a extensão correta correspondente ao formato retornado e confirme que o tamanho em bytes é diferente de zero. Um arquivo truncado normalmente significa que a requisição de download foi interrompida — tente novamente o download, em vez de regenerar.

Com essas verificações, a maioria dos problemas de integração do primeiro dia pode ser resolvida em minutos, não em horas.

Artigos relacionados

Ver todos os artigos →

Comece a criar com artefactos de IA persistentes

Obtenha a sua chave de API e chame modelos de imagem, vídeo e áudio através de uma única API baseada em tasks.

Ler a documentação da API