API 快速开始

创建密钥、选择声音、生成第一段语音。

创建 API 密钥

登录后打开 API 密钥,点击“新建密钥”,复制以 sk- 开头的密钥。密钥只显示一次。

在终端中保存为环境变量,后面的示例都会用到:

export VAL_API_KEY="sk-your-api-key"

确认账户

curl https://voiceailabs.com/api/v1/account \
  -H "Authorization: Bearer $VAL_API_KEY"

返回中的 credits.balance 是可用积分,limits.max_text_length 是单次合成的最大字数。

选择声音

可以直接使用声音广场中的公共声音(不占声音名额),也可以使用你克隆的声音:

# 公共声音
curl "https://voiceailabs.com/api/v1/voices/public?limit=5" \
  -H "Authorization: Bearer $VAL_API_KEY"

# 我的声音
curl "https://voiceailabs.com/api/v1/voices" \
  -H "Authorization: Bearer $VAL_API_KEY"

记下某个声音的 id,作为下一步的 voice_id。

创建合成任务

curl https://voiceailabs.com/api/v1/tts \
  -H "Authorization: Bearer $VAL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1024-intro" \
  -d '{
    "voice_id": "VOICE_ID",
    "text": "欢迎使用 Voice AI Labs。",
    "language": "zh"
  }'

接口返回 202 和任务对象,status 为 pending。Idempotency-Key 可以保证网络重试时不会重复创建和扣费,详见错误码与限制。

获取音频

每隔几秒查询一次,直到 status 为 completed 或 failed:

curl https://voiceailabs.com/api/v1/syntheses/SYNTHESIS_ID \
  -H "Authorization: Bearer $VAL_API_KEY"

完成后从 audio_url 下载 MP3。生产环境推荐用 Webhook 代替轮询。

完整示例

const BASE_URL = "https://voiceailabs.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.VAL_API_KEY}` };

async function call(path, init = {}) {
  const response = await fetch(`${BASE_URL}${path}`, {
    ...init,
    headers: { ...headers, ...init.headers },
  });
  const body = await response.json();
  if (body.code !== 0) {
    const error = new Error(body.message);
    Object.assign(error, body.data); // error_code, retryable, request_id
    throw error;
  }
  return body.data;
}

async function textToSpeech(voiceId, text) {
  let task = await call("/tts", {
    method: "POST",
    headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
    body: JSON.stringify({ voice_id: voiceId, text }),
  });

  while (task.status === "pending" || task.status === "processing") {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    task = await call(`/syntheses/${task.id}`);
  }
  if (task.status === "failed") {
    throw new Error(`Synthesis failed, refund: ${task.refund?.status}`);
  }
  return task.audio_url;
}

textToSpeech("VOICE_ID", "Hello from Voice AI Labs!").then(console.log);
import os
import time
import uuid

import requests

BASE_URL = "https://voiceailabs.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['VAL_API_KEY']}"}


def call(method, path, **kwargs):
    headers = {**HEADERS, **kwargs.pop("headers", {})}
    body = requests.request(method, f"{BASE_URL}{path}", headers=headers, timeout=60, **kwargs).json()
    if body["code"] != 0:
        raise RuntimeError(f"{body['data']['error_code']}: {body['message']} ({body['data']['request_id']})")
    return body["data"]


def text_to_speech(voice_id, text):
    task = call(
        "POST",
        "/tts",
        json={"voice_id": voice_id, "text": text},
        headers={"Idempotency-Key": str(uuid.uuid4())},
    )
    while task["status"] in ("pending", "processing"):
        time.sleep(3)
        task = call("GET", f"/syntheses/{task['id']}")
    if task["status"] == "failed":
        raise RuntimeError(f"Synthesis failed, refund: {task['refund']}")
    return task["audio_url"]


print(text_to_speech("VOICE_ID", "Hello from Voice AI Labs!"))

下一步