Paginação
Cursor, não página — e por quê.
Toda listagem devolve o mesmo envelope, com um cursor para a próxima página. Não existe page, e não é esquecimento.
A forma
{
"object": "list",
"data": [ /* … */ ],
"pagination": {
"has_more": true,
"next_cursor": "cnt_01K7ZFA5X8Q2M4N6P8R0T2V4W6"
}
}
limit vale 25 por padrão e no máximo 100 . after recebe o next_cursor da página anterior — e nada além disso: um cursor inventado responde 422 invalid_cursor, e não a primeira página em silêncio.
Terminal
curl "https://api.amivu.com.br/v1/conversations?limit=50&after=conv_01K7..." \
-H "Authorization: Bearer $AMIVU_API_KEY"
Por que não page=2
Dois problemas, e o segundo é o que machuca:
- Custo.
OFFSETcresce com a distância — a página 500 obriga o banco a varrer e descartar 12.475 linhas antes de devolver 25. - Ele mente enquanto você pagina. Uma conversa nova entrando no topo empurra tudo para baixo, e a página 2 devolve a última linha da página 1 de novo. Quem sincroniza um CRM assim duplica registro.
O cursor é uma posição, e não uma distância. A consulta vira “depois desta linha” — uma busca por índice — e o que entrou no topo depois simplesmente não aparece, em vez de deslocar tudo.
Percorrer tudo
Pare por `has_more`, não por lista vazia
Uma página cheia com
has_more: false é o fim legítimo. Esperar uma página vazia custa uma requisição a mais em toda varredura.async function* todosOsContatos() {
let cursor = null;
while (true) {
const url = new URL("https://api.amivu.com.br/v1/contacts");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("after", cursor);
const resposta = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.AMIVU_API_KEY}` },
});
const { data, pagination } = await resposta.json();
yield* data;
if (!pagination.has_more) return;
cursor = pagination.next_cursor;
}
}Escrevemos sobre operação de atendimento, canais e IA no blog da Amivu.