メインコンテンツまでスキップ

バッチ STT

バッチ STT API は、音声ファイルをテキストに変換する HTTP ベースの REST API です。

対応フォーマット

ヒント

mp4, m4a, mp3, amr, flac, wav に対応しています。

認証トークン

認証ガイドに従ってトークンを取得してからご利用ください。


API 一覧

MethodURL説明
POST/v1/transcribe変換ジョブの作成
GET/v1/transcribe/{TRANSCRIBE_ID}変換結果の取得

1) [POST] /v1/transcribe

保存済みの音声ファイルに対する変換を要求します。

HTTP リクエスト

POST https://openapi.vito.ai/v1/transcribe

リクエストヘッダー

Authorization: Bearer {YOUR_JWT_TOKEN}
  • scheme: bearer
  • bearerFormat: JWT

リクエストボディ

content-type: multipart/form-data

FieldTypeRequired
configRequestConfigrequired
fileBinaryrequired

RequestConfig

Name説明TypeRequired既定値
model_name音声認識モデルstringoptionalsommers, whispersommers
language言語、whisper 専用stringoptionalko, detect, multiko
language_candidates言語検出候補arrayoptional["ko","ja","zh","en"]
use_diarization話者分離booleanoptionalfalse
diarization.spk_count話者数(use_diarization が true のとき)integeroptional0 以上0 (自動)
use_itn英字/数字/単位の正規化booleanoptionaltrue
use_disfluency_filterフィラーワード除去booleanoptionaltrue
use_profanity_filter不適切語フィルタbooleanoptionalfalse
use_paragraph_splitter段落分割booleanoptionaltrue
paragraph_splitter.max1 段落の最大文字数(use_paragraph_splitter が true のとき)integeroptional1 以上50
domainドメインstringoptionalGENERAL, CALLGENERAL
use_word_timestamp単語タイムスタンプbooleanoptionalfalse
keywordsキーワードブーストarrayoptional
注意
  • POST の同時処理数はレート制限に従います。完了の判定は GET API で行ってください。
  • 最大ファイルサイズ: 2GB、最大長: 4 時間。
  • リクエストは受付順に処理されます。長時間ファイルや混雑時は開始まで時間を要する場合があります。

サンプル 1

transcribe.sh
curl -X "POST" \
"https://openapi.vito.ai/v1/transcribe" \
-H "accept: application/json" \
-H "Authorization: Bearer ${YOUR_JWT_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "file=@sample.wav" \
-F 'config={}'

サンプル 2

transcribe.sh
curl -X "POST" \
"https://openapi.vito.ai/v1/transcribe" \
-H "accept: application/json" \
-H "Authorization: Bearer ${YOUR_JWT_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "file=@sample.wav" \
-F 'config={
"use_diarization": true,
"diarization": {
"spk_count": 2
},
"use_itn": false,
"use_disfluency_filter": false,
"use_profanity_filter": false,
"use_paragraph_splitter": true,
"paragraph_splitter": {
"max": 50
}
}'

レスポンス

成功時:

{"id": "{TRANSCRIBE_ID}"}

エラー

HTTP StatusCodeNotes
400H0001パラメータ不正
400H0010非対応フォーマット
401H0002無効トークン
413H0005サイズ超過
413H0006長さ超過
429A0001使用量超過
429A0002同時処理超過
500E500サーバーエラー

失敗例:

{"code":"H0001","msg":"unexpected end of JSON input"}

2) [GET] /v1/transcribe/{TRANSCRIBE_ID}

  • POST で返された TRANSCRIBE_ID に対する結果を取得します。

HTTP リクエスト

GET https://openapi.vito.ai/v1/transcribe/{TRANSCRIBE_ID}

リクエストヘッダー

Authorization: Bearer {YOUR_JWT_TOKEN}

サンプル

get_transcript.sh
curl -X "GET" \
"https://openapi.vito.ai/v1/transcribe/${TRANSCRIBE_ID}" \
-H "accept: application/json" \
-H "Authorization: Bearer ${YOUR_JWT_TOKEN}"

レスポンス本文(抜粋)

Name説明Type
idtranscribe idstring
statusステータスstringtranscribing, completed, failed
results.utterances発話配列array
results.utterances.start_at開始時刻(ms)integer
results.utterances.duration継続時間(ms)integer
results.utterances.msgテキストstring
results.utterances.spk話者/チャネル idinteger
results.utterances.lang言語stringISO 639-1
ヒント

長時間ファイル対応のためポーリング方式です。transcribing の場合は 5 秒程度の間隔で最終状態まで取得してください。短すぎる間隔は 429 を招く可能性があります。

Unified example

In the example script below, you can combine the desired settings with the PRESET environment variable. The default is sommers_basic.

transcribe.py
import json
import os
import time
from typing import Any, Dict, Optional

import requests


class RTZROpenAPIClient:
"""Minimal client for RTZR OpenAPI (auth + STT file).

- Fetches JWT via /v1/authenticate using client_id/client_secret
- Submits a file transcription job via /v1/transcribe
- Polls /v1/transcribe/{id} every few seconds until completed/failed
"""

def __init__(
self,
client_id: Optional[str] = None,
client_secret: Optional[str] = None,
base_url: str = "https://openapi.vito.ai",
) -> None:
self.base_url = base_url.rstrip("/")
self.client_id = client_id or os.getenv("RTZR_CLIENT_ID")
self.client_secret = client_secret or os.getenv("RTZR_CLIENT_SECRET")
if not self.client_id or not self.client_secret:
raise ValueError(
"Missing credentials. Set RTZR_CLIENT_ID and RTZR_CLIENT_SECRET "
"environment variables, or pass client_id/client_secret to RTZROpenAPIClient."
)
self._sess = requests.Session()
self._token: Optional[Dict[str, Any]] = None

@property
def token(self) -> str:
# Renew if missing or expiring within 30 minutes
if self._token is None or self._token.get("expire_at", 0) < time.time() - 1800:
resp = self._sess.post(
f"{self.base_url}/v1/authenticate",
data={"client_id": self.client_id, "client_secret": self.client_secret},
)
resp.raise_for_status()
self._token = resp.json()
access = self._token.get("access_token")
if not access:
raise RuntimeError("authenticate: 'access_token' not found in response")
return access

def _auth_headers(self) -> Dict[str, str]:
return {"Authorization": f"Bearer {self.token}"}

def transcribe_file(self, file_path: str, config: Dict[str, Any]) -> Dict[str, Any]:
url = f"{self.base_url}/v1/transcribe"
with open(file_path, "rb") as f:
files = {"file": (os.path.basename(file_path), f)}
data = {"config": json.dumps(config)}
resp = self._sess.post(url, headers=self._auth_headers(), files=files, data=data)
resp.raise_for_status()
return resp.json()

def get_transcription(self, transcribe_id: str) -> Dict[str, Any]:
url = f"{self.base_url}/v1/transcribe/{transcribe_id}"
resp = self._sess.get(url, headers=self._auth_headers())
resp.raise_for_status()
return resp.json()

def wait_for_result(
self,
transcribe_id: str,
poll_interval_sec: int = 5,
timeout_sec: int = 3600,
) -> Dict[str, Any]:
deadline = time.time() + timeout_sec
while True:
if time.time() > deadline:
raise TimeoutError("Timed out waiting for transcription result")
result = self.get_transcription(transcribe_id)
status = result.get("status")
if status in ("completed", "failed"):
return result
time.sleep(poll_interval_sec)


# Preset configurations
PRESETS: Dict[str, Dict[str, Any]] = {
"sommers_basic": { # 1) sommers without diarization
"model_name": "sommers",
"use_diarization": False,
"domain": "GENERAL",
},
"sommers_call_diarization": { # 2) sommers + diarization + CALL, spk_count=2
"model_name": "sommers",
"domain": "CALL",
"use_diarization": True,
"diarization": {"spk_count": 2},
},
"whisper_en_diarization": { # 3) whisper + diarization, language=en
"model_name": "whisper",
"language": "en",
"use_diarization": True,
},
# Additional commonly requested options
"paragraph_split_80": {"use_paragraph_splitter": True, "paragraph_splitter": {"max": 80}},
"keywords_example": {"keywords": ["stt", "returnzero", "api"]},
"with_word_timestamps": {"use_word_timestamp": True},
"disfluency_on": {"use_disfluency_filter": True},
"profanity_on": {"use_profanity_filter": True},
"whisper_detect_multi": {
"model_name": "whisper",
"language": "multi",
"language_candidates": ["ko", "en", "ja"],
},
}


def main():
audio_path = os.getenv("AUDIO_PATH", "sample.wav")
preset_name = os.getenv("PRESET", "sommers_basic")

if preset_name not in PRESETS:
raise ValueError(f"Unknown PRESET '{preset_name}'. Available: {sorted(PRESETS.keys())}")

config = PRESETS[preset_name]

client = RTZROpenAPIClient()

submit = client.transcribe_file(audio_path, config)
transcribe_id = submit.get("id")
result = client.wait_for_result(transcribe_id, poll_interval_sec=5)
print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
main()
  • sommers_basic: model_name=sommers, use_diarization=false, domain=GENERAL
  • sommers_call_diarization: model_name=sommers, domain=CALL, use_diarization=true, diarization.spk_count=2
  • whisper_en_diarization: model_name=whisper, language=en, use_diarization=true
  • paragraph_split_80: use_paragraph_splitter=true, paragraph_splitter.max=80
  • keywords_example: keywords=["stt","returnzero","api"]
  • with_word_timestamps: use_word_timestamp=true
  • disfluency_on: use_disfluency_filter=true
  • profanity_on: use_profanity_filter=true
  • whisper_detect_multi: model_name=whisper, language=multi, language_candidates=["ko","en","ja"]