Transcript

Fetch YouTube video transcripts, captions, and metadata without an API key

Overview

The transcript module fetches YouTube video transcripts using YouTube's internal Innertube API. No YouTube Data API key is required — it works out of the box.

Import from the lyra-sdk/transcript subpath:

import { transcribeVideo, listCaptionTracks } from 'lyra-sdk/transcript'

The transcript module is completely independent from the YouTube Data API v3 client (yt()). It does not consume quota units and requires no API key.


transcribeVideo(idOrUrl, options?)

Fetch the transcript for a YouTube video. Accepts a video ID or any YouTube URL.

import { transcribeVideo } from 'lyra-sdk/transcript'

const lines = await transcribeVideo('dQw4w9WgXcQ')

Also accepts URLs:

const lines = await transcribeVideo('https://youtu.be/dQw4w9WgXcQ')

Options

PropertyTypeDefaultDescription
langstringBCP 47 language code (e.g. 'en', 'es', 'pt-BR')
userAgentstringChromeCustom User-Agent string for HTTP requests
useHttpbooleanfalseUse HTTP instead of HTTPS
includeMetabooleanfalseInclude video metadata in the response
customFetch(url: string, init?: RequestInit) => Promise<Response>fetchCustom fetch function for proxy/networking support
signalAbortSignalAbort in-flight requests
cacheCacheStoreCache instance (see Caching)
cacheTTLnumber3600000Cache TTL in milliseconds (1 hour default)
retriesnumber0Max retry attempts for transient failures
retryDelaynumber1000Base delay in ms for exponential backoff

Response

Returns an array of TranscriptLine objects:

PropertyTypeDescription
textstringTranscript text for this segment
durationnumberSegment duration in seconds
offsetnumberStart time in seconds from video start
langstringLanguage code of this transcript

Fetch with a specific language

const lines = await transcribeVideo('dQw4w9WgXcQ', { lang: 'es' })

Language availability

Not all videos have transcripts in every language. If the requested language is unavailable, a TranscriptLanguageError is thrown listing available alternatives.


Fetch with video metadata

Set includeMeta: true to get both transcript lines and video metadata in a single call:

import { transcribeVideo } from 'lyra-sdk/transcript'

const result = await transcribeVideo('dQw4w9WgXcQ', { includeMeta: true })

console.log(result.meta.title)   // "Rick Astley - Never Gonna Give You Up"
console.log(result.meta.author)  // "Rick Astley"
console.log(result.meta.views)   // 1600000000
console.log(result.lines.length) // 61

VideoMeta

PropertyTypeDescription
videoIdstringYouTube video ID
titlestringVideo title
authorstringChannel name
channelIdstringChannel ID
lengthSecondsnumberDuration in seconds
viewCountnumberTotal view count
descriptionstringFull video description
keywordsstring[]Video keyword tags
thumbnailsobject[]Thumbnail images with size info
isLiveContentbooleanWhether the video is a live stream

listCaptionTracks(idOrUrl, options?)

Discover what caption tracks are available for a video without downloading the transcript:

import { listCaptionTracks } from 'lyra-sdk/transcript'

const tracks = await listCaptionTracks('dQw4w9WgXcQ')

for (const track of tracks) {
  console.log(`${track.languageCode}${track.languageName}`)
  console.log(`  Auto-generated: ${track.isAutoGenerated}`)
}

CaptionTrack

PropertyTypeDescription
languageCodestringBCP 47 language code (e.g. 'en')
languageNamestringHuman-readable name (e.g. 'English')
isAutoGeneratedbooleanWhether captions are auto-generated (ASR)

TranscriptClient

Create a client with shared configuration for multiple requests:

import { TranscriptClient } from 'lyra-sdk/transcript'

const client = new TranscriptClient({
  lang: 'en',
  retries: 2,
  retryDelay: 500,
})

// Reuses config from constructor
const lines = await client.transcribe('dQw4w9WgXcQ')
const tracks = await client.availableTracks('dQw4w9WgXcQ')

Methods

MethodDescription
transcribe(videoId, overrides?)Fetch transcript with merged config
availableTracks(videoId, overrides?)List caption tracks with merged config

Both methods accept optional overrides that are merged with the constructor defaults.


Custom fetch (proxy support)

Pass a customFetch function to route requests through a proxy or custom networking layer:

const lines = await transcribeVideo('dQw4w9WgXcQ', {
  customFetch: async (url, init) => {
    return fetch(`https://my-proxy.example.com/fetch?url=${encodeURIComponent(url)}`, init)
  },
})

Output formatters

Convert transcript lines into standard subtitle formats:

import { transcribeVideo, toSRT, toVTT, toPlainText } from 'lyra-sdk/transcript'

const lines = await transcribeVideo('dQw4w9WgXcQ')

// SubRip format
const srt = toSRT(lines)

// WebVTT format
const vtt = toVTT(lines)

// Plain text (custom separator, defaults to newline)
const text = toPlainText(lines, ' ')

Error handling

The transcript module provides granular error classes for every failure case:

import {
  transcribeVideo,
  TranscriptError,
  TranscriptRateLimitError,
  TranscriptVideoUnavailableError,
  TranscriptDisabledError,
  TranscriptNotFoundError,
  TranscriptLanguageError,
  TranscriptInvalidVideoIdError,
  TranscriptInvalidLangError,
} from 'lyra-sdk/transcript'

try {
  const lines = await transcribeVideo('dQw4w9WgXcQ', { lang: 'de' })
} catch (error) {
  if (error instanceof TranscriptLanguageError) {
    console.log(`Language "${error.lang}" not available`)
    console.log('Available:', error.availableLangs.join(', '))
  } else if (error instanceof TranscriptRateLimitError) {
    console.log('Rate limited — try again later or use a proxy')
  } else if (error instanceof TranscriptDisabledError) {
    console.log(`Transcripts disabled for video "${error.videoId}"`)
  } else if (error instanceof TranscriptVideoUnavailableError) {
    console.log(`Video "${error.videoId}" is unavailable`)
  }
}

Error classes

ErrorWhen it's thrown
TranscriptErrorBase class for all transcript errors
TranscriptInvalidVideoIdErrorInput is not a valid video ID or URL
TranscriptInvalidLangErrorLanguage code doesn't match BCP 47 format
TranscriptVideoUnavailableErrorVideo doesn't exist or is private
TranscriptRateLimitErrorYouTube is rate-limiting your IP (reCAPTCHA page)
TranscriptDisabledErrorVideo has captions disabled by the uploader
TranscriptNotFoundErrorNo transcript data available for the video
TranscriptLanguageErrorRequested language not available (lists alternatives)
TranscriptPlaylistErrorInvalid batch transcript range (from/to)

Batch transcript

To fetch transcripts for an entire playlist, see the Batch Transcript page.

On this page