Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Changelog

## Unreleased

### Paginación por cursores (`pagination=cursor`)

- Nuevo modo de paginación opt-in `pagination=cursor`, **recomendado** para
listas grandes: recorre todos los resultados sin el tope de 30 páginas del
modo `page`, con páginas estables aunque cambien los datos.
- Nuevos parámetros `after` y `before` para navegar entre páginas de forma
secuencial. Cada búsqueda regresa `previous_cursor` y `next_cursor`.
- Nuevo campo `totals_are_capped` en las respuestas de búsqueda de ambos modos:
cuando hay más de 3,000 resultados, `total_results` regresa `3000` y
`totals_are_capped` es `true` ("más de 3,000").
- La paginación por páginas (`pagination=page`, el modo por defecto) queda
limitada a 30 páginas.
153 changes: 153 additions & 0 deletions website/docs/guides/pagination.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
sidebar_position: 11
---

# Paginación

Todos los listados de la API devuelven resultados paginados. Facturapi ofrece dos
modos de paginación que puedes elegir con el parámetro `pagination`:

- **`pagination=page`** — el modo clásico, por **páginas numeradas**. Es el
comportamiento por defecto cuando no se envía `pagination`.
- **`pagination=cursor`** — el modo recomendado, por **cursores**. Recorre los
resultados de forma secuencial, sin el tope de 30 páginas del modo `page`, y
con páginas estables aunque cambien los datos.

## ¿Qué modo usar?

| | `pagination=page` | `pagination=cursor` |
| --- | --- | --- |
| Navegación | Saltar a una página específica (`page=3`) | Secuencial: siguiente / anterior |
| Alcance | Hasta 3,000 resultados (30 páginas × 100) | Todos los resultados de la búsqueda |
| Estabilidad | Las páginas se corren si cambian los datos | Páginas estables (basadas en posición) |
| Uso recomendado | Compatibilidad, interfaces con saltos de página | Listas grandes, iteración, exportaciones |

Para la mayoría de los casos, y en particular para listas grandes, te recomendamos
usar `pagination=cursor`.

La diferencia práctica más importante entre ambos modos es el **alcance**:
`pagination=page` solo permite acceder a los primeros 3,000 resultados
(30 páginas × 100 por página), mientras que `pagination=cursor` puede recorrer
**todos** los resultados de la búsqueda, incluso cuando `total_results` está
capeado a 3,000. Si necesitas exportar o procesar más de 3,000 resultados, usa
`pagination=cursor`.

## Parámetros comunes

| Parámetro | Tipo | Descripción |
| --- | --- | --- |
| `limit` | `integer` | Cantidad máxima de resultados a regresar, del 1 al 100. |
| `pagination` | `string` | `page` (por defecto) o `cursor`. |

Los parámetros `page`, `after` y `before` dependen del modo elegido y son
**mutuamente excluyentes** entre modos:

- `pagination=page` acepta `page`, pero no `after` ni `before`.
- `pagination=cursor` acepta `after` o `before`, pero no `page`.

Enviar `after` y `before` al mismo tiempo devuelve un error.

## Paginación por páginas (`pagination=page`)

Es el modo por defecto. Además de `limit`, acepta:

| Parámetro | Descripción |
| --- | --- |
| `page` | Número de página a regresar, empezando desde 1. La página máxima es 30. |

Respuesta:

```json
{
"data": [ ... ],
"page": 1,
"total_pages": 3,
"total_results": 250,
"totals_are_capped": false
}
```

## Paginación por cursores (`pagination=cursor`)

En lugar de un número de página, cada respuesta regresa dos cursores que te
permiten avanzar o retroceder sobre los resultados:

| Parámetro | Descripción |
| --- | --- |
| `after` | Devuelve los resultados posteriores al cursor indicado. |
| `before` | Devuelve los resultados anteriores al cursor indicado. |

Respuesta:

```json
{
"data": [ ... ],
"previous_cursor": null,
"next_cursor": "d176717f4000000.i65a8282775d4e9263da48115",
"total_results": 250,
"totals_are_capped": false
}
```

`total_results` y `totals_are_capped` se incluyen únicamente en la **primera
página** de la búsqueda (cuando no se envían `after` ni `before`): el total no
cambia entre páginas, así que las siguientes solo regresan `data` y los cursores.

### Navegar entre páginas

1. **Primera página:** se obtiene sin enviar `after` ni `before`.
`previous_cursor` regresa `null`, indicando que no hay páginas anteriores.
2. **Página siguiente:** envía `after` con el valor de `next_cursor` de la
respuesta anterior.
3. **Página anterior:** envía `before` con el valor de `previous_cursor` de la
respuesta anterior.

La presencia de `previous_cursor` y `next_cursor` es la única fuente de verdad
para saber si hay más resultados: si `next_cursor` es `null`, llegaste al final
de la lista; si `previous_cursor` es `null`, estás en la primera página.

```bash
# Primera página
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"

# Página siguiente (usa next_cursor de la respuesta anterior)
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10&after=NEXT_CURSOR" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"

# Página anterior (usa previous_cursor de la respuesta anterior)
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10&before=PREVIOUS_CURSOR" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"
```

Los cursores son tokens opacos y específicos de la búsqueda que los generó: no
los combines con otra búsqueda ni los compartas entre distintas consultas.
Usarlos con una búsqueda diferente puede regresar resultados inesperados.

## Totales y límite de 3,000

Ambos modos regresan `total_results` con el número de elementos que coinciden con
la búsqueda, **con un tope de 3,000**:

- Si hay 3,000 o menos resultados, `total_results` es exacto y
`totals_are_capped` es `false`.
- Si hay más de 3,000, `total_results` regresa `3000` y `totals_are_capped` es
`true`, indicando que el total real es "más de 3,000".

```json
{
"data": [ ... ],
"previous_cursor": "d176717f4000000.i65a8282775d4e9263da48115",
"next_cursor": null,
"total_results": 3000,
"totals_are_capped": true
}
```

En ambos modos `total_results` es informativo: el conteo se acota a 3,000 y no
recorre toda la colección. La ventaja práctica de `pagination=cursor` no es la
velocidad del conteo (ambos modos lo acotan), sino su **alcance** y la
**estabilidad** de las páginas.
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
sidebar_position: 11
---

# Pagination

All API listing endpoints return paginated results. Facturapi offers two
pagination modes that you can choose with the `pagination` parameter:

- **`pagination=page`** — the classic mode, based on **numbered pages**. This is
the default behavior when `pagination` is not sent.
- **`pagination=cursor`** — the recommended mode, based on **cursors**. It
traverses results sequentially, without the 30-page limit of `page` mode, and
with stable pages even when the data changes.

## Which mode should I use?

| | `pagination=page` | `pagination=cursor` |
| --- | --- | --- |
| Navigation | Jump to a specific page (`page=3`) | Sequential: next / previous |
| Reach | Up to 3,000 results (30 pages × 100) | All search results |
| Stability | Pages shift when the data changes | Stable pages (position-based) |
| Recommended for | Compatibility, UIs with page jumps | Large lists, iteration, exports |

For most cases, and especially for large lists, we recommend using
`pagination=cursor`.

The most important practical difference between both modes is **reach**:
`pagination=page` only gives access to the first 3,000 results
(30 pages × 100 per page), while `pagination=cursor` can traverse **all** the
search results, even when `total_results` is capped at 3,000. If you need to
export or process more than 3,000 results, use `pagination=cursor`.

## Common parameters

| Parameter | Type | Description |
| --- | --- | --- |
| `limit` | `integer` | Maximum number of results to return, from 1 to 100. |
| `pagination` | `string` | `page` (default) or `cursor`. |

The `page`, `after` and `before` parameters depend on the selected mode and are
**mutually exclusive** between modes:

- `pagination=page` accepts `page`, but not `after` or `before`.
- `pagination=cursor` accepts `after` or `before`, but not `page`.

Sending `after` and `before` at the same time returns an error.

## Page pagination (`pagination=page`)

This is the default mode. In addition to `limit`, it accepts:

| Parameter | Description |
| --- | --- |
| `page` | Page number to return, starting from 1. The maximum page is 30. |

Response:

```json
{
"data": [ ... ],
"page": 1,
"total_pages": 3,
"total_results": 250,
"totals_are_capped": false
}
```

## Cursor pagination (`pagination=cursor`)

Instead of a page number, each response returns two cursors that let you move
forward or backward through the results:

| Parameter | Description |
| --- | --- |
| `after` | Returns the results after the given cursor. |
| `before` | Returns the results before the given cursor. |

Response:

```json
{
"data": [ ... ],
"previous_cursor": null,
"next_cursor": "d176717f4000000.i65a8282775d4e9263da48115",
"total_results": 250,
"totals_are_capped": false
}
```

`total_results` and `totals_are_capped` are only included on the **first page**
of the search (when neither `after` nor `before` is sent): the total does not
change between pages, so subsequent pages only return `data` and the cursors.

### Navigating between pages

1. **First page:** obtained by not sending `after` or `before`.
`previous_cursor` returns `null`, meaning there are no previous pages.
2. **Next page:** send `after` with the `next_cursor` value from the previous
response.
3. **Previous page:** send `before` with the `previous_cursor` value from the
previous response.

The presence of `previous_cursor` and `next_cursor` is the only source of truth
for whether there are more results: if `next_cursor` is `null`, you reached the
end of the list; if `previous_cursor` is `null`, you are on the first page.

```bash
# First page
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"

# Next page (use next_cursor from the previous response)
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10&after=NEXT_CURSOR" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"

# Previous page (use previous_cursor from the previous response)
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10&before=PREVIOUS_CURSOR" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"
```

Cursors are opaque tokens tied to the search that generated them: do not combine
them with a different search or share them between queries. Using them with a
different search may return unexpected results.

## Totals and the 3,000 limit

Both modes return `total_results` with the number of elements matching the
search, **capped at 3,000**:

- If there are 3,000 or fewer results, `total_results` is exact and
`totals_are_capped` is `false`.
- If there are more than 3,000, `total_results` returns `3000` and
`totals_are_capped` is `true`, indicating the real total is "more than 3,000".

```json
{
"data": [ ... ],
"previous_cursor": "d176717f4000000.i65a8282775d4e9263da48115",
"next_cursor": null,
"total_results": 3000,
"totals_are_capped": true
}
```

In both modes, `total_results` is informational and the count is capped at
3,000 without traversing the whole collection. The practical advantage of
`pagination=cursor` is not count speed (both modes cap it), but **reach** and
**stable pages**.
Loading