语音识别POST
上传音频文件或提供音频 URL,返回识别文本。短音频同步返回,录音文件转写型号异步执行。
POST https://api.tokentosea.com/v1/audio/transcriptions
把音频转成文字。推荐用 multipart/form-data 直接上传文件:平台会校验时长,按需转存到私有存储后交给厂商。部分型号也接受 JSON 中的音频 URL。
前提:全能密钥或模型密钥,账户可用余额足以覆盖按音频时长计算的预留。Python 示例需要 pip install requests,Node.js 示例需要 Node.js 18+(内置 fetch、FormData、Blob)。
支持的型号#
截至 2026-10-21,13 个型号开放本接口,以 GET /v1/models 为准。
| 服务商 | 型号 | 返回方式 |
|---|---|---|
| 千问 | qwen3-asr-flash、qwen-audio-3.0-asr-flash、qwen-audio-3.1-asr-flash、fun-asr-flash-2026-06-15 | 同步 |
| 千问(录音文件转写) | qwen3-asr-flash-filetrans、qwen-audio-3.0-asr-flash-filetrans、qwen-audio-3.1-asr-flash-filetrans、fun-asr、paraformer-v2、paraformer-8k-v2 | 异步(202) |
| 阶跃星辰 | step-asr-1.1 | 异步(202) |
| 智谱 | glm-asr-2512 | 同步 |
| MiniMax | asr-1.0 | 同步 |
只支持非流式(JSON)响应。实时识别请使用 实时音频。
请求#
multipart 上传(推荐)#
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | text | 必填 | 型号 ID |
file | file | 必填 | 音频文件,最大 25 MB,时长不超过 1 小时。千问型号接受 audio/wav、audio/x-wav、audio/mpeg、audio/mp4、audio/ogg、audio/flac(由文件的 Content-Type 判断);其他厂商以其文档为准 |
format | text | 可选 | 阶跃星辰:音频格式,默认 wav。千问由文件的 Content-Type 自动判断 |
parameters | text(JSON) | 可选 | 千问:其他厂商原生参数(例如 Paraformer 的 language_hints),以厂商文档为准 |
provider_options | text(JSON) | 可选 | 厂商原生扩展 |
一次请求只能有一个 file,最多 40 个字段。千问与阶跃星辰的文件会先转存到私有存储,再以签名链接交给厂商;智谱与 MiniMax 的文件直接以 multipart 转发。
JSON 请求#
千问与阶跃星辰型号也可以提供音频 URL,不上传文件:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | string | 必填 | 型号 ID |
file_url | string | 必填 | 音频 URL,厂商需能公网访问。平台会下载它以测量时长(不超过 75 MiB) |
format | string | 部分必填 | 千问 qwen-audio-*-asr-flash、fun-asr-flash-* 必填,例如 wav、mp3;qwen3-asr-flash 与录音文件转写型号不需要 |
sample_rate | integer | 可选 | 千问:采样率 |
parameters | object | 可选 | 千问:其他原生参数 |
智谱 glm-asr-2512 与 MiniMax asr-1.0 请使用 multipart。
响应#
同步型号返回 200 与厂商原生的识别结果(字段以厂商文档为准)。异步型号返回 202 与结果信封,完成后在 output 中给出厂商结果;千问录音文件转写的结果文件会转存到私有存储,output.data[] 中提供下载链接与 asset_id。
计费按厂商返回的音频秒数或 token 结算;平台按实测时长预留费用。
示例#
上传文件(同步)#
curl https://api.tokentosea.com/v1/audio/transcriptions \
-H "Authorization: Bearer $TOKENTOSEA_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F model=qwen3-asr-flash \
-F "file=@your-audio.wav;type=audio/wav"import os
import uuid
import requests
with open("your-audio.wav", "rb") as audio:
response = requests.post(
"https://api.tokentosea.com/v1/audio/transcriptions",
headers={
"Authorization": "Bearer " + os.environ["TOKENTOSEA_API_KEY"],
"Idempotency-Key": str(uuid.uuid4()),
},
data={"model": "qwen3-asr-flash"},
files={"file": ("your-audio.wav", audio, "audio/wav")},
timeout=600,
)
print(response.status_code)
print(response.json())import { randomUUID } from 'node:crypto';
import { readFile } from 'node:fs/promises';
const form = new FormData();
form.set('model', 'qwen3-asr-flash');
form.set('file', new Blob([await readFile('your-audio.wav')], { type: 'audio/wav' }), 'your-audio.wav');
const response = await fetch('https://api.tokentosea.com/v1/audio/transcriptions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TOKENTOSEA_API_KEY}`,
'Idempotency-Key': randomUUID(),
}, // 不要手动设置 Content-Type,fetch 会自动带上 multipart 边界
body: form,
});
console.log(response.status, await response.json());录音文件转写(异步)#
REQUEST_KEY="$(uuidgen)"
curl https://api.tokentosea.com/v1/audio/transcriptions \
-H "Authorization: Bearer $TOKENTOSEA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $REQUEST_KEY" \
-d '{"model": "paraformer-v2", "file_url": "https://example.com/meeting.mp3"}'
curl "https://api.tokentosea.com/v1/results/$REQUEST_KEY" \
-H "Authorization: Bearer $TOKENTOSEA_API_KEY"import os
import time
import uuid
import requests
API = "https://api.tokentosea.com/v1"
HEADERS = {"Authorization": "Bearer " + os.environ["TOKENTOSEA_API_KEY"]}
request_key = str(uuid.uuid4())
submitted = requests.post(
API + "/audio/transcriptions",
headers={**HEADERS, "Idempotency-Key": request_key},
json={"model": "paraformer-v2", "file_url": "https://example.com/meeting.mp3"},
timeout=300,
)
if submitted.status_code not in (200, 202):
raise RuntimeError(f"{submitted.status_code}: {submitted.text}")
while True:
result = requests.get(API + "/results/" + request_key, headers=HEADERS, timeout=30).json()
if result["status"] in ("succeeded", "failed"):
break
time.sleep(5)
print(result)import { randomUUID } from 'node:crypto';
const API = 'https://api.tokentosea.com/v1';
const headers = { Authorization: `Bearer ${process.env.TOKENTOSEA_API_KEY}` };
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const requestKey = randomUUID();
const submitted = await fetch(`${API}/audio/transcriptions`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': requestKey },
body: JSON.stringify({ model: 'paraformer-v2', file_url: 'https://example.com/meeting.mp3' }),
});
if (![200, 202].includes(submitted.status)) throw new Error(`HTTP ${submitted.status}: ${await submitted.text()}`);
let result;
do {
result = await (await fetch(`${API}/results/${requestKey}`, { headers })).json();
if (!['succeeded', 'failed'].includes(result.status)) await sleep(5000);
} while (!['succeeded', 'failed'].includes(result.status));
console.log(JSON.stringify(result, null, 2));错误#
| 状态码 | code | 原因与处理 |
|---|---|---|
413 | multipart_limit_or_invalid_field | 文件超过 25 MB、字段过多,或文件字段名不是 file |
422 | audio_format_not_supported | 千问型号无法识别上传文件的 Content-Type,请设置正确的音频 MIME |
422 | audio_file_url_required | 录音文件转写型号缺少 file_url(或上传的文件) |
422 | audio_format_required | 千问 qwen-audio-*-asr-flash 等以 JSON 调用时缺少 format |
422 | media_duration_invalid | 音频时长无法测量或超过 1 小时 |
422 | input_media_unavailable | file_url 无法下载 |
422 | response_mode_not_verified | 本接口不支持流式 |