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

# Tweet subtitles API for X video captions and text

> Get the caption tracks of a public tweet's videos: language, timed cues, and the full text as a transcript. 1 credit when a track comes back, free otherwise.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-tweet-subtitles-200">
      ```json theme={null}
      {
        "subtitles": {
          "tweetId": "2104593672624886205",
          "tracks": [
            {
              "mediaKey": "13_2104593621043314689",
              "language": "en",
              "name": "Planetary Defenders_Caption.en_US.srt",
              "autoGenerated": false,
              "url": "https://video.twimg.com/subtitles/amplify_video/2104593621043314689/0/-FdxBBY2iGcoP5wb.vtt"
            }
          ]
        }
      }
      ```
    </Tab>

    <Tab title="400" id="response-x-tweet-subtitles-400">
      ```json theme={null}
      {
        "error": "invalid_input",
        "message": "Invalid input. Check the request body."
      }
      ```
    </Tab>

    <Tab title="401" id="response-x-tweet-subtitles-401">
      ```json theme={null}
      {
        "error": "unauthenticated",
        "message": "Authentication required."
      }
      ```
    </Tab>

    <Tab title="402" id="response-x-tweet-subtitles-402">
      ```json theme={null}
      {
        "error": "insufficient_credits",
        "message": "Insufficient credits. Top up or subscribe to continue."
      }
      ```
    </Tab>

    <Tab title="404" id="response-x-tweet-subtitles-404">
      ```json theme={null}
      {
        "error": "tweet_not_found",
        "message": "Tweet not found. Check the tweet ID."
      }
      ```
    </Tab>

    <Tab title="424" id="response-x-tweet-subtitles-424">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "X data source temporarily unavailable. Try again later."
      }
      ```
    </Tab>

    <Tab title="429" id="response-x-tweet-subtitles-429">
      ```json theme={null}
      {
        "error": "rate_limit_exceeded",
        "message": "Too many requests. Try again later.",
        "retryAfter": 60
      }
      ```
    </Tab>

    <Tab title="502" id="response-x-tweet-subtitles-502">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "X data source temporarily unavailable. Try again later."
      }
      ```
    </Tab>

    <Tab title="503" id="response-x-tweet-subtitles-503">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "Xquik is busy right now. Retry shortly."
      }
      ```
    </Tab>
  </Tabs>
</Panel>

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

<Callout icon="coins" color="#5c3327">
  **1 credit when a track comes back** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit
</Callout>

Tweet subtitles returns the caption tracks of one public tweet's videos.
The endpoint is `GET /api/v1/x/tweets/{id}/subtitles`.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://xquik.com/api/v1/x/tweets/2104593672624886205/subtitles \
    -H "x-api-key: xq_your_api_key_here" | jq
  ```

  ```javascript Node.js theme={null}
  const tweetId = "2104593672624886205";
  const response = await fetch(`https://xquik.com/api/v1/x/tweets/${tweetId}/subtitles`, {
    headers: { "x-api-key": "xq_your_api_key_here" },
  });
  const { subtitles } = await response.json();
  const transcriptRows = subtitles.tracks.map((track) => ({
    tweet_id: subtitles.tweetId,
    media_key: track.mediaKey ?? null,
    language: track.language ?? null,
    auto_generated: track.autoGenerated,
    cue_count: track.cues.length,
    transcript: track.text,
  }));

  process.stdout.write(`${JSON.stringify(transcriptRows)}\n`);
  ```

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

  tweet_id = "2104593672624886205"
  response = requests.get(
      f"https://xquik.com/api/v1/x/tweets/{tweet_id}/subtitles",
      headers={"x-api-key": "xq_your_api_key_here"},
  )
  subtitles = response.json()["subtitles"]
  transcript_rows = [
      {
          "tweet_id": subtitles["tweetId"],
          "media_key": track.get("mediaKey"),
          "language": track.get("language"),
          "auto_generated": track["autoGenerated"],
          "cue_count": len(track["cues"]),
          "transcript": track["text"],
      }
      for track in subtitles["tracks"]
  ]
  print(json.dumps(transcript_rows))
  ```
</CodeGroup>

Use `GET /x/tweets/{id}/subtitles` when you need what a tweet's video says as text.
Each track holds its language, its timed cues, and the full text as one line.
A call that returns a track costs 1 credit. A tweet without captions costs nothing.

## Build a transcript from the tracks

Read `text` for the whole transcript as one line.
Read `cues` for timed lines, to show beside a player or to cut clips.

A video can hold several tracks, one per language. Pick a track by `language`.
`autoGenerated` is `true` when X transcribed the audio itself.
An uploaded caption file has `autoGenerated: false` and its file name in `name`.

A long video splits its captions into files.
Xquik reads the first 50 files of a track and sets `truncated` to `true` past that.

A tweet with no video, or a video without captions, returns `"tracks": []`.

## Path parameters

<ParamField path="id" type="string" required>
  Post ID or URL-encoded post URL, such as `x.com/nasa/status/20`. See [path IDs](/api-reference/overview#path-ids).
</ParamField>

## Headers

<ParamField header="x-api-key" type="string">
  Full account key. Sessions and OAuth also work.
</ParamField>

<ParamField header="Authorization" type="string">
  `Bearer xq_your_guest_key_here` for `paid_reads`.
</ParamField>

## Response

### 200 OK

<ResponseField name="subtitles" type="object">
  The caption tracks of the tweet's videos.
  **Subtitles object fields.**

  <ResponseField name="tweetId" type="string">
    ID of the tweet.
  </ResponseField>

  <ResponseField name="tracks" type="object[]">
    Caption tracks. Empty when no video has captions.
    **Track object fields.**

    <ResponseField name="mediaKey" type="string">
      Media key of the video, as media rows carry it.
    </ResponseField>

    <ResponseField name="language" type="string">
      Language code of the track, in lowercase.
    </ResponseField>

    <ResponseField name="name" type="string">
      Track name the uploader or X gave it.
    </ResponseField>

    <ResponseField name="autoGenerated" type="boolean">
      `true` when X transcribed the audio itself.
    </ResponseField>

    <ResponseField name="url" type="string">
      Link to the track's first WebVTT file.
    </ResponseField>

    <ResponseField name="text" type="string">
      Every cue's text in order, as one line.
    </ResponseField>

    <ResponseField name="truncated" type="boolean">
      `true` when the track holds more than 50 caption files. The cues then cover the first 50.
    </ResponseField>

    <ResponseField name="cues" type="object[]">
      Timed captions in order. Each cue has `startMs` and `endMs` in milliseconds, and `text` with a
      line break between lines.
    </ResponseField>
  </ResponseField>
</ResponseField>

```json theme={null}
{
  "subtitles": {
    "tweetId": "2104593672624886205",
    "tracks": [
      {
        "mediaKey": "13_2104593621043314689",
        "language": "en",
        "name": "Promo Vid Version 6 - Planetary Defenders - This Is Not an Exercise_Caption.en_US.srt",
        "autoGenerated": false,
        "url": "https://video.twimg.com/subtitles/amplify_video/2104593621043314689/0/-FdxBBY2iGcoP5wb.vtt",
        "text": "This asteroid 2024 YR4 might get interesting. There was a chance that it could impact Earth.",
        "truncated": false,
        "cues": [
          {
            "startMs": 0,
            "endMs": 3370,
            "text": "This asteroid\n2024 YR4 might get interesting."
          },
          {
            "startMs": 3403,
            "endMs": 6406,
            "text": "There was a chance that it could impact\nEarth."
          }
        ]
      }
    ]
  }
}
```

### 400 Invalid tweet ID

```json theme={null}
{ "error": "invalid_tweet_id" }
```

The provided tweet ID is empty or not a valid format.

### 401 Unauthenticated

```json theme={null}
{ "error": "unauthenticated" }
```

Missing or invalid API key. Check the `x-api-key` header value.

### 402 Payment required

Account keys get account options. Guest keys get guest top-up only.
No checkout starts automatically. Confirm any payment action.

### 404 Tweet not found

```json theme={null}
{ "error": "tweet_not_found" }
```

X has no such tweet. The author may have deleted it, or the account is private.

### 502 X API unavailable

```json theme={null}
{ "error": "x_api_unavailable" }
```

The read service returned an error. Retry after a short delay.

### 503 Service busy

```json theme={null}
{ "error": "x_api_unavailable" }
```

X did not serve a caption file in time, or Xquik is busy. Retry after a short delay. The request costs nothing.

### 429 Rate limit exceeded

```json theme={null}
{ "error": "rate_limit_exceeded", "retryAfter": 60 }
```

You exceeded your tier rate limit. Wait for the `Retry-After` header before retrying.

### 424 Dependency failed

```json theme={null}
{ "error": "x_api_unavailable" }
```

The normalized v1 response contract can return 424 when the read service is unavailable.

## Tweet subtitles API questions

### How do I get the transcript of a Twitter video with an API?

Call `GET /x/tweets/{id}/subtitles` with the tweet ID or URL.
Read `subtitles.tracks[0].text` for the transcript.

### Does it work for videos without uploaded captions?

Yes, when X transcribed the audio itself. Those tracks have `autoGenerated: true`.
A video X has no captions for returns no track.

### Which format are the captions in?

Each cue has a start and an end in milliseconds, and its text.
`url` links to the track's first WebVTT file on X's video host.

### How much does a tweet's subtitles call cost?

A call that returns at least 1 track costs 1 credit.
A tweet without captions costs nothing. A refused or failed request costs nothing.

### Does Xquik transcribe the audio?

No. Xquik reads the caption tracks X serves with the video.

### Does this replace X's official API?

No. This page documents Xquik, an independent third-party service.
It does not document X's official API.

<Note>
  **Next steps.** [Get tweet](/api-reference/x/get-tweet) reads the tweet's fields as JSON, or [Download media](/api-reference/x/download-media) saves its photos and videos.
</Note>

<div className="related-api-links">
  <Accordion title="Related tweet, reply & media APIs" icon="link">
    * Tweets: [Get tweet](/api-reference/x/get-tweet) · [Batch tweets](/api-reference/x/batch-tweets) · [Tweet thread](/api-reference/x/tweet-thread) · [Hidden replies](/api-reference/x/tweet-hidden-replies) · [Translate tweet](/api-reference/x/tweet-translation) · [Embed tweet](/api-reference/x/tweet-embed) · [Resolve links](/api-reference/x/resolve-links) · [Tweet subtitles](/api-reference/x/tweet-subtitles) · [X Article](/api-reference/x/get-article)
    * Engagement: [Tweet replies](/api-reference/x/tweet-replies) · [Quote tweets](/api-reference/x/tweet-quotes) · [Likers](/api-reference/x/favoriters) · [Reposters](/api-reference/x/retweeters) · [Check repost](/api-reference/x/tweet-repost-check)
    * Profiles: [User tweets](/api-reference/x/user-tweets) · [Batch user tweets](/api-reference/x/batch-user-tweets) · [User replies](/api-reference/x/user-replies) · [User likes](/api-reference/x/user-likes) · [User media](/api-reference/x/user-media) · [User highlights](/api-reference/x/user-highlights) · [User articles](/api-reference/x/user-articles)
    * Feeds: [List tweets](/api-reference/x/list-tweets) · [Trends](/api-reference/x/trends) · [Trend locations](/api-reference/x/trend-locations) · [Search Spaces](/api-reference/x/search-spaces) · [Get Space](/api-reference/x/get-space) · [Space replay](/api-reference/x/space-replay) · [Get broadcast](/api-reference/x/get-broadcast) · [Hashflags](/api-reference/x/hashflags) · [Search places](/api-reference/x/search-places) · [Download media](/api-reference/x/download-media)
  </Accordion>
</div>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.