Paginação
Percorra listas longas com cursor: estável mesmo que registros sejam criados enquanto você pagina.
O que pagina como
| Endpoint | Cursor | Offset | Ordem | meta |
|---|---|---|---|---|
GET /api/v1/cotizaciones |
Sim | Sim | O mais recente primeiro | limit, offset, total, next_cursor |
GET /api/v1/clientes |
Sim | Sim | Por empresa (de A a Z) |
limit, offset, total, next_cursor |
GET /api/v1/productos |
Sim | Sim | Ativos primeiro, depois por nombre |
limit, offset, total, next_cursor |
GET /api/v1/facturas |
Sim | Não | A mais recente primeiro | next_cursor |
GET /api/v1/events |
Sim | Não | O mais recente primeiro | next_cursor |
GET /api/v1/webhooks |
Não | Não | O mais recente primeiro | Sem meta: devolve todos os endpoints da chave. |
GET /api/v1/cobranza |
Não | Não | — | Sem meta: devolve a carteira completa de recebíveis. |
Em todas as listas paginadas, limit vai de 1 a 200 e vale 50 se você não o enviar.
Use o cursor
Peça a primeira página sem cursor. Se meta.next_cursor trouxer um valor, repita a mesma requisição, com os mesmos filtros, acrescentando cursor=<next_cursor>. Quando chegar null, não há mais nada.
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_..."
O exemplo reduz cada cliente a três campos; a resposta real traz o objeto completo.
O cursor é um token opaco: não o interprete, não o construa e não o guarde para outra lista. Reenvie-o como está, codificado para URL (os exemplos abaixo fazem isso por você). Com cursor, o offset é ignorado e meta.offset chega como null.
Por que cursor e não offset
Com offset, um orçamento criado enquanto você percorre a lista desloca tudo em uma posição: a página seguinte repete um registro e, se um for excluído, você pula outro sem perceber. O cursor aponta para a última linha que você viu, então a próxima página começa exatamente depois dela, haja ou não escritas.
offset continua funcionando em orçamentos, clientes e produtos por compatibilidade. meta.total conta todos os registros do filtro: serve para mostrar “142 clientes”, não para decidir quando parar. Para isso, use next_cursor.
Percorrer uma lista completa
#!/usr/bin/env bash
# Exige jq. Imprime o id e a empresa de todos os clientes.
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
doneimport { Cord } from '@flouviahq/node';
const cord = new Cord(process.env.CORD_API_KEY);
// listAll() pede páginas de 200 e segue next_cursor por você.
for await (const cliente of cord.clients.listAll()) {
console.log(cliente.id, cliente.empresa);
}
// Com filtros:
for await (const factura of cord.invoices.listAll({ estado: 'open' })) {
console.log(factura.numero, factura.saldo, factura.moneda);
}const base = 'https://cordhq.app/api/v1/events';
let cursor = null;
do {
const url = new URL(base);
url.searchParams.set('limit', '200');
url.searchParams.set('type', 'quote.paid');
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.CORD_API_KEY}` } });
if (!res.ok) throw new Error(`${res.status} ${(await res.json()).code}`);
const page = await res.json();
for (const event of page.data) console.log(event.created_at, event.type, event.object_id);
cursor = page.meta.next_cursor;
} while (cursor);listAll() existe em quotes, clients, products, invoices e events, e sempre pagina por cursor desde a versão 1.1.0 do SDK. Veja SDK de Node.
Erros
| Situação | Resposta |
|---|---|
Um cursor que não é de orçamentos, clientes ou produtos |
400, code: "invalid_cursor", error: "El cursor no es válido. Usa el next_cursor de la respuesta anterior." |
Um cursor que não é de eventos |
400, code: "invalid_request", error: "cursor inválido" |
limit fora do intervalo |
Não é erro: é ajustado para 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"
}
Um cursor não serve entre listas nem entre filtros: se você mudar status, type ou qualquer outro filtro, comece de novo sem cursor.