---
title: "Get the captions of a composition | GPT-Video API reference"
description: "Returns the captions of a composition as an SRT or WebVTT file, inside a JSON answer."
url: "https://www.gpt-video.com/developers/api/get-composition-captions"
operation: "get_composition_captions"
method: "GET"
path: "/v1/compositions/{compositionId}/captions"
scope: "clips:read"
---

Do not assume endpoints have parameters other than the ones explicitly specified.

# Get the captions of a composition

Operation id `get_composition_captions`. Request `GET /v1/compositions/{compositionId}/captions`.

Returns the captions of a composition as an SRT or WebVTT file, inside a JSON answer.

Beta. This endpoint is supported and can gain new fields. If a change would break your code, we tell you before it takes effect and list it in the changelog.

## About

Use it to post the captions as a separate file next to the exported video. The text follows the composition as it is now, and an export made earlier can differ: compare compositionRevision with the revision of a finished export (status done) to know they match. Lines break where they break on screen, and words keep the case they were typed in. A composition with no words to caption answers CONFLICT. Works on compositions of every kind of project.

## Example use

A channel that also posts its clips to YouTube downloads the SRT file of each composition and uploads it next to the video, so viewers can turn the captions on and off.

## Endpoint

```text
GET https://api.gpt-video.com/v1/compositions/{compositionId}/captions
```

- Scope `clips:read`.
- Rate limit 120 requests a minute for each key and 240 for each account, shared by every `read` call. See [rate limits](/developers/rate-limits.md).
- No `Idempotency-Key`.

## Parameters

### Path

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `compositionId` | string | Required | The id of the composition: accept_clip (`POST /v1/clips/{clipId}/accept`) returns it for a clip, and list_compositions (`GET /v1/projects/{projectId}/compositions`) or the project's compositionId give it for the other kinds of project. Matches `^cmp_[0-9a-f]{20}$`. |

### Query

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `format` | string | Optional | srt for SubRip, vtt for WebVTT. Defaults to srt. One of `srt`, `vtt`. Default `srt`. |

## Request

Get the captions as SRT.

```bash title="curl"
curl "https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY"
```

```ts title="TypeScript"
const res = await fetch("https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
  },
});
const data = await res.json();
console.log(data);
```

```python title="Python"
import os

import requests

r = requests.get(
    "https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
    },
)
data = r.json()
print(data)
```

```json title="MCP (Full)"
{
  "method": "tools/call",
  "params": {
    "name": "get_composition_captions",
    "arguments": {
      "compositionId": "cmp_4e8c2a6f09d14b73a1e5"
    }
  }
}
```

## Response

A successful call answers HTTP 200.

### Response example

```json title="HTTP 200"
{
  "captions": {
    "format": "srt",
    "mimeType": "text/plain; charset=utf-8",
    "compositionRevision": 0,
    "text": "1\n00:00:00,000 --> 00:00:01,600\nbefore we had a\n\n2\n00:00:01,600 --> 00:00:02,800\nproduct and that\n\n3\n00:00:02,800 --> 00:00:04,000\nchanged everything for\n\n4\n00:00:04,000 --> 00:00:05,600\nour team the first\n\n5\n00:00:05,600 --> 00:00:06,800\ncustomer paid us\n\n6\n00:00:06,800 --> 00:00:08,400\nbefore we had a\n\n7\n00:00:08,400 --> 00:00:09,600\nproduct and that\n\n8\n00:00:09,600 --> 00:00:10,800\nchanged everything for\n\n9\n00:00:10,800 --> 00:00:12,400\nour team the first\n\n10\n00:00:12,400 --> 00:00:13,600\ncustomer paid us\n\n11\n00:00:13,600 --> 00:00:15,200\nbefore we had a\n\n12\n00:00:15,200 --> 00:00:16,400\nproduct and that\n\n13\n00:00:16,400 --> 00:00:17,600\nchanged everything for\n\n14\n00:00:17,600 --> 00:00:19,200\nour team the first\n\n15\n00:00:19,200 --> 00:00:20,400\ncustomer paid us\n\n16\n00:00:20,400 --> 00:00:22,000\nbefore we had a\n\n17\n00:00:22,000 --> 00:00:23,200\nproduct and that\n\n18\n00:00:23,200 --> 00:00:24,400\nchanged everything for\n\n19\n00:00:24,400 --> 00:00:26,000\nour team the first\n\n20\n00:00:26,000 --> 00:00:27,200\ncustomer paid us\n\n21\n00:00:27,200 --> 00:00:28,800\nbefore we had a\n\n22\n00:00:28,800 --> 00:00:30,000\nproduct and that\n"
  }
}
```

**All response fields**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `captions` | object | Required |   |
| `captions.format` | string | Required | The format of text. One of `srt`, `vtt`. |
| `captions.mimeType` | string | Required | The media type to save text with, character set included. |
| `captions.compositionRevision` | integer | Required | The revision of the composition the text was written from. |
| `captions.text` | string | Required | The whole subtitle file, ready to save. |

**More examples**

#### Get the captions as WebVTT

`GET /v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions?format=vtt`

```json title="HTTP 200"
{
  "captions": {
    "format": "vtt",
    "mimeType": "text/vtt; charset=utf-8",
    "compositionRevision": 0,
    "text": "WEBVTT\n\n00:00:00.000 --> 00:00:01.600\nbefore we had a\n\n00:00:01.600 --> 00:00:02.800\nproduct and that\n\n00:00:02.800 --> 00:00:04.000\nchanged everything for\n\n00:00:04.000 --> 00:00:05.600\nour team the first\n\n00:00:05.600 --> 00:00:06.800\ncustomer paid us\n\n00:00:06.800 --> 00:00:08.400\nbefore we had a\n\n00:00:08.400 --> 00:00:09.600\nproduct and that\n\n00:00:09.600 --> 00:00:10.800\nchanged everything for\n\n00:00:10.800 --> 00:00:12.400\nour team the first\n\n00:00:12.400 --> 00:00:13.600\ncustomer paid us\n\n00:00:13.600 --> 00:00:15.200\nbefore we had a\n\n00:00:15.200 --> 00:00:16.400\nproduct and that\n\n00:00:16.400 --> 00:00:17.600\nchanged everything for\n\n00:00:17.600 --> 00:00:19.200\nour team the first\n\n00:00:19.200 --> 00:00:20.400\ncustomer paid us\n\n00:00:20.400 --> 00:00:22.000\nbefore we had a\n\n00:00:22.000 --> 00:00:23.200\nproduct and that\n\n00:00:23.200 --> 00:00:24.400\nchanged everything for\n\n00:00:24.400 --> 00:00:26.000\nour team the first\n\n00:00:26.000 --> 00:00:27.200\ncustomer paid us\n\n00:00:27.200 --> 00:00:28.800\nbefore we had a\n\n00:00:28.800 --> 00:00:30.000\nproduct and that\n"
  }
}
```

#### A composition with no words to caption

`GET /v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions`

```json title="HTTP 409"
{
  "error": {
    "code": "CONFLICT",
    "message": "This edit has no subtitles to export.",
    "requestId": "req_3e7a1c9d5b0f42e6a4c8d1b3"
  }
}
```

#### Captions of a composition that does not exist

`GET /v1/compositions/cmp_7a1f5d3c9e0b42d8f6a2/captions`

```json title="HTTP 404"
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Composition cmp_7a1f5d3c9e0b42d8f6a2 not found",
    "hint": "Check the id. An id that belongs to another account is not found either.",
    "requestId": "req_6b2d9f0e4a7c41b3d8e5a2f9"
  }
}
```

## Errors

| Code | Status | When | What to do |
| --- | --- | --- | --- |
| [`NOT_FOUND`](/developers/errors.md#NOT_FOUND) | 404 | Captions of a composition that does not exist | Change the request, then send it again. |
| [`CONFLICT`](/developers/errors.md#CONFLICT) | 409 | A composition with no words to caption | Read the resource again, then decide. Do not send the same request again. |

Every call can also answer these errors. [`VALIDATION_FAILED`](/developers/errors.md#VALIDATION_FAILED), [`UNKNOWN_PARAMETER`](/developers/errors.md#UNKNOWN_PARAMETER), [`UNAUTHORIZED`](/developers/errors.md#UNAUTHORIZED), [`SCOPE_MISSING`](/developers/errors.md#SCOPE_MISSING), [`RATE_LIMITED`](/developers/errors.md#RATE_LIMITED), [`INTERNAL`](/developers/errors.md#INTERNAL), [`UNAVAILABLE`](/developers/errors.md#UNAVAILABLE).

## Cost

No credits are used.

## Related operations

- [`get_composition`](/developers/api/get-composition.md) (`GET /v1/compositions/{compositionId}`) Get a composition
- [`create_export`](/developers/api/create-export.md) (`POST /v1/compositions/{compositionId}/exports`) Export a composition
- [`get_export`](/developers/api/get-export.md) (`GET /v1/exports/{exportId}`) Get an export
- [`replace_caption_words`](/developers/api/replace-caption-words.md) (`POST /v1/compositions/{compositionId}/captions/replace`) Correct words in the captions

## Questions

### Do the captions match a video I exported before?

Only if the composition has not changed since. The text follows the composition as it is now; compare compositionRevision with the revision of a finished export (status done).

### Why is the text not in capitals when the video shows the captions in capitals?

The file keeps the words as they were typed. Capitals on screen are a style of the video, and a subtitle file that keeps the case works better for uploads, screen readers and translation.
