Zoom の文字起こし・翻訳 AI を "Zoom 以外" で使う — Google Meet / Teams / YouTube、どんなサイトでもリアルタイム字幕+翻訳
「Zoom の会議 AI は Zoom の中でしか使えない」——そう思っていませんか?
今年登場した Zoom AI Services の Scribe(文字起こし) と Translator(翻訳) は、Zoom 会議に紐づかない ただの REST API です。つまり音声さえ取れれば、Google Meet でも Teams でも YouTube でも、Zoom の高精度 AI で字幕&翻訳ができます。
本記事では、OSS のリアルタイム翻訳アプリ Sokuji(Chrome 拡張・GitHub 921★) に Zoom AI Services を実際に組み込み、"どんなサイトでも動く" ようにした実装と、公式ドキュメントに載っていないハマりどころを全部公開します。
↑ これは Google Meet です。でも字幕と翻訳は Zoom の AI が動かしています。
対象読者 / 先に結論
対象読者:ブラウザ拡張から外部の AI API を叩きたい人、Zoom AI Services を実プロダクトに組み込みたい人。
そして本記事の実装で ハマった 4 つの罠を先にまとめておきます(詳細は各章)。ここだけでも持ち帰ってもらえれば、同じ所で溶けずに済みます。
- 認証 は Build Platform の API Key/Secret から HS256 JWT を自作する(Marketplace の OAuth とは別系統)
-
fileは data-URI(生 base64 は400 UNSUPPORTED_MEDIA) -
翻訳ペアは片側が必ず英語(
ja→zhは弾かれる) -
拡張は
host_permissionsで CORS を越える(CSP のconnect-srcだけでは足りない)
1. なぜ "Zoom 以外" で動くのか
まずここが本記事のキモです。Zoom の AI には、混同しやすい 2 種類があります。
| 使える場所 | 実体 | |
|---|---|---|
| AI Companion | Zoom 会議の中だけ | 会議機能の一部 |
| AI Services API(Scribe / Translator / Summarizer) | どこでも | ただの REST API |
Scribe は「音声 → テキスト」、Translator は「テキスト → テキスト」。どちらも https://api.zoom.us/v2/aiservices/... への普通の POST にすぎません。音声のソースが Zoom である必要はないのです。
だから、ブラウザ拡張でタブ音声やマイク音声を拾って API に流せば、Google Meet だろうと YouTube だろうと、Zoom の AI で字幕・翻訳ができる——というわけです。
2. 全体アーキテクチャ
拡張のサイドパネル
→ タブ / マイク音声(PCM)
→ VAD で発話を区切る(Silero VAD)
→ 発話を WAV(data-URI) 化 → Scribe(文字起こし)
→ Translator(翻訳)
→ 画面に字幕オーバーレイ
ポイントは VAD(音声区間検出)で「1 発話ずつ」区切ること。Scribe はストリーミングではなく ファイル単位の同期 API(Fast モード) なので、「話し終わった 1 文」を送って結果を受け取る 準リアルタイム方式にしています。1 発話あたりの往復はおよそ 2 秒です。
3. 認証:$20 無料クレジットと Build Platform の JWT(ハマり①)
最初の関門が認証です。Zoom AI Services の認証は、Marketplace の普通の OAuth アプリとは別系統でした。
- 使うのは Zoom Build Platform の API Key / API Secret。
- これを使って HS256 の JWT を自分で署名し、
Authorization: Bearer <jwt>で投げます。 - $20 の無料クレジットは、開発者ポータルの Plans & Billing → Zoom Build Platform(旧 Zoom SDK Universal Credit) の無料トライアルから。※ Build Platform は新規/独立アカウントが必要な点に注意。
JWT はブラウザ内で Web Crypto を使って署名できます(BYOK・クライアント署名)。
// API Key / Secret から HS256 JWT を作る(iss = API Key)
async getToken(): Promise<string> {
const now = Math.floor(Date.now() / 1000);
const iat = now - 30, exp = iat + 7200;
const header = base64urlString(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
const payload = base64urlString(JSON.stringify({ iss: this.apiKey, iat, exp }));
const signingInput = `${header}.${payload}`;
const key = await crypto.subtle.importKey(
'raw', new TextEncoder().encode(this.apiSecret),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['sign'],
);
const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(signingInput));
return `${signingInput}.${base64url(new Uint8Array(sig))}`;
}
💡 Marketplace の Server-to-Server OAuth(Account ID + Client ID/Secret)でも通ると案内されますが、実際に AI Services を叩くと Build Platform の API Key/Secret + JWT が確実でした。ここで認証方式を取り違えると延々ハマります。
4. 音声の渡し方:生 base64 では通らない。data: URI が正解(ハマり②)
transcribe の file フィールドに ただの base64 文字列を入れると、こう返ってきます。
{ "code": 400, "reason": "UNSUPPORTED_MEDIA",
"message": "File extension \"\" is not supported. Supported: .m4a, .mp3, .mp4, .wav, .webm" }
正解は data-URI。data:audio/wav;base64,<bytes> にすると 200 で正しく文字起こしされます(公式サンプルは公開 URL を渡していますが、バイトを直送するなら data-URI が必要)。
export async function transcribe(token, wavDataUri, language) {
const json = await post('/scribe/transcribe', token, {
file: wavDataUri, // ← "data:audio/wav;base64,..." が必須
config: { language, word_time_offsets: true },
});
return json?.result?.text_display ?? '';
}
💡 実は自動レビュー bot に「生 base64 が正しいのでは」と指摘されたのですが、実 API で叩いて検証すると data-URI が必須でした。ドキュメントより実測、です。
5. 翻訳の制約:ペアの片側は必ず英語(ハマり③)
Translator には強い制約があります。言語ペアのどちらか一方が英語でなければならないのです。
-
ja → zh(日本語→中国語)を投げると400 "one side must be English"。 -
en ↔ Xの形だけが通ります(ja → en、en → zhなど)。
なので Sokuji 側では「英語を軸にした非対称の言語マトリクス」で提供言語を設計しています。
export async function translate(token, text, sourceLanguage, targetLanguage) {
const json = await post('/translator/translate', token, {
text,
config: { source_language: sourceLanguage, target_languages: [targetLanguage] },
});
return json?.result?.translations?.[targetLanguage] ?? '';
}
💡 おまけの実測:
pt-PTもpt-BRも現状 500 が返りました(zh-CN/ko-KR/fr-FRは 200)。対応言語は必ず実際に叩いて確かめるのが安全です。
6. 拡張で CORS を越える:CSP だけでは足りない。host_permissions(ハマり④)
拡張から api.zoom.us を fetch すると CORS エラーになります。これは "プロバイダーを追加したときの定番の罠" でした。
-
api.zoom.usは寛容なAccess-Control-Allow-Originを返しません。 -
CSP の
connect-srcに足すだけでは不十分。Manifest V3 ではhost_permissionsに追加して初めて、拡張ページからの cross-origin fetch が CORS を越えられます。
"host_permissions": [
"...",
"https://api.zoom.us/*" // ← これが無いと translator/translate で CORS エラー
]
OpenAI や Gemini は寛容な CORS を返すため
connect-srcだけで動きますが、Zoom はそうではありません。ここは API ごとに違うので要注意。
7. どのサイトで動く?
Sokuji の content script は Google Meet / Microsoft Teams / Zoom(Web) に注入され、サイドパネルは どのページでも開けます。音声は タブ音声 or マイクから取得するので、YouTube などの動画にも字幕を出せます。
「Zoom の AI を YouTube の日本語動画に当てて英語字幕を出す」——字面だけ見ると矛盾していますが、REST API なら普通にできてしまいます。
↑ ここは YouTube。Zoom は 1 ミリも開いていませんが、Zoom の AI が字幕を出しています。
8. レイテンシとコスト(実測)
| 項目 | 実測値 |
|---|---|
| 1 発話(3–7 秒)の端到端 | 約 2.0–2.7 秒(Scribe 約 1.4–2.5s + Translator 約 0.6–0.8s) |
| Scribe 料金 | $0.0033 / 分($20 で約 100 時間) |
| 対応ソース言語(Scribe) | en-US / zh-CN / ja-JP / es-ES / it-IT |
ストリーミングではないので "喋りながら 1 文字ずつ" は出ませんが、1 発話ごとの準リアルタイム字幕としては十分実用的でした。
9. まとめ
- Zoom AI Services は "Zoom 専用" ではない。 Scribe / Translator は REST API なので、拡張に載せれば どんなサイトの音声でも字幕+翻訳にできる。
- 実運用のカギは 4 つのハマりどころ:
- Build Platform の API Key/Secret + HS256 JWT(Marketplace OAuth とは別系統)
-
fileは data-URI(生 base64 は 400) - 翻訳ペアは片側が英語
- 拡張は
host_permissionsで CORS 越え
- 実装は OSS の Sokuji に入っています。$20 の無料クレジットで今すぐ試せます。




