---
title: Pagination
url: https://lumean.app/de/developers/docs/guide/pagination
locale: de
description: "Regular lists count pages from one, external voice catalogs from zero. Plus a third shape for chunking previews and a field the spec promises that does not exist."
updated: 2026-09-04
source_locale: en
sources: [get-orders, get-templates, get-templates-browse, get-voices, get-voices-elevenlabs-library, get-voices-heygen, get-orders-chunks-preview-preview-id]
errors: ["422", "429"]
spec_version: 0.0.1
---

# Pagination

The public API has **two incompatible page numbering bases** and three different response
shapes. There is no universal paginator: code written against `GET /orders` will silently skip the first page
of the ElevenLabs catalog.

## Shape 1: regular lists, pages from one

`GET /orders`, `GET /templates/browse`, `GET /voices`, `GET /voices/library`, `GET /voices/public`
put an object in `data`:

```json
{
  "items": [ /* … */ ],
  "current_page": 1,
  "last_page": 5,
  "per_page": 20,
  "total": 93
}
```

The query parameters are `page` (starting at `1`) and `per_page`. The list is exhausted when
`current_page === last_page`.

> [!WARNING]
> Four descriptions of the `per_page` parameter in the spec promise that “pagination metadata is in `data.meta`”.
> There is no `meta` field in any response schema or in any example: the counters sit directly
> in `data`, next to `items`. Affected: `GET /voices`, `GET /voices/library`, `GET /voices/public`
> and `GET /orders/chunks/preview/{previewId}`. Read `data.current_page`, not `data.meta.current_page`.

### GET /orders

List Orders

Справочник: https://lumean.app/de/developers/docs/api/get-orders

## Shape 2: external voice catalogs, pages from zero

`GET /voices/elevenlabs/library` and `GET /voices/heygen` proxy third-party APIs and keep their
conventions. `data` contains a different wrapper:

```json
{
  "voices": [ /* … */ ],
  "total": 1420,
  "page": 0,
  "page_size": 30,
  "has_more": true
}
```

There are four differences at once: the array is called `voices`, not `items`; the page size is `page_size`,
not `per_page`; the continuation flag is the boolean `has_more`, and there is no last page number at all;
and **`page` counts from zero**. A fifth difference hides inside: `total` means different things
in these two operations. For ElevenLabs it is the number of voices **on the current page** (the spec explicitly says
to look at `has_more`, not `total`); for HeyGen it is how many voices match the filters
across all pages.

> [!CAUTION]
> `page=1` in these two operations is the **second** page. The most common complaints, “the ElevenLabs catalog
> returned something different from the UI” and “the first 30 voices went missing”, are exactly this.
> The first page here is `page=0`, which is also the default.

`GET /voices/elevenlabs/library` depends on the external ElevenLabs API: when that API is unavailable,
the operation responds with `503`. A catalog crawl must survive an interruption midway: store the page
number, not just the accumulated array.

### GET /voices/heygen

HeyGen Voices

Справочник: https://lumean.app/de/developers/docs/api/get-voices-heygen

## Shape 3: chunking preview pages

`GET /orders/chunks/preview/{previewId}` resembles neither the first shape nor the second: the counters
sit in a separate `data.page` object and combine both styles.

| Field | Meaning |
| --- | --- |
| `data.page.current_page` | page number, counting **from 1** |
| `data.page.per_page` | page size (default 100, maximum 500) |
| `data.page.total` | total chunks in the split, across all pages |
| `data.page.has_more` | whether there are more pages; there is no `last_page` here |

The neighboring `data.summary` field covers the **entire** text, not the current page, so you can't sum
it across pages. And remember that `{previewId}` is a sha256 of 64 hex characters, not a UUID.

## What isn't paginated at all

Not all lists are paginated, and trying to page through them looks like “the server ignores `page`”.

| Operation | `data` shape |
| --- | --- |
| `GET /templates` | a flat array of all templates, no pages. For page-by-page traversal, use `GET /templates/browse` |
| `GET /orders/{order}/items` | a flat array of all the order's chunks, including retries |
| `GET /usage`, `GET /voices/tags`, `GET /voices/heygen/filters` | flat arrays of entries |

### GET /templates

List Templates

Справочник: https://lumean.app/de/developers/docs/api/get-templates

## Ceilings and defaults

| Operation | Parameters | Range | Default |
| --- | --- | --- | --- |
| `GET /orders` | `page`, `per_page` | `page ≥ 1`, `per_page` 1..100 | 20 |
| `GET /templates/browse` | `page`, `per_page` | `page ≥ 1`, `per_page` 1..100 | 15 |
| `GET /orders/chunks/preview/{previewId}` | `page`, `per_page` | `page ≥ 1`, `per_page` 1..500 | 100 |
| `GET /voices/elevenlabs/library` | `page`, `page_size` | `page ≥ 0`, `page_size ≥ 1` | 0 / 30 |
| `GET /voices/heygen` | `page`, `page_size` | `page ≥ 0`, `page_size` 1..200 | 0 / 55 |
| `GET /voices`, `/voices/library`, `/voices/public` | `per_page` | no ceiling declared in the spec | 20 |

An out-of-range value returns `422` with the field in `errors`; it is not silently truncated.

### 422 — 422 — request not accepted

| Что произошло | Что делать |
| --- | --- |
| The only API response that comes OUTSIDE the common envelope: `{"message": "…", "errors": {"field": ["…"]}}` — it has no `success` field. Code like `if (!res.success)` works on a 422 only by coincidence, because it is comparing against `undefined`. | First check whether `errors` is present. If it is, fix the request according to the listed fields. If it is not, but there is a `reason`, the request shape is not the problem, and repeating the same body gets the same refusal; read the page for that specific code. |

`Retry-After`: нет. Разбор: https://lumean.app/de/developers/docs/errors/422

> [!WARNING]
> For the three voice lists (`GET /voices`, `GET /voices/library`, `GET /voices/public`) the spec
> declares only `per_page`, even though the response carries `current_page` and `last_page`. In other words, they do have pages,
> but the parameter for moving between them is undocumented. Pass `page` as you would for the other
> shape 1 lists, and check `current_page` in the response to confirm it was applied.

## There is no cursor pagination

Neither `cursor`, nor `next_cursor`, nor `next_page_url` exists in the public API: there is not a single
mention of them in the entire OpenAPI spec. The trap is that the `?pag=cursor` parameter **passes validation
and does not return `422`**, but is silently ignored: the list still comes back in offset form. Page
through with `page` / `per_page`.

## How to traverse an entire list

Offset pagination doesn't give you a consistent snapshot: the data changes while you walk through the pages.
`GET /orders` returns the newest orders first, so a single newly created order shifts the whole listing forward
by one position, and you will see the record at a page boundary twice or not at all.

1. **Deduplicate by `id` on the client side.** This is the only safeguard that works in all
   three shapes. A set of already seen `id`s is cheaper than trying to guess the shift.
2. **Narrow the selection so that it doesn't grow.** The `GET /orders` filters are `status`,
   `task_type[]`, and `template_id`. Iterating over terminal statuses gives an almost stable set,
   whereas `status=in_progress` changes right under you.
3. **Stop on a flag, not a counter.** For shape 1, stop when `current_page` reaches
   `last_page`; for external catalogs, when `has_more === false`. `total` is recalculated on
   every request and is fine for a progress indicator, but not for an exit condition.
4. **Use larger pages, but not the maximum.** `per_page=100` instead of 20 cuts the number of
   requests fivefold, and with it the risk of hitting a `429` during the crawl. Half the maximum page size is
   a reasonable compromise between response size and request count.
5. **Pause between pages.** Crawling a large catalog is the most common way to hit
   the key's request-rate limit, which has no `Retry-After`; see [Limits and the 429 code](/developers/docs/guide/limits).

**Правильно**

Keep a set of seen `id`s, page until `has_more === false` or
`current_page === last_page`, and survive a `503` from the external catalog by resuming from the same page number.

**Неправильно**

Compute the total page count as `Math.ceil(total / per_page)` once at the start and then blindly
iterate over the numbers: `total` will be stale by the third page, and for zero-based catalogs
the last number will be `last - 1`, so you will request the empty tail twice.

---

- Diese Seite im Browser: https://lumean.app/de/developers/docs/guide/pagination
- Vollständige maschinenlesbare Referenz: https://api.lumean.app/docs/llm.md
