> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.videogen.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.videogen.io/_mcp/server.

# Pagination

> How cursor pagination works on VideoGen list endpoints: limit and cursor query params, hasMore and nextCursor response fields, and how to fetch every page.

Every list endpoint in the VideoGen API uses the same cursor-based pagination contract. Responses include the items array you already expect, plus two fields that tell you whether more pages exist and how to fetch them.

## Query parameters

| Parameter | Type      | Default | Description                                                                       |
| --------- | --------- | ------- | --------------------------------------------------------------------------------- |
| `limit`   | `integer` | `50`    | Maximum items per page. Minimum 1, maximum 200.                                   |
| `cursor`  | `string`  | (omit)  | Opaque cursor from a previous response's `nextCursor`. Omit on the first request. |

## Response fields

Every paginated response includes:

| Field        | Type             | Description                                                                        |
| ------------ | ---------------- | ---------------------------------------------------------------------------------- |
| `hasMore`    | `boolean`        | When `true`, another page is available.                                            |
| `nextCursor` | `string \| null` | Pass this value as `cursor` on the next request. `null` when `hasMore` is `false`. |

The items array field name depends on the endpoint (`projects`, `files`, `avatarPresenters`, `ttsVoices`, `endpoints`, and so on).

## Paginated endpoints

| Method | Path                       | Items field      |
| ------ | -------------------------- | ---------------- |
| `GET`  | `/v1/projects`             | `projects`       |
| `GET`  | `/v1/workflows/runs`       | `workflowRuns`   |
| `GET`  | `/v1/tools/executions`     | `toolExecutions` |
| `GET`  | `/v1/files`                | `files`          |
| `GET`  | `/v1/resources/tts-voices` | `ttsVoices`      |
| `GET`  | `/v1/webhooks/endpoints`   | `endpoints`      |

`GET /v1/projects` and `GET /v1/files` return results most recently updated first. `GET /v1/workflows/runs` and `GET /v1/tools/executions` return results most recently created first.

## Fetching every page

**TypeScript:**

```typescript
import { VideoGen } from "@videogen/sdk";

const client = new VideoGen({ apiKey: "sk_videogen_live_..." });

const allFiles = [];
let cursor: string | undefined;

do {
  const page = await client.files.getFiles({ limit: 100, cursor });
  allFiles.push(...page.files);
  cursor = page.hasMore ? page.nextCursor ?? undefined : undefined;
} while (cursor != null);
```

**Python:**

```python
from videogen import VideoGen

client = VideoGen(api_key="sk_videogen_live_...")

all_files = []
cursor = None

while True:
    page = client.files.get_files(limit=100, cursor=cursor)
    all_files.extend(page["files"])
    if not page["hasMore"]:
        break
    cursor = page["nextCursor"]
```

**cURL:**

```bash
# First page
curl "https://api.videogen.io/v1/files?limit=50" \
  -H "Authorization: Bearer sk_videogen_live_..."

# Next page (use nextCursor from the previous response)
curl "https://api.videogen.io/v1/files?limit=50&cursor=CURSOR_FROM_RESPONSE" \
  -H "Authorization: Bearer sk_videogen_live_..."
```

## Rules

* **Cursors are opaque.** Treat them as strings. Do not parse or construct them yourself.
* **Cursors are endpoint-specific.** A cursor from `GET /v1/files` only works on `GET /v1/files`. Using one with a different endpoint returns `400`.
* **Pass cursors unmodified.** Copy `nextCursor` exactly into the `cursor` query param.
* **Cache static catalogues.** Avatar presenters and TTS voices change infrequently. Fetch once and cache for hours rather than listing on every request.