---
title: "List the compositions of a project | GPT-Video API reference"
description: "Lists the compositions of a project, the newest first, a page at a time."
url: "https://www.gpt-video.com/developers/api/list-compositions"
operation: "list_compositions"
method: "GET"
path: "/v1/projects/{projectId}/compositions"
scope: "clips:read"
---

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

# List the compositions of a project

Operation id `list_compositions`. Request `GET /v1/projects/{projectId}/compositions`.

Lists the compositions of a project, the newest first, a page at a time.

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 find the composition of a project of any kind: an Auto Clip project has one for each accepted clip, and a split screen, streamer, Reddit story, fake text or Vibe Editing project has one, which is also its compositionId. Then read it with get_composition (`GET /v1/compositions/{compositionId}`), change it with edit_composition (`POST /v1/compositions/{compositionId}/edit`) or export it with create_export (`POST /v1/compositions/{compositionId}/exports`). Pass the nextCursor of a page to get the next one.

## Example use

A dashboard for a social media manager lists the compositions of a project to show which clips were accepted. A click on one opens it with get_composition (`GET /v1/compositions/{compositionId}`), where the manager can edit or export it.

## Endpoint

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

- 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 |
| --- | --- | --- | --- |
| `projectId` | string | Required | The id of the project, as create_project (`POST /v1/projects`) returns it. Matches `^prj_[0-9a-f]{20}$`. |

### Query

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer | Optional | How many items to return, from 1 to 100. Defaults to 20. From 1 to 100. Default `20`. |
| `cursor` | string | Optional | The nextCursor value from the previous page. Leave it out to get the first page. From 1 to 512 characters. |

## Request

List the compositions of an Auto Clip project.

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

```ts title="TypeScript"
const res = await fetch("https://api.gpt-video.com/v1/projects/prj_7c2e9a4f1b3d4e8096a5/compositions", {
  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/projects/prj_7c2e9a4f1b3d4e8096a5/compositions",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
    },
)
data = r.json()
print(data)
```

```json title="MCP (Full)"
{
  "method": "tools/call",
  "params": {
    "name": "list_compositions",
    "arguments": {
      "projectId": "prj_7c2e9a4f1b3d4e8096a5"
    }
  }
}
```

## Response

A successful call answers HTTP 200.

### Fields to check

| Name | Type | Description |
| --- | --- | --- |
| `data[].id` | string | The composition id. It starts with cmp_. |
| `data[].framing.status` | string |   |
| `data[].framing.jobId` | string or null |   |
| `hasMore` | boolean | true when there are more items after this page. |
| `nextCursor` | string or null | Send it as cursor to get the next page. It is null on the last page. |

### Response example

```json title="HTTP 200"
{
  "data": [
    {
      "id": "cmp_1e710123e428fb030a18",
      "projectId": "prj_7c2e9a4f1b3d4e8096a5",
      "clipId": "cnd_8e77645e55cc8ea02336",
      "kind": "autoclip",
      "title": "Selling the idea first",
      "revision": 0,
      "durationSec": 35,
      "width": 1080,
      "height": 1920,
      "fps": 30,
      "source": {
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "startSec": 900,
        "endSec": 935
      },
      "hook": "Sell it before you build it",
      "captions": {
        "style": "word-pop",
        "wordCount": 87
      },
      "framing": {
        "status": "centered",
        "jobId": null
      },
      "createdAt": "2026-09-01T10:02:00.000Z",
      "updatedAt": "2026-09-01T10:02:00.000Z"
    },
    {
      "id": "cmp_4e8c2a6f09d14b73a1e5",
      "projectId": "prj_7c2e9a4f1b3d4e8096a5",
      "clipId": "cnd_9e04a6c3d12b4f78a5b9",
      "kind": "autoclip",
      "title": "Paid before the product",
      "revision": 0,
      "durationSec": 30,
      "width": 1080,
      "height": 1920,
      "fps": 30,
      "source": {
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "startSec": 120,
        "endSec": 150
      },
      "hook": "They paid us before we had a product",
      "captions": {
        "style": "word-pop",
        "wordCount": 75
      },
      "framing": {
        "status": "centered",
        "jobId": null
      },
      "createdAt": "2026-09-01T10:00:00.000Z",
      "updatedAt": "2026-09-01T10:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

**All response fields**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | array of objects | Required | The items of this page. |
| `data[]` | Composition object | Optional | Described in [Composition object](/developers/objects.md#composition). |
| `hasMore` | boolean | Required | true when there are more items after this page. |
| `nextCursor` | string or null | Required | Send it as cursor to get the next page. It is null on the last page. |

**More examples**

#### Find the composition of a split screen project

`GET /v1/projects/prj_9cf6ba9cd17fd89cf513/compositions`

```json title="HTTP 200"
{
  "data": [
    {
      "id": "cmp_cc26cb47fd6004aa5fba",
      "projectId": "prj_9cf6ba9cd17fd89cf513",
      "clipId": null,
      "kind": "splitscreen",
      "title": "Founder interview, split",
      "revision": 0,
      "durationSec": 1800,
      "width": 1080,
      "height": 1920,
      "fps": 30,
      "source": null,
      "hook": null,
      "captions": {
        "style": "bold-karaoke",
        "wordCount": 380
      },
      "framing": {
        "status": "none",
        "jobId": null
      },
      "createdAt": "2026-08-31T21:46:00.000Z",
      "updatedAt": "2026-08-31T21:46:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

#### A project that does not exist

`GET /v1/projects/prj_9f0d3b7e1c5a4d28b6f3/compositions`

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

## Errors

| Code | Status | When | What to do |
| --- | --- | --- | --- |
| [`NOT_FOUND`](/developers/errors.md#NOT_FOUND) | 404 | A project that does not exist | Change the request, then send it 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
- [`edit_composition`](/developers/api/edit-composition.md) (`POST /v1/compositions/{compositionId}/edit`) Edit a composition
- [`create_export`](/developers/api/create-export.md) (`POST /v1/compositions/{compositionId}/exports`) Export a composition
- [`get_project`](/developers/api/get-project.md) (`GET /v1/projects/{projectId}`) Get a project
- [`delete_composition`](/developers/api/delete-composition.md) (`DELETE /v1/compositions/{compositionId}`) Delete a composition

## Questions

### Why does my split screen project show one composition only?

A split screen, streamer, Reddit story, fake text or Vibe Editing project has one composition, made by its build or its preparation. Only an Auto Clip project has one for each accepted clip.

### Why is the list of a new project empty?

A composition exists once a clip is accepted, or once the project is built or prepared. Until then the project has none.
