¿Cómo describe OpenAPI la paginación en las respuestas de una API?

¿Cómo describe OpenAPI la paginación en las respuestas de una API? #

La paginación es uno de los patrones más comunes en las API REST. Cualquier endpoint que devuelva una colección de recursos (usuarios, pedidos, productos, eventos) debe contemplar el caso en que esa colección sea mayor de lo que conviene devolver en una sola respuesta. OpenAPI no impone un único patrón de paginación, pero ofrece las herramientas de esquemas y parámetros necesarias para describir con precisión cualquier enfoque de paginación. Este artículo recorre cómo describir en documentos OpenAPI los estilos de paginación más habituales.

Por qué importa la paginación en el diseño de API #

Sin paginación, las API que devuelven grandes conjuntos de datos obligan a los clientes a recibir potencialmente millones de registros en una sola respuesta, lo que agota la memoria, el ancho de banda de red y los márgenes de tiempo de espera. La paginación divide los resultados en páginas manejables y permite a los clientes solicitarlas de forma incremental. Desde la perspectiva de OpenAPI, la paginación afecta a:

  • Parámetros de consulta: las entradas que los clientes utilizan para controlar qué página reciben.
  • Esquemas del cuerpo de la respuesta: la forma del contenedor paginado, que incluye el arreglo (array) de datos y metadatos como los totales y los cursores.
  • Encabezados de respuesta: algunas API comunican los metadatos de paginación mediante encabezados en lugar del cuerpo.
  • Encabezados Link: el encabezado HTTP Link (RFC 5988) es una forma estándar de comunicar las URL de la página siguiente y la anterior.

Paginación basada en desplazamiento (offset) #

La paginación por desplazamiento es el enfoque más sencillo: los clientes proporcionan un offset (cuántos registros omitir) y un limit (cuántos registros devolver).

Parámetros #

parameters:
  - name: limit
    in: query
    description: Maximum number of results to return (1–100)
    schema:
      type: integer
      minimum: 1
      maximum: 100
      default: 20

  - name: offset
    in: query
    description: Number of results to skip before returning results
    schema:
      type: integer
      minimum: 0
      default: 0

Esquema de respuesta #

components:
  schemas:
    UserListResponse:
      type: object
      required: [data, total, limit, offset]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/User'
        total:
          type: integer
          description: Total number of users matching the query
          example: 1523
        limit:
          type: integer
          description: Maximum number of results per page
          example: 20
        offset:
          type: integer
          description: Number of results skipped
          example: 40

La paginación por desplazamiento es fácil de implementar y de entender, pero tiene inconvenientes bien conocidos: si se insertan o eliminan registros entre una página y otra, los clientes pueden ver duplicados o perder registros. Para la mayoría de los casos de uso, sigue siendo una opción pragmática.

Paginación por páginas #

La paginación por páginas utiliza los parámetros page (número de página, empezando en 1) y per_page (tamaño de página) en lugar de desplazamientos directos.

Parámetros #

parameters:
  - name: page
    in: query
    description: Page number (1-indexed)
    schema:
      type: integer
      minimum: 1
      default: 1

  - name: per_page
    in: query
    description: Number of results per page
    schema:
      type: integer
      minimum: 1
      maximum: 100
      default: 25

Esquema de respuesta con metadatos de paginación #

components:
  schemas:
    PaginationMeta:
      type: object
      required: [current_page, per_page, total_pages, total_count]
      properties:
        current_page:
          type: integer
          example: 2
        per_page:
          type: integer
          example: 25
        total_pages:
          type: integer
          example: 61
        total_count:
          type: integer
          example: 1523

Paginación basada en cursor #

La paginación basada en cursor (o keyset) es el enfoque más escalable para conjuntos de datos grandes que se actualizan con frecuencia. En lugar de un desplazamiento, la API devuelve un cursor (un token opaco que representa la posición en el conjunto de datos) y el cliente lo envía de vuelta para obtener la página siguiente.

Parámetros #

parameters:
  - name: cursor
    in: query
    description: >
      Opaque cursor returned in the previous response. Omit for the first page.
    schema:
      type: string
      example: "eyJpZCI6MTAwfQ=="

  - name: limit
    in: query
    description: Maximum number of results to return
    schema:
      type: integer
      minimum: 1
      maximum: 100
      default: 20

Esquema de respuesta #

components:
  schemas:
    UserCursorResponse:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/User'
        next_cursor:
          type: string
          nullable: true
          description: >
            Cursor for the next page. Null if this is the last page.
          example: "eyJpZCI6MTIwfQ=="
        has_more:
          type: boolean
          description: Whether there are more results after this page
          example: true

El cursor es opaco para el cliente: no debe interpretarse ni construirse. Es importante documentarlo con claridad en los campos description.

Muchas API (incluida la API de GitHub) utilizan el encabezado HTTP Link (RFC 5988) para comunicar las URL de paginación en lugar de incluirlas en el cuerpo de la respuesta.

responses:
  "200":
    description: A paginated list of repositories
    headers:
      Link:
        description: >
          Pagination links. May include `next`, `prev`, `first`, and `last` relations.
          Example: `<https://api.example.com/users?page=3>; rel="next",
          <https://api.example.com/users?page=1>; rel="first"`
        schema:
          type: string
      X-Total-Count:
        description: Total number of results across all pages
        schema:
          type: integer
    content:
      application/json:
        schema:
          type: array
          items:
            $ref: '#/components/schemas/User'

Componentes de paginación reutilizables #

Para las API con varios endpoints paginados, definir parámetros y esquemas reutilizables en components evita repeticiones y mantiene el documento coherente:

components:
  parameters:
    LimitParam:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20

    CursorParam:
      name: cursor
      in: query
      schema:
        type: string

  schemas:
    CursorPage:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items: {}
        next_cursor:
          type: string
          nullable: true
        has_more:
          type: boolean

Las operaciones individuales hacen referencia a estos componentes compartidos:

paths:
  /users:
    get:
      parameters:
        - $ref: '#/components/parameters/LimitParam'
        - $ref: '#/components/parameters/CursorParam'
      responses:
        "200":
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/CursorPage'
                  - properties:
                      data:
                        items:
                          $ref: '#/components/schemas/User'

Documentar el comportamiento de la paginación en las descripciones #

Las anotaciones de los esquemas por sí solas no describen por completo la semántica de la paginación. Los campos description de las operaciones y de los parámetros deben explicar:

  • Qué estilo de paginación se utiliza y por qué.
  • El comportamiento de los cursores: si caducan y durante cuánto tiempo siguen siendo válidos.
  • El orden de clasificación: la paginación normalmente solo tiene sentido cuando los resultados se encuentran en un orden estable y determinista.
  • El tamaño máximo de página: qué ocurre si el cliente solicita más del máximo.
  • Los resultados vacíos: si un arreglo data vacío o un cursor nulo indica el final de los resultados.

Estándares del sector: JSON:API y HAL #

Algunos equipos adoptan formatos de contenedor estandarizados que incluyen la paginación:

Ambos se pueden describir en OpenAPI mediante sus respectivos esquemas en components.

Conclusión #

Las herramientas de parámetros y esquemas de OpenAPI cubren con precisión todos los patrones de paginación habituales: por desplazamiento, por páginas y basada en cursor. La clave es la coherencia: elegir un único estilo de paginación para todos los endpoints de colecciones, definir parámetros y esquemas de respuesta reutilizables en components e invertir en descripciones claras que expliquen no solo qué hacen los parámetros, sino cómo funciona la paginación en su conjunto. Una paginación bien documentada es una de las mejoras con más impacto que un equipo de API puede hacer en la experiencia del desarrollador.


Última actualización: 30 de abril de 2026.

Este sitio web no está afiliado a la OpenAPI Initiative.