# List projects

`GET https://api-vod-express.syncwords.com/v1/projects`

List projects

Your account’s projects, newest first, a page at a time. Follow `links.next` until it is null. To find one project before creating it, filter by its exact `name`.

**Guide:** [Projects](/vod-express/concepts/projects)

## Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `page` | query | integer | no | The page, from 1; below 1 is a 422. |
| `perPage` | query | integer | no | Projects a page. |
| `name` | query | string | no | Only the projects named exactly this, case included. Use it to find a project before creating it again. |
| `search` | query | string | no | Only the projects whose name contains this, ignoring case. |
| `status` | query | enum: running, stopped, error, completed, failed | no | Only the projects in this state: `running` (its processing service is up), `stopped`, or `error` (its last start failed, or its service stopped working); or by what their files reached, as `files` last counted them: `completed` — stopped, nothing left to do (every file `done` or `failed`) and at least one `done`; `failed` — in `error`, or stopped with nothing left to do and no file `done`. |

## Responses

### 200

OK

Example:

```json
{
  "data": [
    {
      "id": "01m3k8jnrdw509457nhz7w81bq",
      "name": "Fishing Channel S01",
      "description": null,
      "buildVersion": "0.9.0-alpha1-2026-10-20T14-10Z-3f05aa",
      "status": "running",
      "files": {
        "total": 3,
        "pending": 1,
        "processing": 0,
        "done": 1,
        "failed": 1,
        "countedAt": "2026-09-28T06:15:00.000Z"
      },
      "startOnNewFiles": true,
      "config": {
        "sourceMedia": {
          "@type": "S3FolderSource"
        },
        "transcription": {
          "@type": "Speechmatics",
          "language": "en"
        },
        "translations": [
          {
            "language": "es",
            "engine": {
              "@type": "Gemini"
            },
            "ttsOptions": {
              "provider": {
                "@type": "Deepdub",
                "voice": "auto"
              }
            }
          }
        ],
        "outputs": [
          {
            "@type": "S3FolderOutput",
            "items": [
              {
                "@type": "VideoFile",
                "ext": "mp4",
                "nameTemplate": "${name}.es.${ext}",
                "useOriginalTracks": true,
                "dubTracks": [
                  {
                    "language": "es",
                    "title": "Spanish"
                  }
                ]
              }
            ]
          }
        ]
      },
      "storage": {
        "connection": null,
        "path": "s3://sw-vod-express/projects/141/01m3k8jnrdw509457nhz7w81bq/",
        "inputPath": "s3://sw-vod-express/projects/141/01m3k8jnrdw509457nhz7w81bq/input/",
        "outputPath": "s3://sw-vod-express/projects/141/01m3k8jnrdw509457nhz7w81bq/output/",
        "inputPrefix": null,
        "outputPrefix": null
      },
      "meta": {
        "createdAt": "2026-09-28T05:42:35.534Z",
        "updatedAt": "2026-09-28T05:42:35.534Z",
        "createdBy": {
          "accountId": 141,
          "accountName": "Acme Media",
          "userId": null,
          "userEmail": null,
          "userFullName": null
        },
        "configLocked": false,
        "messages": []
      }
    }
  ],
  "links": {
    "first": "https://api-vod-express.syncwords.com/v1/projects?status=running&page=1",
    "last": "https://api-vod-express.syncwords.com/v1/projects?status=running&page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "links": [
      {
        "url": null,
        "label": "&laquo; Previous",
        "active": false
      },
      {
        "url": "https://api-vod-express.syncwords.com/v1/projects?status=running&page=1",
        "label": "1",
        "active": true
      },
      {
        "url": null,
        "label": "Next &raquo;",
        "active": false
      }
    ],
    "path": "https://api-vod-express.syncwords.com/v1/projects",
    "per_page": 25,
    "to": 1,
    "total": 1
  }
}
```

Schema:

```json
{
  "$ref": "#/components/schemas/ProjectPage"
}
```

### 401

No token, or an expired or unknown one: `unauthenticated`.

Example:

```json
{
  "message": "Unauthenticated.",
  "code": "unauthenticated"
}
```

Schema:

```json
{
  "$ref": "#/components/schemas/Error"
}
```

### 403

VOD Express is not available to the account: switched off for it (`vod_express_not_enabled`), or the account is restricted — its access revoked, its trial expired or used up (`account_restricted`, with the reason in `message`).

Example:

```json
{
  "message": [
    "VOD Express is not enabled for your account."
  ],
  "code": "vod_express_not_enabled"
}
```

Schema:

```json
{
  "$ref": "#/components/schemas/Error"
}
```

### 422

A bad filter: `validation_failed`.

Example:

```json
{
  "message": "The selected status is invalid.",
  "errors": {
    "status": [
      "The selected status is invalid."
    ]
  },
  "code": "validation_failed"
}
```

Schema:

```json
{
  "$ref": "#/components/schemas/Error"
}
```

### 429

Rate limited: `too_many_requests`. Wait the seconds `Retry-After` says, then repeat the call.

Example:

```json
{
  "message": [
    "Too Many Attempts."
  ],
  "code": "too_many_requests"
}
```

Schema:

```json
{
  "$ref": "#/components/schemas/Error"
}
```

## Schemas

### ProjectPage

`#/components/schemas/ProjectPage`

```json
{
  "type": "object",
  "required": [
    "data",
    "links",
    "meta"
  ],
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Project"
      }
    },
    "links": {
      "type": "object",
      "description": "Absolute URLs that keep the request’s filters and `perPage`.",
      "required": [
        "first",
        "last",
        "prev",
        "next"
      ],
      "properties": {
        "first": {
          "type": "string"
        },
        "last": {
          "type": "string"
        },
        "prev": {
          "type": [
            "string",
            "null"
          ]
        },
        "next": {
          "type": [
            "string",
            "null"
          ],
          "description": "Null on the last page."
        }
      }
    },
    "meta": {
      "type": "object",
      "description": "snake_case, the one exception to the API’s camelCase.",
      "required": [
        "current_page",
        "from",
        "last_page",
        "links",
        "path",
        "per_page",
        "to",
        "total"
      ],
      "properties": {
        "current_page": {
          "type": "integer"
        },
        "from": {
          "type": [
            "integer",
            "null"
          ]
        },
        "last_page": {
          "type": "integer"
        },
        "links": {
          "type": "array",
          "description": "A pagination bar: previous, the page numbers (`...` for a gap, with a null `url`), next. Labels are HTML (`&laquo; Previous`).",
          "items": {
            "type": "object",
            "required": [
              "url",
              "label",
              "active"
            ],
            "properties": {
              "url": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "label": {
                "type": "string"
              },
              "active": {
                "type": "boolean"
              }
            }
          }
        },
        "path": {
          "type": "string",
          "description": "The listing’s URL, without a query."
        },
        "per_page": {
          "type": "integer"
        },
        "to": {
          "type": [
            "integer",
            "null"
          ]
        },
        "total": {
          "type": "integer"
        }
      }
    }
  }
}
```

### Project

`#/components/schemas/Project`

```json
{
  "type": "object",
  "required": [
    "id",
    "name",
    "description",
    "buildVersion",
    "status",
    "files",
    "startOnNewFiles",
    "config",
    "storage",
    "meta"
  ],
  "properties": {
    "id": {
      "type": "string",
      "description": "A lowercase ULID; ids sort by creation time."
    },
    "name": {
      "type": "string"
    },
    "description": {
      "type": [
        "string",
        "null"
      ]
    },
    "buildVersion": {
      "type": "string",
      "description": "The build the project runs on, kept until you change it."
    },
    "status": {
      "enum": [
        "running",
        "stopped",
        "error"
      ],
      "description": "Whether the processing service is up. `error`: the last start failed, or the service stopped working (`pod` in `GET …/status`); `POST …/start` retries it."
    },
    "files": {
      "type": [
        "object",
        "null"
      ],
      "required": [
        "total",
        "pending",
        "processing",
        "done",
        "failed",
        "countedAt"
      ],
      "description": "How many input files are in each state, as last counted: the counts of `GET …/files`’ `meta`, kept on the project so a list can show every project’s progress. A project with a service, or changed in the last day, is counted once a minute; every change made through this API (an upload completed, a file deleted or retried, a files listing) counts again at once. A file copied in with the AWS CLI shows at the next minute. `null` until the first count. A project is finished when `pending` and `processing` are both 0.",
      "properties": {
        "total": {
          "type": "integer"
        },
        "pending": {
          "type": "integer"
        },
        "processing": {
          "type": "integer"
        },
        "done": {
          "type": "integer"
        },
        "failed": {
          "type": "integer"
        },
        "countedAt": {
          "type": "string",
          "format": "date-time",
          "description": "When they were counted."
        }
      }
    },
    "startOnNewFiles": {
      "type": "boolean",
      "description": "When the project is `stopped` and a new file lands in its input folder, start it: at once when an API upload completes, and within a minute when the project stopped itself after its last file and the file came another way (the AWS CLI, an SDK, a one-`PUT` upload). False: new files wait for `POST …/start`. Retrying a file starts the project either way. See Auto-stop."
    },
    "config": {
      "$ref": "#/components/schemas/Config",
      "description": "The processing instructions, validated against the schema of the project’s build. Shown here: build `0.9.0-alpha1-2026-10-20T14-10Z-3f05aa`; `GET /schema` has any build’s."
    },
    "storage": {
      "type": "object",
      "required": [
        "connection",
        "path",
        "inputPath",
        "outputPath",
        "inputPrefix",
        "outputPrefix"
      ],
      "description": "Where the project’s files are: SyncWords’ storage (`connection` null; the folder the upload role reaches), or your own bucket through a storage connection, with the project’s two folders in it.",
      "properties": {
        "connection": {
          "type": [
            "object",
            "null"
          ],
          "required": [
            "id",
            "name",
            "bucket",
            "endpoint"
          ],
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "bucket": {
              "type": "string"
            },
            "endpoint": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        },
        "path": {
          "type": "string",
          "description": "The project’s folder, or the folder of your bucket both others share."
        },
        "inputPath": {
          "type": "string",
          "description": "Upload here."
        },
        "outputPath": {
          "type": "string",
          "description": "Outputs appear here."
        },
        "inputPrefix": {
          "type": [
            "string",
            "null"
          ],
          "description": "The input folder in your bucket, as `storage` takes it; null for SyncWords’ storage."
        },
        "outputPrefix": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "meta": {
      "type": "object",
      "required": [
        "createdAt",
        "updatedAt",
        "createdBy",
        "configLocked",
        "messages"
      ],
      "properties": {
        "createdAt": {
          "type": "string",
          "format": "date-time"
        },
        "updatedAt": {
          "type": "string",
          "format": "date-time"
        },
        "createdBy": {
          "type": "object",
          "required": [
            "accountId",
            "accountName",
            "userId",
            "userEmail",
            "userFullName"
          ],
          "description": "The owning account, and the user who created the project; null fields for an organization token, or a deleted account or user.",
          "properties": {
            "accountId": {
              "type": "integer"
            },
            "accountName": {
              "type": [
                "string",
                "null"
              ]
            },
            "userId": {
              "type": [
                "integer",
                "null"
              ]
            },
            "userEmail": {
              "type": [
                "string",
                "null"
              ]
            },
            "userFullName": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        },
        "configLocked": {
          "type": "boolean",
          "description": "True while SyncWords support manages the project’s configuration for you: `config` and `buildVersion` cannot be changed (`409 project_config_locked`) until support hands it back. `name` and `description` can be changed as usual."
        },
        "messages": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Message"
          },
          "description": "What the config will do that you probably do not mean, such as a dub no output carries. Empty when there is nothing to say."
        }
      }
    }
  }
}
```

### Message

`#/components/schemas/Message`

```json
{
  "type": "object",
  "required": [
    "severity",
    "code",
    "path",
    "message"
  ],
  "description": "Something worth changing about a project that was accepted: nothing is refused for it. Worked out from the stored config on every answer, so it shows on a read too, and goes away once the config is fixed.",
  "properties": {
    "severity": {
      "type": "string",
      "enum": [
        "info",
        "warning",
        "error"
      ],
      "description": "How much it matters. `warning`: it costs money or loses output. Treat a value you do not know as `info`."
    },
    "code": {
      "type": "string",
      "description": "For programs: stable, one per situation. `unused_translation`: no output carries the translation, which is still produced and billed (and its dub, `params.dubbed`). `unused_dub`: the translation’s subtitles are in an output, its dub in none, and the dub is still produced and billed. `dictionary_not_applied`: a dictionary, postprocessing dictionary or glossary the config names is no longer in the account or in the project’s languages, so the project processes without it (`params.field`, `params.reference`, `params.reason`: `not_found` or `language`). More codes may be added.",
      "examples": [
        "unused_dub",
        "unused_translation",
        "dictionary_not_applied"
      ]
    },
    "path": {
      "type": [
        "string",
        "null"
      ],
      "description": "What it is about: a JSON pointer, the same as the keys of a 422’s `errors`. Null when it is about the whole project.",
      "examples": [
        "/config/translations/0/ttsOptions"
      ]
    },
    "message": {
      "type": "string",
      "description": "For people: what is wrong, what it costs, and what to change."
    },
    "params": {
      "type": "object",
      "description": "The values the message names, for a client that words it itself: `language`, and `dubbed` on `unused_translation`; `field`, `reference` and `reason` on `dictionary_not_applied`.",
      "additionalProperties": {
        "type": [
          "string",
          "number",
          "boolean",
          "null"
        ]
      }
    }
  }
}
```

### Error

`#/components/schemas/Error`

```json
{
  "type": "object",
  "required": [
    "message",
    "code"
  ],
  "properties": {
    "message": {
      "description": "For people. A string, or an array of one string on some statuses: read it as `Array.isArray(m) ? m[0] : m`.",
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    },
    "code": {
      "description": "For programs: stable, one per situation. See the Errors guide.",
      "enum": [
        "bad_request",
        "invalid_output_path",
        "invalid_idempotency_key",
        "unauthenticated",
        "invalid_credentials",
        "forbidden",
        "vod_express_not_enabled",
        "account_restricted",
        "not_found",
        "project_not_found",
        "file_not_found",
        "file_meta_not_found",
        "output_not_found",
        "feed_not_found",
        "upload_not_found",
        "webhook_not_found",
        "webhook_delivery_not_found",
        "aws_access_not_provisioned",
        "storage_connection_not_found",
        "method_not_allowed",
        "conflict",
        "file_exists",
        "project_reads_feed",
        "project_reads_folder",
        "file_processing",
        "project_config_locked",
        "idempotency_in_progress",
        "storage_connection_in_use",
        "payload_too_large",
        "unsupported_media_type",
        "validation_failed",
        "service_start_failed",
        "idempotency_key_reused",
        "webhook_limit_reached",
        "storage_failed",
        "storage_connection_check_failed",
        "storage_connection_folder_only",
        "storage_prefix_taken",
        "build_without_storage_connections",
        "build_has_no_schema",
        "too_many_requests",
        "internal_error",
        "bad_gateway",
        "service_stop_failed",
        "aws_access_failed",
        "platform_unavailable",
        "service_unavailable",
        "build_list_unavailable"
      ]
    },
    "errors": {
      "type": "object",
      "description": "On a 422: each field’s messages, keyed by field name, or by JSON pointer inside `config`.",
      "additionalProperties": {
        "type": "array",
        "items": {
          "type": "string"
        }
      }
    }
  }
}
```

### Config

A project’s `config`: every field is in the [Config reference](/vod-express/configuration/reference.md), and `GET /schema` answers it as JSON Schema.
