transcriptfetchGitHubDashboard
SDKs

Node SDK

The official, typed Node.js / TypeScript client. Fetch transcripts, channels, playlists, and search as structured data, ESM + CommonJS, zero runtime dependencies.

Official SDK. Source + issues on GitHub. Your key falls back to the TRANSCRIPTFETCH_API_KEY env var, keep it server-side. One credit per successful caption fetch (AI audio transcription bills per started 5 minutes of audio); failed/blocked/no-transcript requests are free.

Using n8n? There is a community node that wraps this API for n8n workflows, no code required: n8n-nodes-transcriptfetch.

Install

BASH
npm install transcriptfetch

Requires Node 18+ (uses the built-in fetch). Ships types for TypeScript.

Quickstart

Get an API key (100 free credits a month) at the dashboard, then:

TYPESCRIPT
import { TranscriptFetch } from "transcriptfetch";

// apiKey falls back to the TRANSCRIPTFETCH_API_KEY env var
const tf = new TranscriptFetch({ apiKey: "tf_live_..." });

const t = await tf.transcripts.video("https://youtu.be/aircAruvnKk");
console.log(t.title);
for (const seg of t.segments) {
  console.log(`[${seg.start.toFixed(1)}] ${seg.text}`);
}

console.log("credits left:", t.usage?.balance);

By default a transcript comes back as timestamped segments and has no text; the response carries exactly one of the two. To get one plain string instead, turn timestamps off:

TYPESCRIPT
const plain = await tf.transcripts.video("https://youtu.be/aircAruvnKk", { timestamps: false });
console.log(plain.text);

Endpoints

video/channel/playlist accept URLs or raw IDs (normalized automatically).

TYPESCRIPT
await tf.transcripts.video(video, { mode, timestamps, callbackUrl });     // single transcript
await tf.transcripts.channel(channel, { limit, cursor, sinceVideoId });   // a channel's videos (metadata)
await tf.transcripts.playlist(playlist, { limit, cursor });               // a playlist's videos
await tf.transcripts.search(query, { limit, cursor });                    // search YouTube
await tf.transcripts.batch(videoIds);                                     // up to 50 transcripts in one call
await tf.health();                                                        // unauthenticated liveness probe

Long media and jobs

mode: "audio", or any captionless source that is not short, answers 202 with a job instead of a transcript: poll the job (or pass callbackUrl) and read the result from there. Short captionless media usually still comes back inline in the same call. The full contract, including what AI transcription costs, is in the transcription guide.

Pagination

List endpoints are cursor-paginated. Iterate every result with an async generator:

TYPESCRIPT
// Iterate every result without managing cursors
for await (const video of tf.transcripts.iterChannel("@lexfridman", { limit: 10 })) {
  console.log(video.videoId, video.title);
}

// ...or page manually via page.nextCursor and the cursor option.

Errors

All errors subclass TranscriptFetchError. API errors carry .status, .code, .message, and .requestId.

TYPESCRIPT
import {
  InsufficientCreditsError, RateLimitError, APIError,
} from "transcriptfetch";

try {
  await tf.transcripts.video("bad");
} catch (err) {
  if (err instanceof InsufficientCreditsError) {
    // 402: top up at /pricing
  } else if (err instanceof RateLimitError) {
    console.log(err.retryAfter);            // 429
  } else if (err instanceof APIError) {
    console.log(err.status, err.code, err.requestId);
  }
}

Reliability built in. Automatic retries on 429 (honoring Retry-After) and 5xx with exponential backoff; every write auto-sends an Idempotency-Key so a retried request is never double-charged. Configure via new TranscriptFetch({ apiKey, baseUrl, timeout: 30000, maxRetries: 2 }).

Prefer raw HTTP? The API is plain HTTPS/JSON, see the endpoint reference for every route and the response format.

Next →Back to Quickstart
Node SDK · TranscriptFetch docs