HiAPI
Blog

Tutorial

Cómo usar qwen-audio-3.0-tts-plus a través de la API de hiapi: implementar texto a voz con curl y Python

Aug 4, 2026
Cómo usar qwen-audio-3.0-tts-plus a través de la API de hiapi: implementar texto a voz con curl y Python

qwen-audio-3.0-tts-plus es un modelo de texto a voz al que se accede mediante la cola unificada de tareas asíncronas de la plataforma hiapi: usa el mismo endpoint POST /v1/tasks que la generación de imágenes y vídeos, no una API de audio independiente. Envías el texto, recibes inmediatamente un taskId y luego consultas o esperas una devolución de llamada para obtener la URL del archivo de audio descargable. Este tutorial te guía a través de una solicitud que funciona en curl y Python (verificada de extremo a extremo contra la API en vivo) y luego presenta los patrones de producción que realmente necesitas: devoluciones de llamada frente a sondeo, reintentos idempotentes y las formas exactas de error que debes manejar.

Qué vas a construir

Un script que envía texto a qwen-audio-3.0-tts-plus, espera a que la tarea finalice y descarga el audio generado; además, incluye una referencia de parámetros y patrones de manejo de errores para convertir ese script en una integración real.

Requisitos previos: una clave de API de hiapi. Obténla desde el panel de hiapi; todas las solicitudes siguientes se autentican con Authorization: Bearer sk-<your-key>.

Ejemplo mínimo funcional

curl

Crear la tarea:

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer $HIAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen-audio-3.0-tts-plus",
    "input": {
      "text": "Welcome to hiapi, the unified API for AI models.",
      "voice": "longanlingxin"
    }
  }'
{"code": 200, "data": {"taskId": "tk-hiapi-01KZ59B7ZJ7012PXB5J9GE1QE7"}, "message": "success"}

Luego consulta GET /v1/tasks/<taskId> con el mismo token bearer hasta que data.status deje de ser "processing". Para este modelo, en las pruebas tardó aproximadamente 5 segundos:

curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01KZ59B7ZJ7012PXB5J9GE1QE7 \
  -H "Authorization: Bearer $HIAPI_KEY"
{
  "code": 200,
  "data": {
    "taskId": "tk-hiapi-01KZ59B7ZJ7012PXB5J9GE1QE7",
    "status": "success",
    "model": "qwen-audio-3.0-tts-plus",
    "storage": "temp",
    "created": 1785810231,
    "completed": 1785810236,
    "output": [{"type": "audio", "url": "https://temp.hiapi.ai/.../01KZ59B7ZJ7012PXB5J9GE1QE7-0.mp3", "artifactId": "66654", "expireAt": 1786415036}]
  },
  "message": "success"
}

output[0].url es un MP3 real y descargable (content-type: audio/mpeg, confirmado mediante una obtención directa). Es un enlace temporal: cuando storage mantiene el valor predeterminado "temp", expireAt es casi exactamente 7 días después de created, así que si necesitas conservar el audio a largo plazo, descarga los bytes y guárdalos por tu cuenta.

Python

import os
import time
import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
headers = {"Authorization": f"Bearer {os.environ['HIAPI_KEY']}"}

resp = requests.post(
    API_BASE,
    headers={**headers, "Content-Type": "application/json"},
    json={
        "model": "qwen-audio-3.0-tts-plus",
        "input": {
            "text": "Welcome to hiapi, the unified API for AI models.",
            "voice": "longanlingxin",
        },
    },
    timeout=30,
)
resp.raise_for_status()
task_id = resp.json()["data"]["taskId"]

while True:
    task = requests.get(f"{API_BASE}/{task_id}", headers=headers, timeout=30).json()["data"]
    if task["status"] == "success":
        audio_url = task["output"][0]["url"]
        break
    if task["status"] == "fail":
        raise RuntimeError(task["error"])
    time.sleep(2)

audio_bytes = requests.get(audio_url, timeout=60).content
with open("output.mp3", "wb") as f:
    f.write(audio_bytes)

Patrones de uso en producción

El esquema de entrada completo

El objeto input solo acepta los siguientes campos; la API rechaza cualquier otro campo con un 400, así que no reutilices nombres de parámetros de otros proveedores de TTS:

Campo ¿Obligatorio? Tipo / valores
text Sí Cadena
voice Sí Uno de "longanlingxin", "longanlufeng" (este modelo actualmente solo tiene dos voces disponibles)
format No Uno de "pcm", "wav", "mp3", "opus"; si se omite, el valor predeterminado es mp3
sample_rate No Uno de 8000, 16000, 22050, 24000, 44100, 48000
pitch No Número, 0.5–2
volume No Número, 0–100

Cada límite y valor de enumeración anterior proviene de los propios errores de validación de la API: si pasas un voice no válido o un pitch fuera de rango, hiapi te indicará exactamente los valores permitidos:

{"code": 400, "data": null, "error_code": "INVALID_REQUEST", "message": "invalid input: voice: value must be one of 'longanlingxin', 'longanlufeng'"}

Este modelo no tiene campos de control emotion, speed ni de estilo SSML; las solicitudes que incluyan estos campos fallarán con "additional properties '...' not allowed".

Devolución de llamada frente a sondeo

Para cualquier caso que vaya más allá de un script rápido, omite el sondeo y deja que hiapi envíe el resultado. Añade un objeto callback a la solicitud de creación de la tarea:

{
  "model": "qwen-audio-3.0-tts-plus",
  "callback": {"url": "https://example.com/hiapi/callback", "when": "final"},
  "input": {"text": "Welcome to hiapi.", "voice": "longanlingxin"}
}

"final" es actualmente el único valor admitido para when; tanto si la tarea tiene éxito como si falla, hiapi hará una única POST a tu callback.url, y desde ese payload leerás la misma forma de detalles de la tarea que obtienes al consultar GET /v1/tasks/<taskId>. Si omites callback por completo, consulta tú mismo mediante sondeo, como se muestra en los ejemplos anteriores.

También conviene saberlo: la salida de la tarea usa por defecto storage: "temp" (los archivos viven aproximadamente una semana, como muestra el valor expireAt anterior). Si necesitas que el audio siga siendo recuperable a largo plazo, establece "storage": "persistent" en la tarea; el almacenamiento persistente se cobra por tamaño, así que para generaciones puntuales suele ser más económico descargar y guardar por tu cuenta.

Idempotencia y reintentos

POST /v1/tasks acepta la cabecera Idempotency-Key (máximo 255 bytes). Reintentar la misma solicitud con la misma clave bajo la misma cuenta crea la tarea una sola vez; la repetición devuelve el taskId original en lugar de una segunda generación facturada. Úsala para reintentar tras un timeout o un 5xx, cuando realmente no sabes si la primera solicitud llegó:

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer $HIAPI_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: tts-job-42" \
  -d '{"model": "qwen-audio-3.0-tts-plus", "input": {"text": "...", "voice": "longanlingxin"}}'

Manejo de errores

Una clave no válida o no autorizada devuelve HTTP 401 con el sobre de error estándar de hiapi (verificado directamente contra este modelo):

{"error": {"code": "permission_denied", "message": "This API key cannot use the selected model...", "request_id": "...", "type": "hiapi_error"}}

Un payload de input incorrecto devuelve HTTP 400 con error_code: "INVALID_REQUEST" y un message que indica el campo infractor, como se muestra en la sección de esquema anterior; ramifica según error_code / error.code, no según el texto del mensaje legible por humanos. Si la tarea alcanza el estado terminal "fail" en lugar de fallar en la creación, la respuesta de detalles de la tarea incluye la misma forma bajo data.error (code + message), no bajo la clave error de nivel superior.

Lecturas relacionadas

Preguntas frecuentes

¿La generación de audio es diferente de la API de tareas de imagen/vídeo de hiapi? No: la misma cola POST /v1/tasks, el mismo flujo de creación y posterior sondeo o devolución de llamada, y la misma forma de output[0].url. Solo output[0].type (aquí "audio") y el esquema de input varían según el modelo.

¿Qué ocurre si no establezco format? Obtendrás un MP3; en las pruebas se confirmó omitiéndolo. Si necesitas wav, pcm u opus, establece format explícitamente.

¿Puedo transmitir el audio en streaming en lugar de esperar a que finalice la tarea? No. Este modelo solo expone el flujo estándar de tarea de creación/sondeo/devolución de llamada; su esquema de entrada no tiene parámetros de streaming, y la generación es lo bastante rápida (unos segundos para textos cortos) como para que consultar cada 1-2 segundos suela ser suficiente.

¿Cuánto tiempo permanece válida la URL de salida? Aproximadamente 7 días por defecto (storage: "temp"), según el intervalo entre las marcas de tiempo created y expireAt de la tarea. Descarga el audio a tiempo, o si necesitas que siga siendo recuperable durante más tiempo, establece "storage": "persistent" en la tarea.

¿Por qué falla mi solicitud con "additional properties ... not allowed"? El esquema es estricto: este modelo solo acepta text, voice, format, sample_rate, pitch y volume. Los parámetros de otras API de TTS (como emotion o speed) no se reconocen aquí y devolverán un 400.

¿Dónde encuentro el precio actual? En la página de precios; este tutorial omite deliberadamente codificar el coste por carácter o por solicitud, ya que los precios pueden cambiar.

Artículos relacionados

Ver todos los artículos →

Empieza a crear con artefactos de IA persistentes

Consigue tu API key y llama a modelos de imagen, vídeo y audio a través de una única API basada en tasks.

Leer la documentación de la API