---
title: Transcript API  文档
description: 提交媒体 URL、跟踪异步转录任务，并获取结构化转录、章节、翻译以及 SRT、VTT 字幕文件。
version: V1
base_url: https://videotranscriber.ai/openapi/v1
---

<!-- Generated from content/api-docs/cn.md. Edit the source document instead. -->

<a id="video-transcriber-api"></a>

# Transcript API 

提交公开可访问的媒体 URL，跟踪异步任务状态，并通过统一 API 获取结构化转录、章节、翻译以及 SRT、VTT 字幕文件。

`REST + JSON` · `UTF-8` · `UTC / ISO 8601` · `支持 200+ 语言`

**基础 URL**

`https://videotranscriber.ai/openapi/v1`


**通过 AI Agent 使用**

先按 [VideoTranscriber MCP 接入说明](#mcp-overview)连接 AI 客户端，再安装 [transcribe-media Agent Skill](#agent-skill-overview)，即可通过对话完成转录任务。


<a id="quickstart"></a>

## 快速开始

一个转录请求分为三个步骤。接口会立即返回，任务在后台异步处理。

1. 通过 `POST /transcriptions` 创建任务。
2. 轮询返回的 `poll_url`，并遵循 `retry_after` 指定的间隔。
3. 获取结构化结果和短期有效的字幕下载 URL。

**异步处理**

创建转录任务时，接口不会等待媒体处理完成。请保存返回的请求 ID，并持续轮询，直到任务进入终态。


<a id="authentication"></a>

## 身份验证

先登录网站，在 API 控制台创建 API Key，再使用该 Key 调用 OpenAPI。所有 OpenAPI 请求都通过 Bearer API Key 鉴权，不接受网站 Cookie 或登录 Token。

```http [HTTP 请求头]
Authorization: Bearer $VT_API_KEY
```

| 请求头 | 必填 | 说明 |
| --- | --- | --- |
| `Authorization` | 是 | Bearer API Key。 |
| `Content-Type` | POST 请求 | 必须为 `application/json`。 |
| `Idempotency-Key` | POST 请求 | 一次逻辑创建操作的稳定标识。 |
| `X-Request-ID` | 否 | 客户端链路标识，服务端会原样返回或自动生成。 |

**请妥善保管 API Key**

不要在浏览器端代码、公开仓库、日志、截图或支持消息中暴露 API Key。


**赠送额度**

首次成功创建 API Key 时赠送 1,500 API Quota，每个用户限领取一次，同一 IP 也限领取一次。IP 限制只影响赠送额度，不影响创建 Key。API Quota 永久有效，使用时优先消耗赠送额度。网站积分与 API Quota 独立。


<a id="idempotency"></a>

## 幂等请求

每次逻辑创建操作都应使用稳定的 `Idempotency-Key`，避免网络重试产生重复计费任务。

Key 在 API 账户内唯一，长度为 1–128 个可见 ASCII 字符，不能包含空格。修改模型、选项或附加能力时，需要使用新的 Key。省略模型与显式传入 `basic` 保持兼容。

| 请求 | 结果 |
| --- | --- |
| 相同 Key 和相同请求体 | 返回原任务，HTTP 200。 |
| 相同 Key 和不同请求体 | 返回 HTTP 409 `idempotency_conflict`。 |

<a id="sources-and-formats"></a>

## 来源与格式

请提供公开可访问的 HTTP 或 HTTPS URL。OpenAPI 不支持文件上传。 不支持 Vimeo 平台链接。

| 来源 | 支持示例 |
| --- | --- |
| 平台链接 | YouTube、TikTok、Instagram、Facebook、X、Bilibili |
| 云盘链接 | Google Drive、Dropbox |
| 文件直链 | MP3、MP4、M4A、WAV、WebM、MOV、AVI、OGG、AAC |

**URL 必须可以访问**

媒体 URL 不能依赖交互式登录、即将过期的浏览器会话或私有网络连接。


<a id="features-and-billing"></a>

## 能力与计费

用量按照媒体时长向上取整，最少按 1 分钟计费：

`billable_minutes = max(1, ceil(duration_seconds / 60))`

| 功能 | 说明 | 额度用量 |
| --- | --- | --- |
| 转录（基础模型） | 返回全文、检测语言、带时间轴的片段和可选说话人。 | 1 API Quota / 1 分钟 |
| 转录（高级模型） | 返回全文、检测语言、词级时间戳和可选说话人。 | 3 API Quota / 1 分钟 |
| YouTube、<br>TikTok、<br>Instagram、<br>Facebook、<br>X、<br>Bilibili | 直接通过受支持的平台 URL 转录媒体内容。 | 免费（限时） |
| 说话人识别 | 识别并标记转录中的不同说话人。 | 免费（限时） |
| 语言检测 | 自动检测媒体的源语言。 | 免费（限时） |
| 时间戳 | 返回带时间轴的片段，用于定位内容和同步字幕。 | 免费（限时） |
| 字幕（SRT/VTT） | 生成 SRT、VTT 字幕文件的签名下载 URL。 | 免费（限时） |
| 动态音频标签（高级模型） | 自动检测非语音提示、声音事件和内联音频标记。 | 免费（限时） |
| 智能清理语气词和重复内容（高级模型） | 自动移除语气词、冗余重复和不流畅的口语表达。 | 免费（限时） |
| 章节 | 生成带标题和摘要的有序章节区间。 | 1 API Quota / 1 分钟 |
| 翻译 | 生成翻译全文和对齐片段。 | 1 API Quota / 1 分钟 |
| 关键词提示（高级模型） | 使用自定义关键词引导转录，提高识别准确率。 | 1 API Quota / 1 分钟 |

例如，10 分钟媒体使用高级模型、非空 Keyterms、章节和翻译，全部成功后消耗 `10 × (3 + 1 + 1 + 1) = 60 API Quota`。Dynamic Audio Tags 和 Smart Cleanup 不改变费用。

任务开始处理后会按实际时长预占额度，成功的能力结算扣费，最终失败或取消的能力释放预占额度。部分成功只对成功的能力计费。提交时的余额检查只检查最低所需额度；解析实际时长后，余额不足仍可能使任务失败。

<a id="task-lifecycle"></a>

## 任务生命周期

- `queued`
- `processing`
- `succeeded`
- `partial_succeeded`
- `failed`
- `cancelled`

**轮询**

请使用响应中的 `retry_after` 或响应头中的 `Retry-After`。不要高频连续轮询。


<a id="create-transcription"></a>

## 创建转录任务

`POST /transcriptions`

通过公开媒体 URL 创建异步转录任务。

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `source_url` | string | 是 | 公开 HTTP/HTTPS 媒体直链或支持的平台链接，最长 4,096 字符。 |
| `language` | string | 否 | 默认为 `auto`，也可指定源语言代码，例如 `en`、`zh`。 |
| `speaker_diarization` | boolean | 否 | 默认为 `false`，启用说话人识别。 |
| `transcription_model` | string | 否 | `basic` 或 `advanced`，默认为 `basic`。 |
| `include_audio_events` | boolean | 否 | 默认为 `false`，启用 Dynamic Audio Tags。仅高级模型支持。 |
| `cleanup_speech` | boolean | 否 | 默认为 `false`，启用 Smart Cleanup。仅高级模型支持。 |
| `keyterms` | string[] | 否 | 默认为 `[]`，启用关键词识别。仅高级模型支持，非空时额外计费。 |
| `features.chapters` | boolean | 否 | 默认为 `false`，生成章节。 |
| `features.translation` | object | 否 | 传入时启用翻译。 |
| `features.translation.target_language` | string | 翻译时必填 | 目标语言代码，例如 `es`、`zh-CN`，最长 32 字符。 |

基础模型不能启用 `include_audio_events`、`cleanup_speech` 或传入非空 `keyterms`，否则返回 HTTP 400 `invalid_request`。请求体和嵌套 `features`、`translation` 对象不接受未知字段。

Keyterms 最多传入 1,000 项，每项为非空字符串，最多 50 字符、5 个以空白分隔的词。不能包含 `<`、`>`、`{`、`}`、`[`、`]`、`\`、`,`、`，`。服务端会去除首尾空白、合并连续空白，并按大小写不敏感规则去重。

以下各语言示例均使用高级模型，启用声音事件、语音清理、Keyterms、章节和西班牙语翻译。只需要基础转录时，可仅提交 `source_url`，其余字段使用默认值。

**cURL**

```bash [cURL]
curl --request POST "https://videotranscriber.ai/openapi/v1/transcriptions" \
  --header "Authorization: Bearer $VT_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: transcribe-order-0001" \
  --data '{
    "source_url": "https://example.com/media/demo.mp4",
    "language": "auto",
    "speaker_diarization": true,
    "transcription_model": "advanced",
    "include_audio_events": true,
    "cleanup_speech": true,
    "keyterms": ["VideoTranscriber", "API"],
    "features": {
      "chapters": true,
      "translation": {"target_language": "es"}
    }
  }'
```

**Python**

```python [Python]
import os
import requests

response = requests.post(
    "https://videotranscriber.ai/openapi/v1/transcriptions",
    headers={
        "Authorization": f"Bearer {os.environ['VT_API_KEY']}",
        "Idempotency-Key": "transcribe-order-0001",
    },
    json={
        "source_url": "https://example.com/media/demo.mp4",
        "language": "auto",
        "speaker_diarization": True,
        "transcription_model": "advanced",
        "include_audio_events": True,
        "cleanup_speech": True,
        "keyterms": ["VideoTranscriber", "API"],
        "features": {"chapters": True, "translation": {"target_language": "es"}},
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

**Go**

```go [Go]
package main

import (
  "bytes"
  "fmt"
  "io"
  "net/http"
  "os"
)

func main() {
  body := []byte(`
    {
      "source_url": "https://example.com/media/demo.mp4",
      "language": "auto",
      "speaker_diarization": true,
      "transcription_model": "advanced",
      "include_audio_events": true,
      "cleanup_speech": true,
      "keyterms": [
        "VideoTranscriber",
        "API"
      ],
      "features": {
        "chapters": true,
        "translation": {
          "target_language": "es"
        }
      }
    }
  `)
  req, _ := http.NewRequest("POST", "https://videotranscriber.ai/openapi/v1/transcriptions", bytes.NewReader(body))
  req.Header.Set("Authorization", "Bearer "+os.Getenv("VT_API_KEY"))
  req.Header.Set("Content-Type", "application/json")
  req.Header.Set("Idempotency-Key", "transcribe-order-0001")
  res, err := http.DefaultClient.Do(req)
  if err != nil { panic(err) }
  defer res.Body.Close()
  data, _ := io.ReadAll(res.Body)
  fmt.Println(string(data))
}
```

**Ruby**

```ruby [Ruby]
require "json"
require "net/http"
require "uri"

uri = URI("https://videotranscriber.ai/openapi/v1/transcriptions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('VT_API_KEY')}"
request["Content-Type"] = "application/json"
request["Idempotency-Key"] = "transcribe-order-0001"
request.body = {
  source_url: "https://example.com/media/demo.mp4",
  language: "auto",
  speaker_diarization: true,
  transcription_model: "advanced",
  include_audio_events: true,
  cleanup_speech: true,
  keyterms: ["VideoTranscriber", "API"],
  features: {chapters: true, translation: {target_language: "es"}}
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.body
```

**PHP**

```php [PHP]
<?php
$payload = [
  "source_url" => "https://example.com/media/demo.mp4",
  "language" => "auto",
  "speaker_diarization" => true,
  "transcription_model" => "advanced",
  "include_audio_events" => true,
  "cleanup_speech" => true,
  "keyterms" => ["VideoTranscriber", "API"],
  "features" => [
    "chapters" => true,
    "translation" => ["target_language" => "es"]
  ]
];

$ch = curl_init("https://videotranscriber.ai/openapi/v1/transcriptions");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer " . getenv("VT_API_KEY"),
    "Content-Type: application/json",
    "Idempotency-Key: transcribe-order-0001"
  ],
  CURLOPT_POSTFIELDS => json_encode($payload)
]);
echo curl_exec($ch);
curl_close($ch);
```

**Java**

```java [Java]
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class CreateTranscription {
  public static void main(String[] args) throws Exception {
    String body = """
      {
        "source_url": "https://example.com/media/demo.mp4",
        "language": "auto",
        "speaker_diarization": true,
        "transcription_model": "advanced",
        "include_audio_events": true,
        "cleanup_speech": true,
        "keyterms": [
          "VideoTranscriber",
          "API"
        ],
        "features": {
          "chapters": true,
          "translation": {
            "target_language": "es"
          }
        }
      }
      """;
    HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://videotranscriber.ai/openapi/v1/transcriptions"))
      .header("Authorization", "Bearer " + System.getenv("VT_API_KEY"))
      .header("Content-Type", "application/json")
      .header("Idempotency-Key", "transcribe-order-0001")
      .POST(HttpRequest.BodyPublishers.ofString(body))
      .build();
    var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println(response.body());
  }
}
```


```json [响应]
{
  "id": "req_example_001",
  "object": "transcription",
  "status": "queued",
  "created_at": "2026-10-10T00:00:00Z",
  "transcription_model": "advanced",
  "poll_url": "/openapi/v1/transcriptions/req_example_001",
  "retry_after": 3
}
```

新任务返回 HTTP 202；相同幂等 Key 和相同请求体返回原任务，HTTP 200。保存响应中的 `id` 用于后续查询。`poll_url` 和 `result_url` 已包含 `/openapi/v1`，应相对于 `https://videotranscriber.ai` 解析，不能再次拼接基础路径。

<a id="get-transcription"></a>

## 查询转录任务

`GET /transcriptions/{request_id}`

查询任务状态、各能力状态、用量、错误和结果可用性。

**cURL**

```bash [cURL]
curl --request GET \
  "https://videotranscriber.ai/openapi/v1/transcriptions/$REQUEST_ID" \
  --header "Authorization: Bearer $VT_API_KEY"
```

**Python**

```python [Python]
import os
import requests

request_id = os.environ["REQUEST_ID"]
response = requests.get(
    f"https://videotranscriber.ai/openapi/v1/transcriptions/{request_id}",
    headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

**Go**

```go [Go]
req, _ := http.NewRequest("GET", "https://videotranscriber.ai/openapi/v1/transcriptions/"+os.Getenv("REQUEST_ID"), nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("VT_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer res.Body.Close()
```

**Ruby**

```ruby [Ruby]
uri = URI("https://videotranscriber.ai/openapi/v1/transcriptions/#{ENV.fetch('REQUEST_ID')}")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('VT_API_KEY')}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.body
```

**PHP**

```php [PHP]
<?php
$url = "https://videotranscriber.ai/openapi/v1/transcriptions/" . getenv("REQUEST_ID");
$ch = curl_init($url);
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("VT_API_KEY")]
]);
echo curl_exec($ch);
curl_close($ch);
```

**Java**

```java [Java]
String requestId = System.getenv("REQUEST_ID");
HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://videotranscriber.ai/openapi/v1/transcriptions/" + requestId))
  .header("Authorization", "Bearer " + System.getenv("VT_API_KEY"))
  .GET()
  .build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```


`transcription_model` 返回本次任务使用的模型。`usage.reserved_quotas` 是当前尚未结算的预占额度，结算后通常为 `0`；`usage.used_quotas` 是已实际扣除的额度。各能力也分别返回这两个字段。

非终态响应包含 `retry_after`。`result_available=true` 时返回 `result_url`，此时可获取结果。`partial_succeeded` 表示部分能力成功；终态任务不再继续轮询，失败原因查看任务及各能力的 `error`。

下面是 45 秒媒体使用上述高级模型及附加能力、全部成功的示例，总消耗 6 Quota。

```json [响应]
{
  "id": "req_example_001",
  "object": "transcription",
  "status": "succeeded",
  "created_at": "2026-10-10T00:00:00Z",
  "transcription_model": "advanced",
  "started_at": "2026-10-10T00:00:03Z",
  "finished_at": "2026-10-10T00:00:10Z",
  "duration_seconds": 45.0,
  "billable_minutes": 1,
  "usage": {
    "reserved_quotas": 0,
    "used_quotas": 6
  },
  "features": [
    {
      "type": "transcript",
      "status": "succeeded",
      "target_language": null,
      "reserved_quotas": 0,
      "used_quotas": 4,
      "error": null
    },
    {
      "type": "chapters",
      "status": "succeeded",
      "target_language": null,
      "reserved_quotas": 0,
      "used_quotas": 1,
      "error": null
    },
    {
      "type": "translation",
      "status": "succeeded",
      "target_language": "es",
      "reserved_quotas": 0,
      "used_quotas": 1,
      "error": null
    }
  ],
  "result_available": true,
  "result_expires_at": "2026-11-09T00:00:10Z",
  "error": null,
  "result_url": "/openapi/v1/transcriptions/req_example_001/result"
}
```

<a id="get-result"></a>

## 获取任务结果

`GET /transcriptions/{request_id}/result`

获取成功能力的结构化结果，以及 SRT、VTT 文件的签名下载 URL。

**cURL**

```bash [cURL]
curl --request GET \
  "https://videotranscriber.ai/openapi/v1/transcriptions/$REQUEST_ID/result" \
  --header "Authorization: Bearer $VT_API_KEY"
```

**Python**

```python [Python]
import os
import requests

request_id = os.environ["REQUEST_ID"]
response = requests.get(
    f"https://videotranscriber.ai/openapi/v1/transcriptions/{request_id}/result",
    headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

**Go**

```go [Go]
req, _ := http.NewRequest("GET", "https://videotranscriber.ai/openapi/v1/transcriptions/"+os.Getenv("REQUEST_ID")+"/result", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("VT_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer res.Body.Close()
```

**Ruby**

```ruby [Ruby]
uri = URI("https://videotranscriber.ai/openapi/v1/transcriptions/#{ENV.fetch('REQUEST_ID')}/result")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('VT_API_KEY')}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.body
```

**PHP**

```php [PHP]
<?php
$url = "https://videotranscriber.ai/openapi/v1/transcriptions/" . getenv("REQUEST_ID") . "/result";
$ch = curl_init($url);
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("VT_API_KEY")]
]);
echo curl_exec($ch);
curl_close($ch);
```

**Java**

```java [Java]
String requestId = System.getenv("REQUEST_ID");
HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://videotranscriber.ai/openapi/v1/transcriptions/" + requestId + "/result"))
  .header("Authorization", "Bearer " + System.getenv("VT_API_KEY"))
  .GET()
  .build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```


**部分成功**

只返回成功能力的结果；未请求或失败的能力字段为 `null`。各能力失败原因通过任务状态接口查询。


结果未就绪时返回 HTTP 409 `result_not_ready`，结果过期时返回 HTTP 410 `result_expired`。下载 URL 过期后，可以在结果保留期内重新调用本接口获取新的 URL。下方下载链接为示例。

```json [响应]
{
  "id": "req_example_001",
  "status": "succeeded",
  "result_expires_at": "2026-11-09T00:00:10Z",
  "transcript": {
    "language": "en",
    "duration_seconds": 45.0,
    "text": "Welcome to VideoTranscriber.",
    "segments": [
      {
        "id": 0,
        "start": 0.0,
        "end": 3.2,
        "text": "Welcome to VideoTranscriber.",
        "speaker": "speaker_0"
      }
    ],
    "audio_events": [
      {
        "start": 3.2,
        "end": 3.8,
        "text": "(laughter)"
      }
    ]
  },
  "chapters": [
    {
      "id": 0,
      "start": 0.0,
      "end": 45.0,
      "title": "Introduction",
      "summary": "An introduction to VideoTranscriber."
    }
  ],
  "translation": {
    "source_language": "en",
    "target_language": "es",
    "text": "Bienvenido a VideoTranscriber.",
    "segments": [
      {
        "source_segment_id": 0,
        "start": 0.0,
        "end": 3.2,
        "text": "Bienvenido a VideoTranscriber.",
        "speaker": "speaker_0"
      }
    ]
  },
  "files": {
    "subtitles": {
      "srt": {
        "url": "https://example.com/signed/subtitles.srt",
        "url_expires_at": "2026-10-10T01:00:10Z"
      },
      "vtt": {
        "url": "https://example.com/signed/subtitles.vtt",
        "url_expires_at": "2026-10-10T01:00:10Z"
      }
    },
    "translated_subtitles": {
      "srt": {
        "url": "https://example.com/signed/translation_es.srt",
        "url_expires_at": "2026-10-10T01:00:10Z"
      },
      "vtt": {
        "url": "https://example.com/signed/translation_es.vtt",
        "url_expires_at": "2026-10-10T01:00:10Z"
      },
      "target_language": "es"
    }
  }
}
```

<a id="response-schemas"></a>

## 响应数据结构

JSON 使用 UTF-8 编码。时间轴字段统一使用秒并允许小数。客户端必须忽略未知响应字段。

| 数据结构 | 内容 |
| --- | --- |
| Transcript | 检测语言、全文、时长、时间轴片段、可选说话人和 `audio_events`。 |
| Chapters | 按顺序返回包含开始、结束、标题和摘要的章节区间。 |
| Translation | 源语言、目标语言、翻译全文和对齐片段。 |

未启用说话人识别，或供应商未返回可靠说话人标签时，`speaker` 字段为 `null`。

`transcript.audio_events` 为声音事件数组，每项包含秒级 `start`、`end` 和事件描述 `text`，例如笑声或掌声。声音事件不混入 `transcript.text` 或语音 `segments`。基础模型、未启用声音事件或未识别到事件时返回 `[]`；读取历史结果时，客户端应将缺失字段按 `[]` 处理。

`chapters` 为章节数组。翻译片段的 `source_segment_id` 对应原转录片段的 `id`。`files.subtitles` 和 `files.translated_subtitles` 中的 SRT/VTT 项包含 `url`、`url_expires_at`；翻译字幕还包含 `target_language`，无对应文件时不返回该文件组。

<a id="errors"></a>

## 错误处理

请求级失败使用 HTTP 状态码和稳定的英文 `error.code`。不要根据 `error.message` 编写分支逻辑。

```json [错误响应]
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "request_id": "vt_example_trace_001",
    "retryable": false,
    "details": {
      "cleanup_speech": [
        "This feature requires the advanced model."
      ]
    }
  }
}
```

| HTTP 状态码 | error.code | 处理方式 |
| --- | --- | --- |
| 400 | `invalid_request` | 检查 `details` 中的字段错误，包括基础模型启用了高级选项。 |
| 400 | `invalid_idempotency_key` | 提供符合格式要求的 `Idempotency-Key`。 |
| 400 | `unsupported_source_url` | 使用受支持的公开 HTTP/HTTPS 来源。 |
| 401 | `invalid_api_key` / `api_key_revoked` | 检查或更换 API Key。 |
| 402 | `insufficient_quota` | API Quota 余额不足，需要补充额度。 |
| 403 | `api_account_disabled` | API 账户不可用。 |
| 404 | `request_not_found` | 任务不存在、不属于当前 API 账户或已清理。 |
| 409 | `idempotency_conflict` | 更改请求体时使用新的幂等 Key。 |
| 409 | `result_not_ready` | 继续查询任务状态，确认结果可用后再获取。 |
| 410 | `result_expired` | 结果超过保留期限，无法继续获取。 |
| 429 | `rate_limit_exceeded` / `concurrency_limit_exceeded` | 降低请求频率或等待进行中的任务完成。 |
| 500 | `internal_error` | 根据 `retryable` 决定是否重试，保留链路标识用于排查。 |

`error.request_id` 是链路标识，与转录任务的 `id` 不同；`details` 为字段错误详情，没有详情时为 `null`。

HTTP 错误与任务失败分别处理。状态查询成功时返回 HTTP 200，任务仍可能为 `failed` 或 `partial_succeeded`；检查响应中的任务 `error` 和 `features[].error`，其字段为 `code`、`message`、`retryable`。例如 `task_timeout`、`provider_unavailable`、`transcription_failed`、`no_speech_detected`。

<a id="limits-and-retries"></a>

## 限制与重试

请遵循 API 返回的速率限制和重试响应头：

- `X-RateLimit-Limit`
- `X-RateLimit-Remaining`
- `X-RateLimit-Reset`
- `Retry-After`

**可重试错误**

提交请求发生网络超时或无法确定是否被受理时，使用原 `Idempotency-Key` 和原请求体重试，避免重复创建。明确要重新执行已失败的任务时，才使用新的 Key；复用原 Key 只会返回原任务。对于 429，遵循 `Retry-After` 后再重试。


<a id="retention-and-security"></a>

## 保留与安全

| 资源 | 保留时间 |
| --- | --- |
| 结果 | 从结果生成之日起保留 30 天。 |
| 下载 URL | 私有签名地址，有效期最长一小时。 |
| 任务记录 | 按 UTC 保留创建月份和下一个自然月。 |

**支持请求**

请提供转录 ID 和 `X-Request-ID`。不要发送完整的 API Key、Authorization 请求头、包含凭据的来源 URL 或已签名的结果 URL。


<a id="mcp-overview"></a>

## VideoTranscriber MCP

将 AI 客户端连接到 VideoTranscriber，通过对话转录公开媒体链接，生成章节或翻译，并获取转录内容和字幕文件。

`Streamable HTTP` · `Bearer API Key` · `异步转录`

**MCP 地址**

`https://videotranscriber.ai/mcp`


<a id="mcp-guide-before-connecting"></a>

### 接入准备

1. 登录 VideoTranscriber，在 API 控制台创建 API Key。
2. 使用支持远程 Streamable HTTP MCP 和 Authorization 请求头的 AI 客户端。
3. 确认 API 账户有足够的 API Quota，覆盖所选模型和附加能力。

MCP 与 OpenAPI 使用同一套 API Key 和 API Quota 余额。不接受网站登录 Token 或 Cookie。VS 的 MCP 使用 Bearer API Key 鉴权，不提供 OAuth 登录流程。

<a id="mcp-guide-authentication"></a>

### MCP 身份验证

在客户端的 MCP 连接配置中设置凭据：

```http [Authorization 请求头]
Authorization: Bearer YOUR_VS_API_KEY
```

将 `YOUR_VS_API_KEY` 替换为控制台创建的 Key。凭据只配置在连接设置中，不作为工具参数传入，也不粘贴到对话里。

<a id="mcp-guide-connect-with-codex"></a>

### 在 Codex 中配置

在启动 Codex 的进程环境中设置 API Key：

```bash [环境变量]
export VT_API_KEY="YOUR_VS_API_KEY"
```

在 `~/.codex/config.toml` 中添加以下配置。这段内容使用 TOML 格式：

```text [config.toml]
[mcp_servers.videotranscriber]
url = "https://videotranscriber.ai/mcp"
bearer_token_env_var = "VT_API_KEY"
```

执行 `codex mcp list`，然后在 Codex CLI 会话中打开 `/mcp`，检查连接状态和可用工具。

桌面客户端的进程也需要能读取 `VT_API_KEY`。在终端导出环境变量，只会影响从该终端启动的进程。支持请求头配置界面的客户端，也可以直接设置 `Authorization: Bearer YOUR_VS_API_KEY`。

客户端配置选项参见 [OpenAI MCP 文档](https://developers.openai.com/codex/mcp)。

<a id="mcp-guide-connect-with-claude-code"></a>

### 在 Claude Code 中配置

先按上面的方式在终端设置 `VT_API_KEY`，再注册远程服务：

```bash [Claude Code]
claude mcp add --transport http --scope user videotranscriber \
  https://videotranscriber.ai/mcp \
  --header "Authorization: Bearer $VT_API_KEY"

claude mcp get videotranscriber
```

在 Claude Code 中打开 `/mcp`，检查连接状态和工具列表。

使用 `mcpServers` JSON 配置的客户端，可以使用以下 HTTP 连接项：

```json [HTTP 配置]
{
  "mcpServers": {
    "videotranscriber": {
      "type": "http",
      "url": "https://videotranscriber.ai/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_VS_API_KEY"
      }
    }
  }
}
```

配置文件位置因客户端而异，请使用该客户端支持的 HTTP 配置格式。参见 [Claude Code MCP 文档](https://code.claude.com/docs/en/mcp)。

<a id="mcp-guide-available-tools"></a>

### 可用工具

<a id="mcp-guide-create-transcription"></a>

#### MCP tool: create_transcription

创建异步任务，立即返回任务 ID。转录内容需要在任务处理完成后获取。

```text [参数]
source_url: 必填字符串；公开 HTTP/HTTPS 媒体链接，最长 4,096 字符。
idempotency_key: 必填字符串；1–128 个可见 ASCII 字符，不能包含空格。
language: 可选字符串；默认 "auto"。
transcription_model: "basic" 或 "advanced"；默认 "basic"。
speaker_diarization: 可选布尔值；默认 false。
include_audio_events: 可选布尔值；默认 false；仅高级模型支持。
cleanup_speech: 可选布尔值；默认 false；仅高级模型支持。
keyterms: 可选字符串数组；默认为空数组；仅高级模型支持。
chapters: 可选布尔值；默认 false。
target_language: 可选语言代码；传入时启用翻译。
```

MCP 使用顶层 `chapters` 和 `target_language` 参数，服务端将它们映射为 OpenAPI 的 `features.chapters` 和 `features.translation.target_language`。

```json [高级任务参数]
{
  "source_url": "https://example.com/media/demo.mp4",
  "idempotency_key": "vs-transcription-001",
  "language": "auto",
  "transcription_model": "advanced",
  "speaker_diarization": true,
  "include_audio_events": true,
  "cleanup_speech": true,
  "keyterms": ["VideoTranscriber", "API"],
  "chapters": true,
  "target_language": "es"
}
```

以上 JSON 是工具参数，不是直接向 MCP 地址提交的 HTTP 请求体。MCP 协议由客户端处理。

每个需要创建的任务生成一个 `idempotency_key`，重试时保留原值。修改模型、选项或附加能力时使用新的 Key。Keyterms 最多 1,000 项，每项为非空字符串，最多 50 字符、5 个词，其他校验规则与 OpenAPI 一致。

<a id="mcp-guide-get-transcription"></a>

#### MCP tool: get_transcription

查询任务状态、各能力状态、额度用量、错误和结果可用性。

```json [参数]
{
  "request_id": "req_0123456789abcdef0123456789abcdef"
}
```

将 `create_transcription` 返回的 `id` 作为 `request_id`。任务 ID 的格式为 `req_` 加 32 位小写十六进制字符。

<a id="mcp-guide-get-transcription-result"></a>

#### MCP tool: get_transcription_result

获取可用的转录、章节、翻译结果和字幕下载链接。

```json [参数]
{
  "request_id": "req_0123456789abcdef0123456789abcdef"
}
```

在 `get_transcription` 返回 `result_available=true` 后调用。数据字段沿用 [OpenAPI 结果结构](#get-result)，声音事件位于 `transcript.audio_events`。

<a id="mcp-guide-task-workflow"></a>

### 任务调用流程

1. 调用一次 `create_transcription`，保存返回的 `id`。
2. 按 `retry_after` 指定的间隔轮询 `get_transcription`，避免连续高频查询。
3. 任务进入 `succeeded`、`partial_succeeded`、`failed` 或 `cancelled` 时停止轮询。
4. 如果 `result_available=true`，调用 `get_transcription_result`。
5. 部分成功时获取成功的结果，通过 `features[].error` 查看失败原因。

工具结果通过 `structuredContent` 和 JSON 文本内容返回 OpenAPI 数据，同时携带 `_meta.http_status`，有重试间隔时携带 `_meta.retry_after`。工具调用失败时 `isError=true`。部分客户端界面只显示 JSON 内容。

提交超时不能证明任务未创建，应使用相同的 `idempotency_key` 和参数重试。明确重新执行失败任务时使用新的 Key，这会创建独立的计费任务。OpenAPI 不提供任务取消操作。

<a id="mcp-guide-usage-examples"></a>

### 使用示例

```text [基础转录]
使用基础模型转录 https://example.com/media/demo.mp4。
只创建一次任务，等待完成后返回全文和 SRT 下载链接。
```

```text [高级转录]
使用高级模型转录 https://example.com/media/demo.mp4。
启用说话人识别、Dynamic Audio Tags 和 Smart Cleanup。
以 VideoTranscriber、API 为 Keyterms，生成章节并翻译为西班牙语。
等待任务完成，返回结果和实际 Quota 用量。
```

<a id="mcp-guide-billing-and-result-retention"></a>

### 计费与结果保留

成功能力沿用 OpenAPI 费率：基础转录每分钟 1 Quota，高级转录 3 Quota；非空 Keyterms 加 1 Quota，章节和翻译各加 1 Quota。Dynamic Audio Tags 和 Smart Cleanup 不额外收费。时长向上取整到整分钟，最少按 1 分钟计费。

失败能力释放预占额度，部分成功只对成功能力扣费。状态响应中的 `reserved_quotas` 是当前未结算的预占额度，`used_quotas` 是实际已扣额度。

结果保留 30 天，签名下载链接有效期最长一小时。下载链接过期后，可在结果保留期内重新调用结果工具获取新链接。详细规则参见 [能力与计费](#features-and-billing)。

<a id="mcp-guide-troubleshooting"></a>

### 常见问题

- `invalid_api_key` 或 `api_key_revoked`：检查 Key 配置，以及客户端是否能读取环境变量。
- `api_account_disabled`：API 账户不可用。
- `authentication_unavailable`：服务恢复后重新连接。
- `insufficient_quota`：补充 API Quota 后再提交任务。
- `rate_limit_exceeded` 或 `concurrency_limit_exceeded`：降低请求频率或等待已有任务完成。
- `idempotency_conflict`：同一 Key 被用于不同参数。
- `result_not_ready`：继续查询任务状态，等待 `result_available=true`。
- `result_expired`：结果已超过保留期限。
- 工具列表未出现：检查地址、HTTP 传输方式、客户端权限及鉴权配置。

请求失败和异步任务失败分别处理。状态查询成功时，任务仍可能为 `failed`。需要检查任务 `error` 和 `features[].error`。提交支持请求时，不要发送完整 API Key 或签名下载链接。

通过已安装的 Skill 使用相同流程，参见 [Agent Skill 接入说明](#agent-skill-overview)。

<a id="agent-skill-overview"></a>

## Transcribe Media Agent Skill

安装官方 `transcribe-media` Skill，让 AI 客户端按既定流程创建 Video Transcriber AI 任务、等待处理完成，并获取转录全文和字幕链接。

`Agent Skill` · `Video Transcriber AI MCP` · `公开媒体链接`

**Skill 与 MCP 需要分别配置**

Skill 提供工作流程指引，MCP 连接提供工具和 API 凭据。开始转录前，需要完成 Skill 安装和 MCP 服务配置。


<a id="agent-skill-guide-choose-the-right-skill"></a>

### 选择合适的 Skill

仓库包含两个职责独立的 Skill：

| Skill | 适用场景 |
| --- | --- |
| `transcribe-media` | 通过 MCP 转录公开媒体链接，或继续查询已有的 Video Transcriber AI 任务。 |
| `transcript-insights` | 对已有 transcript 或字幕做摘要、翻译、整理和洞察提取。 |

完整流程可以先使用 `transcribe-media` 取得 transcript，再交给 `transcript-insights` 分析。

<a id="agent-skill-guide-before-installing"></a>

### 安装准备

1. 使用支持 Agent Skills 和远程 Streamable HTTP MCP 的 AI 客户端。
2. 如需使用 Skills CLI，准备 Node.js/npm 和 Git。
3. 登录 Video Transcriber AI，在 API 控制台创建 API Key。
4. 提交任务前确认 API 账户有足够的 API Quota。

官方仓库为 [VideoTranscriberAI/videotranscriber-skills](https://github.com/VideoTranscriberAI/videotranscriber-skills)。安装时保留完整 Skill 目录，包括 `SKILL.md`、`references` 和 `agents` 目录。

<a id="agent-skill-guide-install-the-skill"></a>

### 安装 transcribe-media

查看仓库中可安装的 Skill：

```bash [查看 Skills]
npx skills add VideoTranscriberAI/videotranscriber-skills --list
```

为 Codex 安装 `transcribe-media`：

```bash [Codex]
npx skills add VideoTranscriberAI/videotranscriber-skills \
  --skill transcribe-media --agent codex --global
```

为 Claude Code 安装：

```bash [Claude Code]
npx skills add VideoTranscriberAI/videotranscriber-skills \
  --skill transcribe-media --agent claude-code --global
```

去掉 `--global` 可以安装到当前项目。这些命令只安装 Skill，不会创建 MCP 连接。

手动安装时，将仓库中的完整 `skills/transcribe-media` 目录复制到客户端支持的 Skill 目录。例如，Codex 可以使用 `~/.agents/skills/transcribe-media/`，Claude Code 可以使用 `~/.claude/skills/transcribe-media/`。安装后刷新 Skill 列表或重新启动客户端。

<a id="agent-skill-guide-configure-mcp"></a>

### 配置 Video Transcriber AI MCP

配置一个名为 `videotranscriber` 的 Streamable HTTP MCP 连接：

```text [连接信息]
Endpoint: https://videotranscriber.ai/mcp
Authorization: Bearer YOUR_VS_API_KEY
```

API Key 应保存在客户端连接设置或受保护的环境变量中，不要写进 Prompt、Skill 文件、代码仓库或工具参数。

Codex、Claude Code 和 JSON 配置示例参见 [MCP 接入说明](#mcp-overview)。连接后确认以下工具可用：

- `create_transcription`
- `get_transcription`
- `get_transcription_result`

如果工具不可用，先修复 MCP 连接，再让 Skill 创建任务。

<a id="agent-skill-guide-use-the-skill"></a>

### 使用 Skill

在 Codex 中使用 `$transcribe-media`：

```text [基础转录]
使用 $transcribe-media，通过基础模型转录
https://example.com/media/demo.mp4。
只创建一次任务，等待完成后返回转录全文和 SRT 字幕链接。
```

```text [高级转录]
使用 $transcribe-media，通过高级模型转录
https://example.com/media/demo.mp4。
启用说话人识别、Dynamic Audio Tags 和 Smart Cleanup，
生成章节并翻译为西班牙语，最后报告实际 API Quota 用量。
```

在 Claude Code 中，在请求开头使用 `/transcribe-media`。

继续查询已有任务时，不需要新建任务：

```text [已有任务]
使用 $transcribe-media 查询任务
req_0123456789abcdef0123456789abcdef。
如果结果可用，返回转录全文和字幕链接。
```

请将示例地址和任务 ID 替换为真实值。媒体来源必须是公开可访问的 HTTP/HTTPS 链接；该 Skill 不负责上传本地或私有文件。

<a id="agent-skill-guide-workflow-and-options"></a>

### 调用流程与选项

Skill 创建一次任务，保存 `request_id`，按 `retry_after` 轮询，在结果可用后获取内容。任务进入 `succeeded`、`partial_succeeded`、`failed` 或 `cancelled` 时停止轮询。

- 默认模型为 `basic`。
- `advanced` 支持 Dynamic Audio Tags、Smart Cleanup 和非空 Keyterms。
- 可以按需启用说话人识别、章节和翻译。
- 每个任务使用一个幂等 Key；网络重试使用相同 Key 和完全一致的参数。
- 请求超时或响应丢失时，不会自动更换 Key 创建第二个任务。
- 查询已有任务 ID 不会创建新的计费任务。
- 轮询有次数和时长上限。超过等待上限时，Skill 返回最新状态和任务 ID，后续可继续查询同一任务。

转录内容只作为待处理资料，不作为 AI 客户端的工具调用指令。

<a id="agent-skill-guide-billing-and-retention"></a>

### Skill 计费与结果保留

创建转录任务会消耗 API 账户的 API Quota。基础转录每分钟 1 Quota，高级转录每分钟 3 Quota；非空 Keyterms、章节、翻译各增加每分钟 1 Quota。Dynamic Audio Tags 和 Smart Cleanup 不额外收费。媒体时长向上取整到整分钟，最少按 1 分钟计算。

只对成功的能力收费。状态结果包含实际用量时，Skill 会返回该数值。完整规则参见 [能力与计费](#features-and-billing)。

结果保留 30 天，签名字幕下载链接最长有效一小时。链接过期但结果仍在保留期内时，让 Skill 重新获取同一任务的结果即可，不需要重新转录。

<a id="agent-skill-guide-update-the-skill"></a>

### 更新 Skill

```bash [更新]
npx skills update transcribe-media --global
```

如果客户端没有显示更新后的内容，请刷新 Skill 列表或重新启动客户端。

<a id="agent-skill-guide-troubleshooting"></a>

### Skill 常见问题

- 找不到 Skill：检查安装范围，并确认安装目录中包含 `SKILL.md`。
- MCP 工具未出现：检查服务地址、Streamable HTTP 支持、客户端权限和鉴权配置。
- `invalid_api_key` 或 `api_key_revoked`：检查 MCP 连接中保存的 Key，不要把 Key 粘贴到对话中。
- `insufficient_quota`：补充 API Quota 后再创建任务。
- `idempotency_conflict`：同一 Key 被用于不同参数。保留原任务，不要自动换新 Key 绕过错误。
- 任务失败或部分成功：先查看任务错误和各能力错误，再决定是否另建任务。
- 字幕链接过期：在结果保留期内重新获取同一任务的结果。

MCP 连接和工具参数说明参见 [Video Transcriber AI MCP 接入说明](#mcp-overview)。
