# How to Get YouTube Transcripts in Go

Source: https://transcript-yt.com/blog/youtube-transcript-api-go · Updated 2026-11-09 · TranscriptYT

> 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](https://transcript-yt.com/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](https://transcript-yt.com/dashboard/keys) (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](https://transcript-yt.com/docs).

## FAQ

### 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.
