Aller au contenu

Pagination

Parcourez les longues listes avec un curseur : stable même si des enregistrements sont créés pendant que vous paginez.

Quelle pagination pour quelle liste

Endpoint Curseur Offset Ordre meta
GET /api/v1/cotizaciones Oui Oui Le plus récent en premier limit, offset, total, next_cursor
GET /api/v1/clientes Oui Oui Par empresa (de A à Z) limit, offset, total, next_cursor
GET /api/v1/productos Oui Oui Actifs d’abord, puis par nombre limit, offset, total, next_cursor
GET /api/v1/facturas Oui Non La plus récente en premier next_cursor
GET /api/v1/events Oui Non Le plus récent en premier next_cursor
GET /api/v1/webhooks Non Non Le plus récent en premier Pas de meta : renvoie tous les endpoints de la clé.
GET /api/v1/cobranza Non Non — Pas de meta : renvoie l’ensemble des créances.

Dans toutes les listes paginées, limit va de 1 à 200 et vaut 50 si vous ne l’envoyez pas.

Utilisez le curseur

Demandez la première page sans curseur. Si meta.next_cursor contient une valeur, répétez la même requête, avec les mêmes filtres, en ajoutant cursor=<next_cursor>. Lorsqu’il vaut null, il n’y a plus rien.

curl "https://cordhq.app/api/v1/clientes?limit=2" \
  -H "Authorization: Bearer sk_test_..."
{
  "data": [
    { "id": "0e6f2b8a-91c4-4d7e-b3a5-6c2d8f1e4a90", "empresa": "Aceros Monterrey", "email": "compras@acerosmty.mx" },
    { "id": "7a2d4c19-5e8b-4f3a-a1d6-9c0e2b4f6d81", "empresa": "Bodega Central", "email": "" }
  ],
  "meta": {
    "limit": 2,
    "offset": 0,
    "total": 142,
    "next_cursor": "WyJCb2RlZ2EgQ2VudHJhbCIsIjdhMmQ0YzE5LTVlOGItNGYzYS1hMWQ2LTljMGUyYjRmNmQ4MSJd"
  }
}
curl "https://cordhq.app/api/v1/clientes?limit=2&cursor=WyJCb2RlZ2EgQ2VudHJhbCIsIjdhMmQ0YzE5LTVlOGItNGYzYS1hMWQ2LTljMGUyYjRmNmQ4MSJd" \
  -H "Authorization: Bearer sk_test_..."

L’exemple réduit chaque client à trois champs ; la réponse réelle contient l’objet complet.

Le curseur est un jeton opaque : ne l’interprétez pas, ne le construisez pas et ne le conservez pas pour une autre liste. Renvoyez-le tel quel, encodé pour l’URL (les exemples ci-dessous le font pour vous). Avec cursor, l’offset est ignoré et meta.offset vaut null.

Pourquoi un curseur plutôt qu’un offset

Avec offset, un devis créé pendant que vous parcourez la liste décale tout d’une position : la page suivante répète un enregistrement, et si l’un d’eux est supprimé, vous en sautez un autre sans vous en rendre compte. Le curseur pointe vers la dernière ligne que vous avez vue : la page suivante commence donc exactement après, qu’il y ait eu des écritures ou non.

offset fonctionne toujours sur les devis, les clients et les produits, par compatibilité. meta.total compte tous les enregistrements correspondant au filtre : il sert à afficher « 142 clients », pas à décider quand s’arrêter. Pour cela, utilisez next_cursor.

Parcourir une liste complète

#!/usr/bin/env bash
# Nécessite jq. Affiche l’id et l’entreprise de tous les clients.
set -euo pipefail
args=(-sSG "https://cordhq.app/api/v1/clientes" -d limit=200 -H "Authorization: Bearer $CORD_API_KEY")
cursor=""
while :; do
  if [ -n "$cursor" ]; then
    page=$(curl "${args[@]}" --data-urlencode "cursor=$cursor")
  else
    page=$(curl "${args[@]}")
  fi
  echo "$page" | jq -r '.data[] | [.id, .empresa] | @tsv'
  cursor=$(echo "$page" | jq -r '.meta.next_cursor // empty')
  [ -z "$cursor" ] && break
done

listAll() existe sur quotes, clients, products, invoices et events, et pagine toujours par curseur depuis la version 1.1.0 du SDK. Voir SDK Node.

Erreurs

Situation Réponse
Un cursor qui ne provient pas des devis, des clients ou des produits 400, code: "invalid_cursor", error: "El cursor no es válido. Usa el next_cursor de la respuesta anterior."
Un cursor qui ne provient pas des événements 400, code: "invalid_request", error: "cursor inválido"
limit hors limites Ce n’est pas une erreur : il est ramené à 1 ou à 200.
{
  "error": "El cursor no es válido. Usa el next_cursor de la respuesta anterior.",
  "code": "invalid_cursor",
  "request_id": "req_5d2b8f1a4c7e0b3d6a9f2c5e",
  "doc_url": "https://docs.cordhq.app/docs/desarrolladores/esenciales/errores#invalid_cursor"
}

Un curseur ne se transmet ni d’une liste à l’autre ni d’un filtre à l’autre : si vous changez status, type ou tout autre filtre, recommencez sans curseur.