コンテンツにスキップ
  1. インサイト

スピーチtoテキストAPI連携:デベロッパーガイド

執筆者
Jack Limebear

聴くこの記事を聴く

スピーチtoテキストAPIを使うと、デベロッパーは音声認識や文字起こし機能をアプリに直接組み込めます。ただし、STT APIを接続する前に、いくつかのアーキテクチャ設計を決めておく必要があります。

どの文字起こしモードを使いますか?長時間のオーディオファイルはどう処理しますか?プロダクト名や固有名詞の正しい表記はどう確保しますか?少し先を見越して計画することで、スケーラブルなスピーチtoテキストAPI連携をデベロッパーアプリに実現できます。

このガイドでは、ElevenLabsのスピーチtoテキストAPI連携に必要なすべてを、すぐに本番環境で使えるわかりやすいコード例とともに解説します。参考として、ElevenLabsのスピーチtoテキストクイックスタートAPIガイドも別ウィンドウで開いておくと便利です。

まとめ

  • ElevenLabsのスピーチtoテキストには2つのモードがあります:バッチ(録音済み音声用)とリアルタイム(WebSocket経由のライブ音声ストリーム用)です。
  • Scribe v2は、90以上の言語でバッチ文字起こしに対応し、話者分離、キーターム指定、エンティティ検出、単語ごとのタイムスタンプ、マルチチャンネル対応などの機能があります。
  • Scribe v2 リアルタイムは、約150msの低遅延でライブストリーミングに対応し、話している途中の部分的な文字起こしと、発話が終わった時点で確定した文字起こしを返します。
  • 8分を超えるファイルは、Scribe v2が自動で分割し並列で文字起こしします。長時間のジョブには、同期レスポンスを待つのではなくwebhookを使いましょう。
  • キーターム指定は、コンテキストを使って特定の用語にモデルの認識を寄せる機能です。プロダクト名や専門用語、珍しい固有名詞などでキーワードリストよりも信頼性があります。

スピーチtoテキストAPI連携:バッチとリアルタイムの違い

ElevenLabsのSTT API連携は、まずバッチかリアルタイムかのアーキテクチャ選択から始まります。どちらも強力なSTTモデルをデベロッパー環境に提供しますが、この選択が利用できる機能や全体の遅延などに影響します。

2つのモードの違いは以下の通りです:

  • バッチ(Scribe v2):音声やビデオファイル全体を受け取り、文字起こしして1つのレスポンスで全文を返します。最大1,000個のキーターム、最大32人の話者分離、エンティティ検出、マルチチャンネル、非同期配信用webhookなど幅広い機能に対応。標準モードで最大3GB・10時間までのファイルをサポートします。
  • リアルタイム(Scribe v2 Realtime):WebSocket経由でライブ音声ストリームを受け取り、約150msの遅延で部分的・確定済みの文字起こしを返します。最大50個のキータームや単語ごとのタイムスタンプに対応。ボイスアシスタントやライブ字幕、ユーザーが話しながら即時応答が必要なアプリに最適です。

さらに詳しく違いを見ていきましょう。

Feature
Batch (Scribe v2)
Realtime (Scribe v2 Realtime)
Input
Pre-recorded audio or video file
Live audio stream
Latency
File duration + processing time
~150ms
Keyterms
Up to 1,000 (50 chars each)
Up to 50 (20 chars each)
Speaker diarization
Up to 32 speakers
-
Entity detection
Entity detection, up to 56
-
Multichannel
Up to 5 channels
-
Languages
Accurate in 90+ languages
Accurate in 90+ languages
Async delivery
Webhooks
-
Best for
Transcription pipelines, meeting recordings, long-form audio
Voice agents, live captions, real-time assistants

簡単な目安:音声が文字起こし前にすべて揃っている場合はバッチ、音声が生成されながら文字起こしする場合はリアルタイムを使いましょう。

Scribe v2とScribe v2 Realtimeの選び方

バッチとリアルタイムは用途が異なるため、最初に間違った方を選ぶと後で作り直しになることが多いです。

より詳しくは、用途ごとにモデルをどう選ぶかを紹介します:

  • Scribe v2は、音声が処理開始前にすべて揃っている場合(会議録音、ポッドキャスト文字起こし、メディアファイル、オフライン処理など)に最適です。話者分離やエンティティ検出、最大1,000個のキータームなど全機能に対応します。
  • Scribe v2 Realtimeはライブ音声向けです。WebSocketストリームを受け取り、音声が届くたびに文字起こしを返すので、ボイスアシスタントやユーザーが話し終わる前に応答が必要な場面に最適です。

もう1つ考慮すべき点は、言語によって精度が異なることです。言語ごとの文字起こし誤り率(WER)を確認してから言語構成を決めるのがおすすめです。Scribe v2は90以上の対応言語ごとにWERランクを公開しています。

高精度(WER5%以下)は主要なヨーロッパ言語、日本語、インドネシア語、ベトナム語などをカバー。高い精度(WER5~10%)はヒンディー語、ベンガル語、中国語、韓国語、ジョージア語など。詳しくは言語サポートドキュメントでご確認ください。

ElevenLabsスピーチtoテキストAPIのセットアップ

クライアントを動かすには、たった2ステップだけです。まずSDKをインストールし、次に認証情報を安全に保存します。

最初の文字起こしリクエストを書く前に、この2つを済ませましょう。

SDKをインストールし、APIキーを.envファイルやプラットフォームのシークレットマネージャーで管理されたシークレットとして保存します。APIキーをアプリにハードコーディングしないでください。

Python

pip install elevenlabs
pip install python-dotenv

TypeScript

npm install @elevenlabs/elevenlabs-js

.envファイルを作成:

ELEVENLABS_API_KEY=<your_api_key_here>

クライアントを初期化:

Python

import os
from dotenv import load_dotenv
from elevenlabs.client import ElevenLabs

load_dotenv()

elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)

TypeScript

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

ElevenLabs STT APIで最初のバッチ文字起こし

クライアントの初期化ができたら、最初のファイル送信が可能です。

バッチAPIはファイルを受け取り、文字起こしして同期的に全結果を返します。下記の例では、話者分離とオーディオイベントタグ付けを有効にしたリモート音声ファイルの文字起こしを行います。

Python

# example.py
import os
from dotenv import load_dotenv
from io import BytesIO
import requests
from elevenlabs.client import ElevenLabs

load_dotenv()

elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)

audio_url = (
    "https://storage.googleapis.com/eleven-public-cdn/audio/marketing/nicole.mp3"
)
response = requests.get(audio_url)
audio_data = BytesIO(response.content)

transcription = elevenlabs.speech_to_text.convert(
    file=audio_data,
    model_id="scribe_v2",          # Model to use
    tag_audio_events=True,          # Tag audio events like laughter, applause, etc.
    language_code="eng",            # Language of the audio file. If set to None, the model will detect the language automatically.
    diarize=True,                   # Whether to annotate who is speaking
)

print(transcription)

実行:

python example.py

レスポンスオブジェクトには、全文テキスト、単語ごとのタイムスタンプや話者ID、検出されたオーディオイベントが含まれます。

各単語エントリには、次の3つのいずれかのtypeフィールドが付きます:

  • word:音声内で文字起こしされた単語。
  • spacing:スペースを使う言語で単語間にある空白。日本語や広東語、ビルマ語など多くの言語では該当しません。
  • audio_event:笑いや咳など、発話以外の音声イベントのタグ。

レスポンス構造は以下のようになります:

{
  "language_code": "en",
  "language_probability": 1,
  "text": "With a soft and whispery American accent, I'm the ideal choice for creating ASMR content, meditative guides, or adding an intimate feel to your narrative projects.",
  "words": [
    {
      "text": "With",
      "start": 0.119,
      "end": 0.259,
      "type": "word",
      "speaker_id": "speaker_0"
    },
    {
      "text": " ",
      "start": 0.239,
      "end": 0.299,
      "type": "spacing",
      "speaker_id": "speaker_0"
    },
    {
      "text": "a",
      "start": 0.279,
      "end": 0.359,
      "type": "word",
      "speaker_id": "speaker_0"
    }
  ]
}

language_probabilityフィールドは、モデルが言語検出にどれだけ自信があるか(0.00~1.00)を示します。

STT APIリアルタイム連携

リアルタイムSTTAPI連携は少し異なる流れです。1リクエスト1レスポンスではなく、WebSocket接続を開いて文字起こしが届くたびに受け取ります。

リアルタイムAPIはWebSocketでライブ音声ストリームを受け取り、音声が届くたびに文字起こしを返します。2種類の文字起こしタイプがあります:

  • 部分的な文字起こし:モデルが受信中の音声を処理しながら更新される途中経過。内容が変わる場合もあります。
  • 確定済みの文字起こし:発話区間が終わった時点で確定した最終結果。内容は変わりません。「タイムスタンプを含める」オプションをtrueにすると単語ごとのタイムスタンプも含まれます。

クライアント側の実装では、APIキーの代わりに使い捨てトークンを利用します。これは15分で期限切れになる一時的な認証情報で、サーバー側で発行されるためAPIキーがブラウザに漏れることはありません。

ステップ1:使い捨てトークンを生成(サーバー側)

// Node.js server
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

app.get("/scribe-token", yourAuthMiddleware, async (req, res) => {
  const token = await elevenlabs.tokens.singleUse.create("realtime_scribe");

  res.json(token);
});

ステップ2:接続して文字起こし(クライアント側・React)

import { useScribe } from "@elevenlabs/react";

function MyComponent() {
  const scribe = useScribe({
    modelId: "scribe_v2_realtime",
    onPartialTranscript: (data) => {
      console.log("Partial:", data.text);
    },
    onCommittedTranscript: (data) => {
      console.log("Committed:", data.text);
    },
    onCommittedTranscriptWithTimestamps: (data) => {
      console.log("Committed with timestamps:", data.text);
      console.log("Timestamps:", data.words);
    },
  });

  const handleStart = async () => {
    // Fetch a single use token from the server
    const token = await fetchTokenFromServer();

    await scribe.connect({
      token,
      microphone: {
        echoCancellation: true,
        noiseSuppression: true,
      },
    });
  };

  return (
    <div>
      <button onClick={handleStart} disabled={scribe.isConnected}>
        Start Recording
      </button>
      <button onClick={scribe.disconnect} disabled={!scribe.isConnected}>
        Stop
      </button>

      {scribe.partialTranscript && <p>Live: {scribe.partialTranscript}</p>}

      <div>
        {scribe.committedTranscripts.map((t) => (
          <p key={t.id}>{t.text}</p>
        ))}
      </div>
    </div>
  );
}

useScribeフックはWebSocket接続のライフサイクル、マイクアクセス、文字起こし状態を管理します。partialTranscriptは進行中のテキスト、committedTranscriptsは確定済みセグメントの配列です。

サーバー側ストリーミング(マイクではなくURLやファイルストリームから文字起こしする場合)は、サーバー側ストリーミングガイド.

長時間ファイルの並列処理とスケーリング

長時間ファイルは、一般的なAPIとは異なるスケーリングモデルが必要です。構築前に理解しておくと役立ちます。

バッチ文字起こしの並列処理は、一般的なAPIとは異なります。同時リクエスト数を制限するのではなく、Scribe v2が長時間ファイルを自動で内部的に並列処理します。

8分を超えるファイルは自動で分割され、同時に文字起こしされます。使われる同時セグメント数は次のように計算されます:

Concurrency = min(4, round_up(audio_duration_secs / 480))

具体例:

  • 15分のファイルは同時処理数2
  • 120分のファイルは同時処理数4(最大値)

標準モードでは最大10時間・3GBまでのファイルに対応。※マルチチャンネルモードは最大1時間までなので、下記のマルチチャンネル文字起こしセクションを参照してください。

対応フォーマットについて、STT APIは主要な音声・ビデオフォーマットをサポートしています:

  • 音声:AAC、AIFF、OGG、MP3、OPUS、WAV、FLAC、M4A、WebM。
  • ビデオ:MP4、AVI、MKV、MOV、WMV、FLV、WebM、MPEG、3GPP。

ビデオファイルもそのまま渡せば、音声トラックの文字起こしを受け取れます。事前処理は不要です。

キーターム指定

汎用モデルはブランド名や専門用語を誤認識しがちです。キーターム指定はそれを解決します。

キーターム指定は、文字起こし時に特定の単語やフレーズにモデルの認識を寄せる機能です。音声にプロダクト名や専門用語、珍しい固有名詞が含まれる場合に最適です。

キーターム指定の強みは、単なるキーワードリストではなく文脈を使う点です。たとえば「ElevenLabs」をキータームに指定すれば、話者が社名を言ったときに正しく文字起こしされます。指定しない場合、「I've worked at eleven labs for a year」などが誤認識されることがあります。

キーターム指定なし:

I work at eleven labs.

keyterms=["ElevenLabs"]指定時:

I work at ElevenLabs.

バッチは最大1,000個(各50文字)、リアルタイムは最大50個(各20文字)のキータームに対応します。

キーターム指定付きバッチ文字起こし

Python

import os
from dotenv import load_dotenv
from io import BytesIO
import requests
from elevenlabs.client import ElevenLabs

load_dotenv()

elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)

audio_url = (
    "https://storage.googleapis.com/eleven-public-cdn/documentation_assets/audio/stt-keyterm-prompting.mp3"
)
response = requests.get(audio_url)
audio_data = BytesIO(response.content)

transcription = elevenlabs.speech_to_text.convert(
    file=audio_data,
    model_id="scribe_v2",
    # Keyterms to prompt the model with.
    # Up to 1,000 keyterms can be provided, with a maximum length of 50 characters each
    keyterms=["ElevenLabs"],
)

print(transcription)

キーターム指定付きリアルタイムストリーミング

リアルタイムWebSocket接続時にキータームを渡します:

Python

connection = await elevenlabs.speech_to_text.realtime.connect(RealtimeUrlOptions(
    model_id="scribe_v2_realtime",
    keyterms=["ElevenLabs"],
))

またはWebSocket URLのクエリパラメータとして直接渡すこともできます:

wss://api.elevenlabs.io/v1/speech-to-text/realtime?model_id=scribe_v2_realtime&keyterms=ElevenLabs&keyterms=AnotherTerm

キーターム指定には追加コストがかかります。詳しくはAPI料金ページをご覧ください。

高度な機能

ここまででSTT APIの基本的な文字起こしフローを紹介しましたが、最終出力をさらに充実させる機能もいくつかあります。スピーチtoテキストAPIで使える高度な機能を紹介します:

  • 話者分離(誰が話しているかを特定)
  • ノンバーバティムモード(不要な言葉を除去)
  • エンティティ検出(機密データの検出)
  • マルチチャンネル文字起こし(音声チャンネルごとに分離)

それぞれ詳しく見ていきましょう。

話者分離

バッチリクエストでdiarize=Trueを指定すると、誰が話しているかを注釈できます。Scribe v2は最大32人まで対応。レスポンスの各単語にspeaker_id(例:speaker_0, speaker_1)が付き、話者ごとにセグメントを分けられます。

話者分離は会議文字起こしやインタビュー、多人数録音で、発言者を正しく割り当てる必要がある場合に便利です。

ノンバーバティムモード

no_verbatim=Trueにすると、モデルがフィラーや言い直し、つっかえなどを文字起こしから除去します。「えーっと、ま、もしかしたら、うーん、A案でいきましょう」は「もしかしたらA案でいきましょう」に変換されます。

字幕や要約、読みやすさ重視の用途に最適なクリーンな出力が得られます。バッチ(scribe_v2)・リアルタイム(scribe_v2_realtime)両方で利用可能です。

エンティティ検出とマスキング

Scribe v2は、文字起こし内のエンティティを検出・ラベル付けできます。各エンティティの正確なタイムスタンプも付与。カテゴリはPII(氏名、クレジットカード番号、SSN)、PHI(医療情報)、PCI(決済カード情報)など。全対応エンティティタイプはエンティティ検出ドキュメント.

で確認できます。

これはコンプライアンス用途に特に便利で、文字起こしを保存・表示する前に機密情報を自動でマスキングできます。エンティティ検出は1時間あたり$0.070の追加コストがかかります。詳しくはAPI料金ページ

をご覧ください。

マルチチャンネル文字起こし

use_multi_channel=Trueを指定すると、各音声チャンネルが独立して文字起こしされ、チャンネル番号に基づいて話者IDが割り当てられます。最大5チャンネルまで対応。マルチチャンネルモードの最大ファイル長は1時間です。

話者ごとに音声トラックが分かれている場合(例:電話録音で各参加者が別チャンネル)、マルチチャンネル文字起こしは、モノラルファイルでの話者分離よりも正確な話者割り当てが可能です。

webhookによる非同期配信

少量ならポーリングでも問題ありませんが、長時間ファイルや大量処理パイプラインではスケールしません。webhookを使えば、結果を自動で受け取れます。

長時間ファイルや大量文字起こしパイプラインでは、同期レスポンスを待つのは現実的ではありません。webhookを使えば、リクエスト送信後に結果が処理完了時にエンドポイントへ届くので、ポーリング不要です。

webhookの設定方法ElevenLabsダッシュボードで「Developers > Webhooks」をクリックし、「webhookを作成」

  • を選択して、以下を設定します:名前
  • :webhookのわかりやすい名前を入力します。コールバックURL
  • :webhookに公開可能なHTTPSエンドポイントを追加します。Webhook認証方式
  • :HMACまたはOAuthを選択(セキュリティのため推奨)。イベント:

「Transcription completed」を選択します。

webhook配信で文字起こしを送信

from dotenv import load_dotenv
from elevenlabs.client import ElevenLabs

load_dotenv()

elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)

def transcribe_with_webhook(audio_file):
    try:
        result = elevenlabs.speech_to_text.convert(
            file=audio_file,
            model_id="scribe_v2",
            webhook=True,
        )
        print(f"Transcription started: {result.request_id}")
        return result
    except Exception as e:
        print(f"Error starting transcription: {e}")
        raise e

webhook=Trueの場合、リクエストは文字起こしを返さずすぐに終了し、処理完了後にPOSTリクエストでエンドポイントに結果が届きます。

webhookエンドポイントの実装

import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';
import 'dotenv/config';
import express from 'express';

const elevenlabs = new ElevenLabsClient();
const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

app.post('/webhook/speech-to-text', (req, res) => {
  try {
    const signature = req.headers['elevenlabs-signature'];
    const payload = JSON.stringify(req.body);
    let event;

    try {
      // Verify the webhook signature.
      event = await elevenlabs.webhooks.constructEvent(payload, signature, WEBHOOK_SECRET);
    } catch (error) {
      return res.status(401).json({ error: 'Invalid signature' });
    }

    if (event.type === 'speech_to_text.completed') {
      const { requestId, status, text, language_code } = event.data;

      console.log(`Transcription ${requestId} completed`);
      console.log(`Language: ${language_code}`);
      console.log(`Text: ${text}`);

      processTranscription(requestId, text, language_code);
    } else if (status === 'failed') {
      console.error(`Transcription ${requestId} failed`);
      handleTranscriptionError(requestId);
    }

    res.status(200).json({ received: true });
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

app.listen(3000, () => {
  console.log('Webhook server listening on port 3000');
});

webhookペイロード構造

エンドポイントには以下の形式でPOSTが届きます:

{
  "type": "speech_to_text_transcription",
  "data": {
    "request_id": "some-request-id-123",
    "webhook_metadata": {},
    "transcription": {
      "language_code": "en",
      "language_probability": 0.98,
      "text": "Hello world!",
      "words": [
        {
          "text": "Hello",
          "start": 0.0,
          "end": 0.5,
          "type": "word",
          "speaker_id": "speaker_1"
        }
      ]
    }
  }
}

webhookセキュリティのベストプラクティス

webhookを使う際は、イベントが正しく配信・処理されるよういくつかのセキュリティ対策を取りましょう。

  • webhook署名の検証:必ずelevenlabs.webhooks.constructEvent()でwebhook署名を検証し、ElevenLabsからの正規イベントであることを確認してください。
  • HTTPSエンドポイントの利用:webhook URLはHTTPS必須です。通信中のデータ改ざんを防ぎます。
  • 適切なHTTPステータスコードの返却:成功時は200~299、クライアントエラー時は400~499(再送なし)、サーバーエラー時は500~599(再送あり)を返してください。
  • ローカル開発時はトンネリングツールを利用:ローカル開発では、ngrokなどのトンネリングツールでローカルサーバーを公開HTTPS URLにしましょう。

これらの対策で、webhookを安心して利用できます。

スピーチtoテキストAPI連携のポイントまとめ

本番用のスピーチtoテキストAPI連携は、いくつかの重要な選択に集約されます。

ここを押さえれば、あとはスムーズです:

  • 用途ごとにモードを選択:音声が処理前に揃っている場合はバッチ(scribe_v2)、音声が生成されながら文字起こしする場合(エージェント、ボイスアシスタント、ライブ字幕など)はリアルタイム(scribe_v2_realtime)を使いましょう。
  • 特定用語の精度向上にはキータームを活用:プロダクト名や専門用語、珍しい固有名詞はキータームとして渡しましょう。モデルが文脈を使って適切に反映します。
  • 長時間ファイルはAPIに任せる:8分超のファイルは自動で並列処理されるので、自分で分割する必要はありません。標準モードで最大10時間・3GBまで対応します。
  • 非同期パイプラインにはwebhookを活用:長時間ジョブや大量処理ではwebhook=Trueで送信して次の作業へ。受信したwebhookペイロードの署名検証も忘れずに。
  • クリーンな出力にはノンバーバティムモード:字幕や要約、NLP処理などにはno_verbatim=Trueでフィラーやつっかえを自動除去できます。
  • 音声トラック分離にはマルチチャンネルモード:録音が話者ごとにチャンネル分かれている場合(コールセンター録音やインタビューなど)、マルチチャンネル文字起こしは混合ファイルでの話者分離よりも正確です。なお、このモードは最大1時間までです。

さらに詳しく知りたい方は、APIリファレンス全体もご覧ください。

ElevenAPIでスピーチtoテキスト連携を構築しよう

このガイドを読めば、本番用スピーチtoテキストAPI連携に必要なパターンがすべて揃います。バッチ・リアルタイム文字起こし、高度なキーターム指定や非同期配信も含め、STT連携でアプリをすぐにリリースできます。

まずはスピーチtoテキストAPIについて詳しく学ぶか、サインアップしてElevenAPIで最初のコールを試してみましょう。

STT API連携に関するよくある質問

最高品質のAIオーディオで創造する