HomeBlogTranscribePlansAPI Keys

Trascrizione YouTube in C# con HttpClient e Records

App console C# che deserializza una trascrizione YouTube con System.Text.Json

Da dotnet run a una trascrizione stampata ci sono voluti circa due secondi sulla mia macchina, e la maggior parte era il percorso di andata e ritorno della rete. Nessun pacchetto NuGet appare da nessuna parte in questo articolo — ottenere una trascrizione YouTube in C# è una richiesta GET, e HttpClient più System.Text.Json sono spediti nella scatola da anni. Alla fine avrai un'app console completa con i tipi di record per la risposta, un Main asincrono e la gestione degli errori che legge l'oggetto di errore strutturato dell'API invece di indovinare dai codici di stato.

Tre record

L'API restituisce {"result": {...}} in caso di successo e {"error": {...}} in caso di errore, quindi modella direttamente l'envelope:

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);

Una singola chiamata Deserialize<Envelope> gestisce ogni risposta. Qualunque proprietà sia non-null è la tua risposta. RetryAfterSeconds è nullable perché solo gli errori di limite di velocità lo portano.

Il programma

Punta a .NET 8, funziona così come è su 6 e 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 mappa i campi camelCase dell'API sulle proprietà del record PascalCase, quindi non c'è un singolo attributo [JsonPropertyName] da nessuna parte. Il parametro url accetta ogni variante di URL YouTube più gli ID nudi di 11 caratteri, e l'intestazione può essere Authorization: Bearer yt_... invece di x-api-key se questo si adatta meglio al tuo middleware.

Su quel maxChars=40000

maxChars limita la lunghezza della trascrizione; 150000 è sia il valore predefinito che il limite massimo. Se le didascalie di un video vanno oltre il tuo limite, Truncated torna true e ottieni i primi N caratteri — quindi controlla il flag prima di assumere di avere il testo completo. Ho impostato 40000 nell'esempio perché queste trascrizioni di solito finiscono in un prompt LLM, e a circa quattro caratteri per token cioè un budget di 10k token. Se le tue chiamate al modello passano attraverso l'API OpenAI Responses, c'è un modo per saltare completamente l'impianto HTTP tramite il server MCP con OpenAI; l'equivalente di Anthropic è in utilizzo di trascrizioni con l'API Claude.

Codici di errore su cui fare il branch

ApiError.Code è una stringa stabile: UNAUTHORIZED (401), VALIDATION_ERROR e INVALID_URL (400), VIDEO_NOT_FOUND e TRANSCRIPT_UNAVAILABLE (404), RATE_LIMIT_EXCEEDED (429), YOUTUBE_ERROR (500). Quello che sorprende le persone è TRANSCRIPT_UNAVAILABLE: le trascrizioni provengono dai tracciamenti didascalici di YouTube, e un video il cui proprietario ha disabilitato le didascalie non ha nulla da restituire — nessuna politica di ritentativo lo risolve. Fai il pattern match sul codice, trattalo come un salto permanente.

Per una chiave di prova a zero iscritti, OTTIENI https://youtube2text.org/api/demo-key — è condivisa ed è limitata a 5 video al mese per IP, quindi è solo per gli esperimenti. Se Golang è anche nel tuo stack, la stessa chiamata in Golang usa il trucco dell'envelope identico.

Il riferimento completo leggibile dalla macchina — ogni parametro, ogni errore, pronto da incollare in un prompt di sistema — è su youtube2text.org/api.md.