# Add subtitles to HLS streams

Include WebVTT caption tracks in adaptive streaming packages

Adaptive streams without captions fail accessibility requirements and lose viewers. "Lectern" (an online education platform like Coursera) needs every lecture to ship with subtitle tracks so students can follow along in noisy environments or different languages.

## API

<CodeGroup labels={["CLI", "TypeScript", "Python", "curl"]}>
```bash
ittybit adaptive \
  -i https://lectern-app.com/courses/bio-201/lecture-4.mov \
  --format hls \
  --subtitle https://lectern-app.com/courses/bio-201/lecture-4.vtt \
  --subtitle-label English
```

```typescript
const task = {
  input: 'https://lectern-app.com/courses/bio-201/lecture-4.mov',
  kind: 'adaptive_video',
  options: {
    format: 'hls',
    subtitles: [
      {
        url: 'https://lectern-app.com/courses/bio-201/lecture-4.vtt',
        label: 'English',
        language: 'en',
        default: true,
      },
    ],
  },
};

const res = await fetch('https://api.ittybit.com/jobs', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(task),
});
const data = await res.json();
```

```python

task = {
    "input": "https://lectern-app.com/courses/bio-201/lecture-4.mov",
    "kind": "adaptive_video",
    "options": {
        "format": "hls",
        "subtitles": [
            {
                "url": "https://lectern-app.com/courses/bio-201/lecture-4.vtt",
                "label": "English",
                "language": "en",
                "default": True,
            },
        ],
    },
}

res = requests.post(
    "https://api.ittybit.com/jobs",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
```

```bash
TASK='{
  "input": "https://lectern-app.com/courses/bio-201/lecture-4.mov",
  "kind": "adaptive_video",
  "options": {
    "format": "hls",
    "subtitles": [
      {
        "url": "https://lectern-app.com/courses/bio-201/lecture-4.vtt",
        "label": "English",
        "language": "en",
        "default": true
      }
    ]
  }
}'

curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"
```

</CodeGroup>

The HLS manifest references the VTT file as a subtitle track. Players that support HLS (Safari, hls.js, Video.js) show a captions toggle automatically.

## CLI

```bash
ittybit adaptive \
  -i lecture-4.mov \
  -o lecture-4.m3u8 \
  --subtitle lecture-4.vtt \
  --subtitle-label English \
  --subtitle-language en
```

## Multiple languages

Add several subtitle tracks to the same stream:

```json
{
  "input": "https://lectern-app.com/courses/bio-201/lecture-4.mov",
  "kind": "adaptive_video",
  "options": {
    "format": "hls",
    "subtitles": [
      {
        "url": "https://lectern-app.com/captions/lecture-4-en.vtt",
        "label": "English",
        "language": "en",
        "default": true
      },
      {
        "url": "https://lectern-app.com/captions/lecture-4-es.vtt",
        "label": "Espa\u00f1ol",
        "language": "es"
      },
      {
        "url": "https://lectern-app.com/captions/lecture-4-fr.vtt",
        "label": "Fran\u00e7ais",
        "language": "fr"
      }
    ]
  }
}
```

Each track appears as a selectable option in the player's caption menu.

## VTT format requirements

| Requirement | Detail                                           |
| ----------- | ------------------------------------------------ |
| Format      | WebVTT (`.vtt`) only                             |
| Encoding    | UTF-8                                            |
| Timestamps  | Must align with the source video timeline        |
| Hosted      | The URL must be publicly accessible at task time |

## When to use subtitle tracks vs burned-in captions

| Approach                     | Use case                                  |
| ---------------------------- | ----------------------------------------- |
| Subtitle tracks (this guide) | Multi-language, togglable, accessible     |
| Burned-in (hardcoded)        | Social media clips, guaranteed visibility |

Subtitle tracks keep the text separate from the video, so viewers can toggle them on or off and switch languages without re-encoding.