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

# Paginación

> **Warning**
>
> La API tiene **tres estilos de paginación** conviviendo —y varias formas
> distintas de respuesta entre ellos—. No es una decisión de diseño: es historia. Antes de
> recorrer un listado, mirá en la API Reference qué parámetros acepta ese endpoint
> y qué esquema devuelve. Abajo está la tabla de cuál usa cada uno.

## Estilo 1 — `limit` y `offset`

Es el que usan los endpoints más nuevos, entre ellos los de la sección
`organizations` y los de usuarios.

```bash
curl "https://www.crehana.com/api/v5/rest/org/acme/organizations/areas/?limit=50&offset=0" \
  -H "Api-Key: $API_KEY" -H "Secret-Access: $SECRET_ACCESS"
```

La respuesta trae el total de registros y la página pedida:

```json
{
  "total": 214,
  "results": [ ]
}
```

Para avanzar, sumás `limit` al `offset`: `offset=50`, `offset=100`, y así.
Terminás cuando `results` trae menos elementos que el `limit` pedido.

> **Warning**
>
> Estos endpoints **no** devuelven la envoltura de `message` / `code` / `data`:
> `total` y `results` están en la raíz de la respuesta. Es la diferencia que más
> sorprende al escribir el primer cliente, porque el mismo código no sirve para
> leer un listado y para leer una operación de escritura.

> **Note**
>
> El techo de `limit` **no es el mismo en todos los endpoints**, y pedir más
> devuelve `422`. Los de `organizations` y los de `timeoff` cortan en 50, los de
> usuarios y los reportes de quizzes y general de learning en 100, y el reporte
> general v2, el de performance y los cuatro de Asistencia admiten hasta 500. El
> valor por defecto tampoco es único: en varios es 10 y en los reportes es 100.
> Cada operación dice su techo y su valor por omisión en la API Reference; si no
> querés mirarlo, mandá `limit` explícito y quedate abajo de 50.

> **Note**
>
> Este estilo es sensible a los cambios durante el recorrido. Si mientras
> paginás se da de alta un usuario, los registros se corren y podés ver uno
> repetido o saltearte otro. Para exportaciones grandes conviene hacerlas en una
> ventana de poca actividad.

## Estilo 2 — `cursor` y `first`

Es el que usan los endpoints construidos sobre conexiones, entre ellos los
deprecados de estructura organizacional y los grupos de permisos.

```bash
curl "https://www.crehana.com/api/v5/rest/org/acme/permission_groups?first=50" \
  -H "Api-Key: $API_KEY" -H "Secret-Access: $SECRET_ACCESS"
```

La respuesta trae los campos que necesitás para seguir:

```json
{
  "data": [ ],
  "next_cursor": "YXJyYXljb25uZWN0aW9uOjQ5",
  "has_next_page": true,
  "total_count": 214
}
```

Mientras `has_next_page` sea `true`, repetís la llamada pasando
`cursor=<next_cursor>`. Este estilo no se corre si los datos cambian a mitad
de camino: el cursor apunta a una posición estable.

### La variante del catálogo

Los endpoints del catálogo de contenidos también se paginan con `first`, pero
**la respuesta viene con otra forma**: la conexión sin aplanar. Y no todos
avanzan igual: los tres catálogos de cursos de `catalog/v2/` (`third-party`,
`crehana`, `elevate`) reciben el número de página en **`page`**; el resto del
catálogo —los catálogos v1 y el listado de rutas `catalog/v2/tracks/`— recibe
el cursor en **`after`**.
Los registros están en `edges`, cada uno dentro de un `node`, y los datos para
seguir están en `page_info`:

```json
{
  "total_count": 214,
  "page_info": {
    "has_next_page": true,
    "has_previous_page": false,
    "start_cursor": "YXJyYXljb25uZWN0aW9uOjA=",
    "end_cursor": "YXJyYXljb25uZWN0aW9uOjQ5"
  },
  "edges": [
    { "node": { } }
  ]
}
```

El recorrido es el mismo, con los campos en otro lugar: mientras
`page_info.has_next_page` sea `true`, repetís pasando
`after=<page_info.end_cursor>` (o `page=<siguiente>` en los tres catálogos de
cursos de `catalog/v2/`, que ignoran `after`). Y para leer un registro hay que
bajar un nivel más, a `edges[].node`.

## Estilo 3 — `limit` y `cursor` (reportes de Asistencia)

Los reportes de **Recepción de marcas** y **Gestión diaria de marcas** paginan
con un cursor, pero la respuesta conserva la misma forma que el resto de los
reportes: `total` y `results` en la raíz, más dos campos para seguir.

```bash
curl "https://www.crehana.com/api/v5/rest/org/acme/reports/attendance/marks/?from_date=2026-07-20&to_date=2026-08-19&limit=50" \
  -H "Api-Key: $API_KEY" -H "Secret-Access: $SECRET_ACCESS"
```

```json
{
  "total": 42,
  "results": [ ],
  "next_cursor": "YXR0ZW5kYW5jZTpbIjIwMjYtMDctMjAiLC...",
  "has_next_page": true
}
```

Mientras `has_next_page` sea `true`, repetís la misma llamada agregando
`cursor=<next_cursor>`. El cursor es **opaco**: mandalo tal cual vino, sin
decodificarlo ni construirlo vos.

> **Note**
>
> Los otros dos reportes de Asistencia —Creación de turnos y Programación de
> turnos— siguen con `limit` y `offset`, como los reportes de Learning
> `quizzes`, `general`, `general-v2` y `performance`. Son catálogos de cientos de
> filas, no de millones.
>
> Con este mismo estilo (`limit` + `cursor`, sin `offset`) pagina también el
> reporte de Learning `quiz-results` y los dos de Talent (`competences-results` y
> `competences-processes`).

### Por qué estos dos son distintos

Marcas y jornadas crecen con colaboradores × días: en una organización grande
son millones de filas. Con `offset`, cada página obliga a la base a producir y
descartar todas las filas anteriores, así que bajar el reporte completo se
vuelve cada vez más lento a medida que avanzás. El cursor apunta a la última
fila entregada, de modo que la página siguiente arranca ahí y cuesta lo mismo
sea la primera o la número doscientos.

> **Warning**
>
> **Estos dos endpoints no aceptan `offset`.** Si lo mandás, la respuesta es
> `400`. No se ignora en silencio a propósito: si lo hiciera, recibirías siempre
> la primera página creyendo que estás avanzando.

> **Warning**
>
> **La paginación es solo hacia adelante.** No hay cursor previo ni
> `has_previous_page`: no se puede retroceder ni saltar a una página arbitraria.
> Si necesitás una porción puntual del reporte, acotá `from_date` y `to_date` en
> vez de paginar hasta ahí.

### El recorrido completo

```bash
# Página 1: sin cursor
curl ".../reports/attendance/marks/?from_date=2026-07-20&to_date=2026-08-19&limit=50"

# Guardás next_cursor de esa respuesta y lo mandás en la siguiente
curl ".../reports/attendance/marks/?from_date=2026-07-20&to_date=2026-08-19&limit=50&cursor=<next_cursor>"

# Repetís mientras has_next_page sea true.
# En la última página next_cursor viene en null y has_next_page en false.
```

> **Note**
>
> El cursor está atado a la consulta que lo generó. Si cambiás `from_date`,
> `to_date`, `limit` o el filtro por colaborador, empezá de nuevo desde la
> primera página: un cursor de otra consulta devuelve `400`.

## Cuál usa cada endpoint

En vez de memorizarlo, miralo en la API Reference: los parámetros de query
están listados en cada operación, y el esquema de la respuesta dice cuál forma
devuelve. Como regla aproximada:

| Si el endpoint…                                                                         | Paginación         | Forma de la respuesta                 |
| --------------------------------------------------------------------------------------- | ------------------ | ------------------------------------- |
| Está bajo `/organizations/`, `/users/user-organizations/`, `/reports/` o `/timeoff/`    | `limit` + `offset` | `total` + `results`                   |
| Es `/reports/attendance/marks/` o `/reports/attendance/daily-management/`               | `limit` + `cursor` | `total` + `results` + `next_cursor`   |
| Es `/learning/content/knowledge-hub/catalog/v2/third-party/`, `/crehana/` o `/elevate/` | `first` + `page`   | `total_count` + `page_info` + `edges` |
| Está bajo `/learning/content/knowledge-hub/catalog/` (resto, incluido `/v2/tracks/`)    | `first` + `after`  | `total_count` + `page_info` + `edges` |
| Es `/users/`, `/users/state/` o `/custom-fields/`                                       | `first` + `cursor` | `data` + `next_cursor`                |
| Está marcado como deprecado                                                             | `first` + `cursor` | mirar la operación                    |

## Recomendaciones

* **No asumas un tamaño de página por defecto.** Pedí siempre `limit` o
  `first` de forma explícita.
* **Frená por la respuesta, no por un contador.** Usá `has_next_page`, o que
  la página venga incompleta. Calcular la cantidad de páginas a partir de
  `total_count` falla si los datos cambian.
* **Guardá el cursor, no el número de página.** En los estilos 2 y 3 el cursor
  es lo único que garantiza continuidad.
* **No construyas ni interpretes un cursor.** Es opaco y su formato puede
  cambiar sin aviso: reenviá exactamente el que te devolvió la respuesta.