HomeBlogTranscribePlansAPI Keys

C#でのYouTube文字起こし (HttpClientとレコード)

System.Text.JsonでYouTube文字起こしを逆シリアル化するC#コンソールアプリ

dotnet runから印字トランスクリプトまで、私のマシンで約2秒かかりました。ほとんどがネットワークラウンドトリップです。このポストではどこにもNuGetパッケージは表示されません―C#でYouTube文字起こしを取得することは1つのGETリクエストであり、HttpClientSystem.Text.Jsonは何年も箱に含まれています。最後には、レスポンスのレコード型、非同期Main、ステータスコードから推測する代わりにAPIの構造化エラーオブジェクトを読むエラーハンドリングを備えた完全なコンソールアプリがあります。

3つのレコード

API は成功時に{"result": {...}}を、失敗時に{"error": {...}}を返すため、エンベロープを直接モデル化します:

public record Transcript(
    string VideoId,
    string Title,
    string PubDate,
    string Content,
    int ContentSize,
    bool Truncated);

public record ApiError(
    string Code,
    string Message,
    int Status,
    int? RetryAfterSeconds);

public record Envelope(Transcript? Result, ApiError? Error);

1つのDeserialize<Envelope>呼び出しはすべてのレスポンスを処理します。どちらのプロパティが非nullであるかが答えです。RetryAfterSecondsはnullableです。レート制限エラーのみがそれを運ぶからです。

プログラム

.NET 8をターゲットに、6と7で機能します:

using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

public static class Program
{
    private static readonly HttpClient Http = new();
    private static readonly JsonSerializerOptions Json =
        new(JsonSerializerDefaults.Web);

    public static async Task Main(string[] args)
    {
        var videoUrl = args.Length > 0
            ? args[0]
            : "https://www.youtube.com/watch?v=dQw4w9WgXcQ";

        var request = new HttpRequestMessage(HttpMethod.Get,
            "https://youtube2text.org/api/transcribe" +
            $"?url={Uri.EscapeDataString(videoUrl)}&maxChars=40000");
        request.Headers.Add("x-api-key",
            Environment.GetEnvironmentVariable("YT2TEXT_KEY"));

        var response = await Http.SendAsync(request);
        var envelope = JsonSerializer.Deserialize<Envelope>(
            await response.Content.ReadAsStringAsync(), Json);

        if (envelope?.Error is { } err)
        {
            Console.Error.WriteLine($"{err.Code}: {err.Message}");
            if (err.RetryAfterSeconds is int wait)
                Console.Error.WriteLine($"Retry after {wait}s");
            Environment.Exit(1);
        }

        var t = envelope!.Result!;
        Console.WriteLine($"{t.Title} ({t.ContentSize} chars, truncated: {t.Truncated})");
        Console.WriteLine(t.Content[..Math.Min(t.Content.Length, 400)]);
    }
}

JsonSerializerDefaults.WebはAPIのcamelCaseフィールドをPascalCaseレコードプロパティにマップするため、単一の[JsonPropertyName]属性がありません。urlパラメータはすべてのYouTube URLバリアントと裸の11文字のIDを受け入れ、ヘッダーはミドルウェアにより適切に合わせばAuthorization: Bearer yt_...の代わりにx-api-keyにすることができます。

そのmaxChars=40000について

maxCharsは文字起こし長を制限します; 150000はデフォルトと厳しい上限の両方です。ビデオのキャプションがキャップを過ぎて実行される場合、Truncatedtrueで戻り、最初のN文字を取得します―全テキストを持っていると仮定する前にフラグをチェックしてください。例では40000を設定しました。これらの文字起こしは通常LLMプロンプトで終わるため、4文字あたり約1トークンで10kトークン予算です。モデルコールがOpenAI Responses APIを通じて行く場合、OpenAIを使用したMCPサーバー経由でHTTPプラミングを完全にスキップする方法があります。Anthropicに相当するのはClaude APIで文字起こしを使用するです。

分岐するエラーコード

ApiError.Codeは安定した文字列です: UNAUTHORIZED(401)、VALIDATION_ERRORINVALID_URL(400)、VIDEO_NOT_FOUNDTRANSCRIPT_UNAVAILABLE(404)、RATE_LIMIT_EXCEEDED(429)、YOUTUBE_ERROR(500)。人々を驚かせるのはTRANSCRIPT_UNAVAILABLE: 文字起こしはYouTubeの字幕トラックから来ます。そして、字幕を無効にした所有者のビデオは返すものがありません―再試行ポリシーはそれを修正しません。コードをパターンマッチし、永続的なスキップとして扱います。

ゼロサインアップテストキーの場合、GET https://youtube2text.org/api/demo-key―共有され、月5ビデオあたりIPで制限されています。実験のためだけです。Goもあなたのスタックにある場合、Golangでの同じ呼び出しは同じエンベロープトリックを使用します。

完全な機械可読リファレンス―すべてのパラメータ、すべてのエラー、システムプロンプトに貼り付ける準備ができて―はyoutube2text.org/api.mdにあります。