Paginierung
Durchlaufen Sie lange Listen mit einem Cursor: stabil, auch wenn während der Paginierung Datensätze entstehen.
Was wie paginiert
| Endpoint | Cursor | Offset | Reihenfolge | meta |
|---|---|---|---|---|
GET /api/v1/cotizaciones |
Ja | Ja | Neueste zuerst | limit, offset, total, next_cursor |
GET /api/v1/clientes |
Ja | Ja | Nach empresa (A bis Z) |
limit, offset, total, next_cursor |
GET /api/v1/productos |
Ja | Ja | Aktive zuerst, dann nach nombre |
limit, offset, total, next_cursor |
GET /api/v1/facturas |
Ja | Nein | Neueste zuerst | next_cursor |
GET /api/v1/events |
Ja | Nein | Neuestes zuerst | next_cursor |
GET /api/v1/webhooks |
Nein | Nein | Neueste zuerst | Ohne meta: gibt alle Endpoints des Schlüssels zurück. |
GET /api/v1/cobranza |
Nein | Nein | — | Ohne meta: gibt die gesamten offenen Forderungen zurück. |
In allen paginierten Listen reicht limit von 1 bis 200 und beträgt 50, wenn Sie es nicht angeben.
Den Cursor verwenden
Fordern Sie die erste Seite ohne Cursor an. Enthält meta.next_cursor einen Wert, wiederholen Sie dieselbe Anfrage mit denselben Filtern und fügen cursor=<next_cursor> hinzu. Kommt null zurück, gibt es nichts mehr.
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_..."
Das Beispiel kürzt jeden Kunden auf drei Felder; die echte Antwort enthält das vollständige Objekt.
Der Cursor ist ein opakes Token: Interpretieren Sie ihn nicht, bauen Sie ihn nicht selbst und verwenden Sie ihn nicht für eine andere Liste. Senden Sie ihn unverändert und URL-kodiert zurück (die Beispiele unten erledigen das für Sie). Mit cursor wird offset ignoriert, und meta.offset kommt als null zurück.
Warum Cursor und nicht Offset
Mit offset verschiebt ein Angebot, das angelegt wird, während Sie die Liste durchlaufen, alles um eine Position: Die nächste Seite wiederholt einen Datensatz, und wird einer gelöscht, überspringen Sie einen anderen, ohne es zu bemerken. Der Cursor zeigt auf die letzte Zeile, die Sie gesehen haben, sodass die nächste Seite genau danach beginnt – unabhängig davon, ob geschrieben wurde.
offset funktioniert aus Kompatibilitätsgründen weiterhin bei Angeboten, Kunden und Produkten. meta.total zählt alle Datensätze, die dem Filter entsprechen: Es dient dazu, „142 Kunden“ anzuzeigen, nicht dazu, zu entscheiden, wann Sie aufhören. Verwenden Sie dafür next_cursor.
Eine vollständige Liste durchlaufen
#!/usr/bin/env bash
# Erfordert jq. Gibt ID und Firma aller Kunden aus.
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() fordert Seiten mit 200 Einträgen an und folgt next_cursor für Sie.
for await (const cliente of cord.clients.listAll()) {
console.log(cliente.id, cliente.empresa);
}
// Mit Filtern:
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() gibt es bei quotes, clients, products, invoices und events; seit Version 1.1.0 des SDK paginiert es immer per Cursor. Siehe Node-SDK.
Fehler
| Situation | Antwort |
|---|---|
Ein cursor, der nicht von Angeboten, Kunden oder Produkten stammt |
400, code: "invalid_cursor", error: "El cursor no es válido. Usa el next_cursor de la respuesta anterior." |
Ein cursor, der nicht von Ereignissen stammt |
400, code: "invalid_request", error: "cursor inválido" |
limit außerhalb des Bereichs |
Kein Fehler: Der Wert wird auf 1 bzw. 200 begrenzt. |
{
"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"
}
Ein Cursor gilt nicht listen- oder filterübergreifend: Wenn Sie status, type oder einen anderen Filter ändern, beginnen Sie erneut ohne Cursor.