---
title: "List projects | GPT-Video API reference"
description: "Returns the projects of the account, the most recently created first, one page at a time."
url: "https://www.gpt-video.com/developers/api/list-projects"
operation: "list_projects"
method: "GET"
path: "/v1/projects"
scope: "clips:read"
---

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

# List projects

Operation id `list_projects`. Request `GET /v1/projects`.

Returns the projects of the account, the most recently created first, one 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 a project you created before, or to see what is already in the account. Each project carries its status, its source video and its settings, so you can tell which ones are ready and which still need settings. Projects made in the GPT-Video app are listed too. To read one project with its running analysis, call get_project (`GET /v1/projects/{projectId}`).

## Example use

A dashboard for an agency lists the projects of the account when it opens, marks the ones that are ready, and lets a person pick up a project that was started in the GPT-Video app.

## Endpoint

```text
GET https://api.gpt-video.com/v1/projects
```

- 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

### 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 projects.

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

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

## Response

A successful call answers HTTP 200.

### Fields to check

| Name | Type | Description |
| --- | --- | --- |
| `data[].id` | string | The project id. It starts with prj_. |
| `data[].status` | string | draft has not been analyzed yet, processing is being analyzed, ready has clips, failed means the last analysis failed and errorMessage says why. |
| `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": "prj_1d8b6e0c47a24f93b1c2",
      "kind": "autoclip",
      "title": "Weekly podcast",
      "status": "draft",
      "sourceAssetId": null,
      "compositionId": null,
      "settings": {
        "language": null,
        "clipCount": 5,
        "minDurationSec": null,
        "maxDurationSec": null,
        "objective": "educational",
        "captionStyle": null,
        "reframe": null,
        "instructions": null,
        "vocabulary": null
      },
      "missingSettings": [
        "language",
        "minDurationSec",
        "maxDurationSec",
        "captionStyle",
        "reframe"
      ],
      "splitScreen": null,
      "streamer": null,
      "redditStory": null,
      "fakeText": null,
      "vibeEdit": null,
      "errorMessage": null,
      "createdAt": "2026-09-01T09:50:00.000Z",
      "updatedAt": "2026-09-01T09:50:00.000Z"
    },
    {
      "id": "prj_7c2e9a4f1b3d4e8096a5",
      "kind": "autoclip",
      "title": "Founder interview",
      "status": "ready",
      "sourceAssetId": "ast_3f9a1c7e5b2d4086a1c4",
      "compositionId": null,
      "settings": {
        "language": "en",
        "clipCount": 3,
        "minDurationSec": 15,
        "maxDurationSec": 60,
        "objective": "viral",
        "captionStyle": "word-pop",
        "reframe": "face",
        "instructions": null,
        "vocabulary": null
      },
      "missingSettings": [],
      "splitScreen": null,
      "streamer": null,
      "redditStory": null,
      "fakeText": null,
      "vibeEdit": null,
      "errorMessage": null,
      "createdAt": "2026-09-01T09:10:00.000Z",
      "updatedAt": "2026-09-01T09:40:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

**All response fields**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | array of objects | Required | The items of this page. |
| `data[]` | Project object | Optional | Described in [Project object](/developers/objects.md#project). |
| `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**

#### Ask for one project per page

`GET /v1/projects?limit=1`

```json title="HTTP 200"
{
  "data": [
    {
      "id": "prj_1d8b6e0c47a24f93b1c2",
      "kind": "autoclip",
      "title": "Weekly podcast",
      "status": "draft",
      "sourceAssetId": null,
      "compositionId": null,
      "settings": {
        "language": null,
        "clipCount": 5,
        "minDurationSec": null,
        "maxDurationSec": null,
        "objective": "educational",
        "captionStyle": null,
        "reframe": null,
        "instructions": null,
        "vocabulary": null
      },
      "missingSettings": [
        "language",
        "minDurationSec",
        "maxDurationSec",
        "captionStyle",
        "reframe"
      ],
      "splitScreen": null,
      "streamer": null,
      "redditStory": null,
      "fakeText": null,
      "vibeEdit": null,
      "errorMessage": null,
      "createdAt": "2026-09-01T09:50:00.000Z",
      "updatedAt": "2026-09-01T09:50:00.000Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "eyJrIjp7ImNyZWF0ZWRBdCI6IjIwMjYtMDktMDFUMDk6NTA6MDAuMDAwWiIsImlkIjoicHJqXzFkOGI2ZTBjNDdhMjRmOTNiMWMyIn0sInYiOjF9"
}
```

#### Read the next page

`GET /v1/projects?limit=1&cursor=eyJrIjp7ImNyZWF0ZWRBdCI6IjIwMjYtMDktMDFUMDk6NTA6MDAuMDAwWiIsImlkIjoicHJqXzFkOGI2ZTBjNDdhMjRmOTNiMWMyIn0sInYiOjF9`

```json title="HTTP 200"
{
  "data": [
    {
      "id": "prj_7c2e9a4f1b3d4e8096a5",
      "kind": "autoclip",
      "title": "Founder interview",
      "status": "ready",
      "sourceAssetId": "ast_3f9a1c7e5b2d4086a1c4",
      "compositionId": null,
      "settings": {
        "language": "en",
        "clipCount": 3,
        "minDurationSec": 15,
        "maxDurationSec": 60,
        "objective": "viral",
        "captionStyle": "word-pop",
        "reframe": "face",
        "instructions": null,
        "vocabulary": null
      },
      "missingSettings": [],
      "splitScreen": null,
      "streamer": null,
      "redditStory": null,
      "fakeText": null,
      "vibeEdit": null,
      "errorMessage": null,
      "createdAt": "2026-09-01T09:10:00.000Z",
      "updatedAt": "2026-09-01T09:40:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

#### A cursor written by hand

`GET /v1/projects?cursor=page-2`

```json title="HTTP 400"
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "The cursor is not valid. Send the nextCursor of the previous page unchanged, or leave it out.",
    "hint": "Fix the request as the message says, then send it again.",
    "requestId": "req_05e9c4a50265af20e2cd064b"
  }
}
```

## Errors

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_project`](/developers/api/get-project.md) (`GET /v1/projects/{projectId}`) Get a project
- [`create_project`](/developers/api/create-project.md) (`POST /v1/projects`) Create a project

## Questions

### Why do I see projects I did not create through the API?

The API works on the same account as the GPT-Video app, so projects made in the app are listed too. Each kind of project is changed through its own operations, and kind says which kind it is.

### Can a project appear twice while I page through the list?

No. The cursor marks the last project of the page, so a project created while you read starts a new first page and never shifts the ones after the cursor.
