November 9, 2026 · 4 min read
How to Get YouTube Transcripts in Go
In short
Fetch YouTube transcripts in Go with net/http: typed structs for the JSON response, SRT and VTT downloads, translation, and retries on temporary errors.
Go's standard library is all you need. One GET request to the TranscriptYT YouTube Transcript API returns any public video's transcript with timestamps, so your service never scrapes YouTube or runs into datacenter IP blocks.
Fetch and decode a transcript
Create a free key in the dashboard (100 transcripts, no card) and export it as TRANSCRIPTYT_KEY.
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"time"
)
type Segment struct {
Start float64 `json:"start"`
Duration float64 `json:"duration"`
Text string `json:"text"`
}
type Transcript struct {
VideoID string `json:"videoId"`
Title string `json:"title"`
Language string `json:"language"`
Source string `json:"source"`
Segments []Segment `json:"segments"`
}
type apiError struct {
Error struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
var client = &http.Client{Timeout: 120 * time.Second}
func getTranscript(video string) (*Transcript, error) {
q := url.Values{"url": {video}}
req, err := http.NewRequest("GET", "https://transcript-yt.com/v1/transcript?"+q.Encode(), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("TRANSCRIPTYT_KEY"))
res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
var e apiError
json.NewDecoder(res.Body).Decode(&e)
return nil, fmt.Errorf("%s: %s", e.Error.Code, e.Error.Message)
}
var t Transcript
return &t, json.NewDecoder(res.Body).Decode(&t)
}
func main() {
t, err := getTranscript("https://youtu.be/5f3Pn-N6yLc")
if err != nil {
panic(err)
}
fmt.Println(t.Title, t.Language, t.Source)
for _, s := range t.Segments[:3] {
fmt.Printf("%7.1fs %s\n", s.Start, s.Text)
}
}Subtitle files and translation
Add format=srt, vtt, text, csv, or md and copy the body straight to a file. Add translate_to=es (or any of 150+ codes) to translate while keeping the timings.
q := url.Values{"url": {"5f3Pn-N6yLc"}, "format": {"srt"}, "translate_to": {"es"}}
// same request as above, then:
f, _ := os.Create("captions.es.srt")
defer f.Close()
io.Copy(f, res.Body)Retries
BLOCKED, RATE_LIMITED, and UPSTREAM_ERROR are temporary; retry them with exponential backoff. VIDEO_UNAVAILABLE, NO_CAPTIONS, and LANGUAGE_NOT_AVAILABLE are final for that video, and a 402 means you're out of credits. Failed requests are never billed. Every code is listed in the docs.
Common questions
Is there a Go package for YouTube transcripts?
You don't need one: the API is a single GET request, and the structs below decode the response. Any language that can send HTTPS works the same way.
How do I get plain text instead of segments?
Add format=text to the query and read the body as a string, or join the segments' Text fields.
Can I fetch many videos concurrently?
Yes. Paid plans allow 200 requests a minute; run a small worker pool with goroutines, or use the batch endpoint for up to 500 videos in one request, depending on plan.