# How do I get a YouTube video transcript with an API?

YouTube. Checked October 2026.

Call `GET /v1/youtube/posts/{videoId}/transcript`. It returns the caption lines with start and end times in milliseconds, the language, and whether YouTube generated the captions. It also lists every caption language the video offers. Pass `language`, such as `en` or `es-419`, to pick one. Without it you get the track in the spoken language.

The YouTube Data API only downloads captions for videos you have permission to edit. Its captions.download method needs OAuth and costs 200 quota units a call.

## GET /v1/youtube/posts/{videoId}/transcript

```bash
curl "https://api.openhandle.dev/v1/youtube/posts/synthvid001/transcript?language=en" \
  -H "Authorization: Bearer $OPENHANDLE_API_KEY"
```

```ts
import { OpenHandle } from '@openhandle/sdk';

const openhandle = new OpenHandle({ apiKey: process.env.OPENHANDLE_API_KEY! });
const transcript = await openhandle.youtube.post('synthvid001').transcript.get({ language: 'en' });

const text = transcript.data.segments.map(segment => segment.text).join(' ');
console.log(text);
```

200 OK:

```json
{
  "platform": "youtube",
  "resource": "transcript",
  "capturedAt": "2026-10-26T18:33:17Z",
  "source": "live",
  "data": {
    "postId": "synthvid001",
    "language": "en",
    "languageName": "English",
    "isAutoGenerated": false,
    "segments": [
      { "start": 0, "end": 2400, "text": "Synthetic caption line 1." },
      { "start": 2500, "end": 4900, "text": "Synthetic caption line 2." }
    ],
    "languages": [
      { "language": "en", "name": "English", "isAutoGenerated": false, "isTranslatable": true },
      { "language": "en", "name": "English (auto-generated)", "isAutoGenerated": true, "isTranslatable": true }
    ]
  }
}
```

Trimmed to two caption lines. The video is in the Test dataset, so you can run it on a free Test key.

## Why the YouTube Data API does not work for this

- **Edit permission.** captions.download only works when the signed-in user may edit the video. Otherwise it returns 403 forbidden.
- **Google sign-in.** An API key is not enough. You need a user to sign in with Google.
- **Quota.** Listing caption tracks costs 50 units and downloading one costs 200, out of 10,000 a day.

## What you get

- **Timed lines.** Each segment has `start` and `end` in milliseconds. Divide by 1,000 to link to that second with `&t=`.
- **Every caption language.** `languages` lists the tracks the video offers, both uploaded and auto-generated.

Start from a link? Pass any watch, `youtu.be`, Shorts, or live URL to `POST /v1/urls/fetch`. It returns the video with its `id`.

## When this does not work

A video without captions is not an error. It answers with an empty `segments` list, so check `languages` before you retry.

| Situation | Status | Code |
|---|---|---|
| The video has no captions, or none in that language | 200 | `Empty segments` |
| The video does not exist | 404 | `POST_NOT_FOUND` |
| The ID is not a valid YouTube video ID | 400 | `INVALID_IDENTIFIER` |

```json
{
  "error": {
    "code": "POST_NOT_FOUND",
    "message": "This post was not found.",
    "requestId": "req_01m5…",
    "retryable": false
  }
}
```

Every new workspace starts with 100 free live requests, no card needed.

## Related

- [YouTube API](https://openhandle.dev/apis/youtube): Every YouTube endpoint with examples.
- [YouTube Data API alternative](https://openhandle.dev/compare/youtube-data-api-alternative): Quota, transcripts, and how to move your calls.
- [YouTube docs](https://openhandle.dev/docs/platforms/youtube): Identifiers, filters, and response fields.
