How to get a YouTube transcript in Node.js

Use the Capslane YouTube transcript API from Node.js with native fetch, bounded timeouts and generated job polling.

Request a transcript

Run the call from a trusted server. Browser and mobile bundles must not contain a production API key.

Node.js
const baseUrl = 'https://capslane.com'
const apiKey = process.env.CAPSLANE_API_KEY
const query = new URLSearchParams({
  url: 'dQw4w9WgXcQ',
  mode: 'auto',
})

const response = await fetch(`${baseUrl}/v1/transcript?${query}`, {
  headers: { 'x-api-key': apiKey },
  signal: AbortSignal.timeout(20_000),
})
const result = await response.json()

if (!response.ok) {
  throw new Error(`${result.error}: ${result.message}`)
}

Inspect both the status code and the response body. HTTP 202 is an accepted asynchronous job, not an error.

Poll with a deadline

Generated transcripts can take longer than native extraction. Use a modest interval, an application deadline and the same API key.

Node.js
async function waitForTranscript(jobId) {
  const deadline = Date.now() + 20 * 60_000
  while (Date.now() < deadline) {
    await new Promise((resolve) => setTimeout(resolve, 2_000))
    const response = await fetch(`${baseUrl}/v1/transcript/${jobId}`, {
      headers: { 'x-api-key': apiKey },
      signal: AbortSignal.timeout(20_000),
    })
    const job = await response.json()
    if (job.status === 'completed') return job
    if (job.status === 'failed' || job.status === 'cancelled') throw new Error(job.error)
  }
  throw new Error('Transcript job deadline exceeded')
}

Model both response shapes

A successful immediate response contains content, lang, availableLangs, source, cached and requestId. An accepted response contains jobId, status and requestId.

Keep the integration bounded

Set timeouts on every network call, cap polling, retain the request ID and apply retries only to transient failures. Use native mode when generation must never start.