> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vyla.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# TV Subtitles

> Fetch all available subtitle tracks for a TV episode.

Returns an array of subtitle tracks for a specific TV episode. Each track is a direct `.vtt` or `.srt` link — no proxy needed. Requires a `standard` or `partner` API key. Subtitle tracks are also bundled automatically in `/tv` responses as part of the `meta` event; use this endpoint when you need them independently.

***

## Path Parameters

<ParamField path="tmdb_id" type="string" required>
  TMDB **series** ID — not episode ID.

  **Example:** `1396` for Breaking Bad
</ParamField>

<ParamField path="season" type="number" required>
  Season number.
</ParamField>

<ParamField path="episode" type="number" required>
  Episode number within the season.
</ParamField>

***

## Request

<CodeGroup>
  ```bash cURL theme={null}
  curl https://1c34-y.hf.space/api/subtitles/tv/1396/1/1 \
    --header "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const subtitles = await fetch(
    'https://1c34-y.hf.space/api/subtitles/tv/1396/1/1',
    { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
  ).then(r => r.json());

  subtitles.forEach(sub => {
    console.log(`${sub.label} (${sub.type}): ${sub.file}`);
  });
  ```

  ```typescript TypeScript theme={null}
  interface Subtitle {
    label: string;
    file: string;
    type: string;
    source: string;
  }

  const subtitles: Subtitle[] = await fetch(
    `https://1c34-y.hf.space/api/subtitles/tv/${seriesId}/${season}/${episode}`,
    { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
  ).then(r => r.json());
  ```

  ```python Python theme={null}
  import requests

  series_id = 1396
  season    = 1
  episode   = 1

  subtitles = requests.get(
    f'https://1c34-y.hf.space/api/subtitles/tv/{series_id}/{season}/{episode}',
    headers={'Authorization': 'Bearer YOUR_API_KEY'}
  ).json()

  for sub in subtitles:
      print(f"{sub['label']} ({sub['type']}): {sub['file']}")
  ```
</CodeGroup>

***

## Response

Returns a JSON array. `404` if no subtitles are found for this episode.

<ResponseField name="[]" type="Subtitle[]">
  Array of subtitle track objects for the episode.

  <Expandable title="Subtitle object">
    <ResponseField name="label" type="string">
      Language name of the track, e.g. `English`, `Spanish`, `Arabic`.
    </ResponseField>

    <ResponseField name="file" type="string">
      Direct URL to the subtitle file.
    </ResponseField>

    <ResponseField name="type" type="string">
      Subtitle format — `vtt` or `srt`.
    </ResponseField>

    <ResponseField name="source" type="string">
      Internal subtitle source identifier.
    </ResponseField>
  </Expandable>
</ResponseField>

***

## Responses

<Tabs>
  <Tab title="200 — Success">
    ```json theme={null}
    [
      {
        "label": "English",
        "file": "https://sub.vdrk.site/v1/tv/1396/1/1/English.vtt",
        "type": "vtt",
        "source": "v1"
      },
      {
        "label": "Spanish",
        "file": "https://sub.vdrk.site/v1/tv/1396/1/1/Spanish.vtt",
        "type": "vtt",
        "source": "v1"
      }
    ]
    ```
  </Tab>

  <Tab title="404 — Not Found">
    ```json theme={null}
    { "error": "no subtitles found" }
    ```
  </Tab>

  <Tab title="401 — Unauthorized">
    ```json theme={null}
    { "error": "Missing API key. Provide via Authorization header or X-API-Key header." }
    ```
  </Tab>

  <Tab title="403 — Forbidden">
    ```json theme={null}
    { "error": "Public keys cannot access subtitle endpoints" }
    ```
  </Tab>

  <Tab title="500 — Server Error">
    ```json theme={null}
    { "error": "<error message>" }
    ```
  </Tab>
</Tabs>

***

## Building an Episode Selector with Subtitles

```typescript theme={null}
const BASE = 'https://1c34-y.hf.space';

async function loadEpisode(
  seriesId: number,
  season: number,
  episode: number,
  videoEl: HTMLVideoElement,
  token: string
) {
  Array.from(videoEl.querySelectorAll('track')).forEach(t => t.remove());

  const res     = await fetch(`${BASE}/tv?id=${seriesId}&season=${season}&episode=${episode}`, {
    headers: { 'X-Session-Token': token }
  });
  const reader  = res.body!.getReader();
  const decoder = new TextDecoder();
  let buffer    = '';
  let started   = false;
  let hls: any;

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    buffer = lines.pop()!;

    for (const line of lines) {
      if (!line.startsWith('data: ')) continue;
      const event = JSON.parse(line.slice(6));

      if (event.type === 'meta') {
        (event.subtitles ?? []).forEach((sub: { label: string; file: string }, i: number) => {
          const track   = document.createElement('track');
          track.kind    = 'subtitles';
          track.label   = sub.label;
          track.src     = sub.file;
          track.default = i === 0;
          videoEl.appendChild(track);
        });
      }

      if (event.type === 'source' && !started) {
        started = true;
        hls?.destroy();
        hls = new Hls();
        hls.loadSource(event.source.url);
        hls.attachMedia(videoEl);
        hls.on(Hls.Events.MANIFEST_PARSED, () => videoEl.play());
      }

      if (event.type === 'done' && !started) {
        throw new Error('No sources available for this episode');
      }
    }
  }
}
```

<Note>
  Subtitle availability varies by episode. Newer or less popular shows may return an empty array. Always handle `subtitles.length === 0` gracefully in your UI.
</Note>
