# Paginacao API: como implementar de forma eficiente

> Paginacao API exige escolha entre estratégias como offset, cursor e keyset para garantir performance e escalabilidade em endpoints REST. Offset é simples, mas degrada com grandes volumes; cursor e keyset oferecem consistência e eficiência em dados dinâmicos. Implementar paginação eficiente requer considerar ordenação estável, índices adequados e evitar contagens totais desnecessárias. Boas práticas incluem retornar metadados de próxima página e limitar tamanho de respostas.

*Net Propaganda · Apps e Software · 31 de agosto de 2026 · Ptolomeu Rangel Sicupira*

Paginacao API vai alem do limite e offset. Neste guia, voce aprende a escolher a estrategia certa, evitar erros de performance e construir endpoints que escalam. Inclui cursor, keyset e boas praticas para APIs REST.

Quando uma API retorna milhares de registros de uma vez, o cliente nao precisa de tudo. Ele precisa de uma pagina, depois de outra. Paginacao API e o mecanismo que organiza essa entrega. Sem ela, a resposta fica lenta, o servidor sobrecarrega e o cliente descarta a maior parte do payload. Este guia mostra como implementar paginacao de forma eficiente, cobrindo offset, cursor e keyset, com criterios objetivos para cada escolha.

## Passo 1: Entenda o cenario antes de escolher o metodo

Nenhuma estrategia de paginacao e universalmente superior. Offset e cursor resolvem problemas diferentes. Offset, com page e limit, e simples de entender e debugar. O cliente pede ?page=3&limit=20 e recebe os registros 41 a 60. Funciona bem em tabelas pequenas e em paineis administrativos, onde o volume raramente passa de algumas dezenas de milhares de linhas.

Cursor, com ?cursor=abc123&limit=20, usa um identificador opaco que aponta para a posicao exata na lista. Nao ha pagina 3, ha um ponto de continuacao. Isso evita dois problemas classicos do offset: a degradacao de performance em consultas profundas e a inconsistencia quando novos registros entram entre uma requisicao e outra. O GitHub, por exemplo, documenta o uso de paginacao baseada em cursor em sua API REST, o que indica a preferencia por estabilidade em dados que mudam com frequencia.

Ressalva concreta: offset com ORDER BY em coluna nao indexada pode transformar uma consulta de 50 milissegundos em uma de 3 segundos. Cursor exige indice na coluna de ordenacao, mas mantem a latencia quase constante, independente da profundidade.

## Passo 2: Defina os parametros de paginacao no contrato da API

O contrato precisa ser explicito. Parametros comuns: limit (tamanho da pagina) e offset ou cursor. Defina um limite maximo, como 100 por requisicao, e um valor padrao, como 20. Isso evita que um cliente peça 10 mil registros por engano e derrube o endpoint.

A resposta deve incluir metadados de paginacao: total, next_cursor ou next_page, e has_more. Sem eles, o cliente nao sabe se existem mais paginas. Um formato comum:

{ "data": [...], "pagination": { "total": 1050, "next_cursor": "eyJpZCI6MjB9", "has_more": true } }

Erro comum: omitir has_more. O cliente precisa fazer uma requisicao extra para descobrir que a lista acabou. Com has_more, ele para de buscar no momento certo, economizando banda e processamento.

## Passo 3: Implemente offset pagination para casos simples

Offset e a implementacao mais direta. No SQL, seria algo como:

SELECT * FROM produtos ORDER BY id LIMIT 20 OFFSET 40;

No endpoint, o controller traduz page e limit em OFFSET e LIMIT. Use page = 1 como padrao, nao 0, para evitar confusao em logs e ferramentas de monitoramento.

Dica: inclua ORDER BY obrigatorio. Sem ele, a ordem dos registros e indefinida e a paginacao fica inconsistente entre requisicoes. Ordene por uma coluna unica, como id ou created_at com id como desempate.

Contraexemplo: se o cliente insere um registro novo entre a pagina 1 e a pagina 2, o offset desloca todos os registros seguintes. O item que estava na posicao 21 agora esta na 22, e a pagina 2 repete ou pula dados. Isso e aceitavel em listas estaticas, mas nao em feeds ou logs em tempo real.

## Passo 4: Use cursor pagination para dados em movimento

Cursor pagination resolve o problema do deslocamento. Em vez de contar linhas, o servidor usa um valor opaco, geralmente um base64 do ultimo id ou created_at da pagina anterior. O cliente envia esse valor na proxima requisicao, e o servidor retorna os registros a partir daquele ponto.

SELECT * FROM produtos WHERE id > :cursor_id ORDER BY id LIMIT 20;

O cursor e estavel mesmo com insercao de novos registros. Se um item novo entra no topo, a proxima pagina continua de onde parou, sem repeticao. A performance tambem melhora: o banco usa o indice do WHERE em vez de contar linhas descartadas.

Dica: codifique o cursor em base64 para evitar que o cliente tente adivinhar a estrutura interna. Nunca exponha o id bruto como cursor, pois isso permite enumeracao de dados sensiveis.

Erro comum: usar cursor baseado em updated_at sem indice composto. Se a ordenacao for por updated_at e id, crie um indice em (updated_at, id). Sem isso, a consulta faz um scan completo, anulando o beneficio do cursor.

## Passo 5: Considere keyset pagination para datasets estaveis

Keyset e uma variante do cursor que usa colunas de negocio como chave. Em vez de um identificador opaco, o cliente envia o valor da ultima coluna de ordenacao, como ?after=2024-01-15T10:30:00Z. Funciona bem quando a ordenacao e por data e o volume e moderado.

SELECT * FROM eventos WHERE created_at > :last_timestamp ORDER BY created_at LIMIT 20;

A vantagem e a transparencia: o parametro e legivel e facil de debugar. A desvantagem e a fragilidade com empates. Se dois registros tem o mesmo created_at, a paginacao pode pular um deles. Solucao: adicionar id como desempate no WHERE e no ORDER BY.

Ressalva: keyset nao oferece acesso a pagina arbitraria. O cliente so pode navegar para frente, nunca pular para a pagina 5. Para interfaces que precisam de numeracao, offset ainda e a escolha.

## Passo 6: Adicione filtros e ordenacao sem quebrar a paginacao

Filtros e paginacao precisam ser combinados com cuidado. Quando o cliente aplica um filtro, o total deve refletir apenas os registros filtrados, nao o conjunto inteiro. Calcule o total com COUNT(*) na mesma consulta filtrada.

Erro comum: aplicar o filtro depois da paginacao. Se o servidor pagina primeiro e filtra depois, o cliente recebe paginas com menos itens do que o limit e nunca ve todos os resultados. Filtre antes, no WHERE, e pagina depois.

Ordenacao tambem afeta a paginacao. Se o cliente ordena por nome, o cursor precisa ser baseado em nome, nao em id. Caso contrario, a ordem muda entre paginas e o resultado fica inconsistente. Exija que o parametro sort seja acompanhado da coluna correspondente no cursor ou no offset.

Dica: documente os filtros aceitos e os valores padrao de ordenacao. Isso reduz chamadas de suporte e erros de integracao.

## Passo 7: Proteja a API contra abuso e limites extremos

Paginacao exposta publicamente atrai abuso. Um cliente pode iterar todas as paginas de uma lista enorme, gerando carga desnecessaria. Defina limites rigorosos de limit e considere rate limiting por IP ou por chave de API.

Em APIs publicas, o total pode ser omitido para evitar vazamento de dados. Se a lista tem 2 milhoes de registros, o cliente nao precisa saber o numero exato. Retorne apenas has_more e next_cursor.

Contraexemplo: um endpoint de busca com offset ilimitado pode ser usado para extrair a base inteira de clientes, um registro por vez. Com limit maximo de 20 e rate limit de 10 requisicoes por minuto, a extracao levaria dias, inviabilizando o ataque.

## Passo 8: Teste a paginacao sob carga e com dados reais

Teste com volume real, nao com 50 registros de desenvolvimento. Crie um dataset de pelo menos 100 mil linhas e meca a latencia das paginas 1, 50 e 200. Offset tende a degradar a partir da pagina 50; cursor deve manter a latencia estavel.

Use ferramentas como k6 ou Apache JMeter para simular clientes paginando simultaneamente. Observe o uso de CPU e o tempo de resposta. Se a consulta com offset profundo passa de 500 milissegundos, considere migrar para cursor.

Dica: registre o tempo de resposta por pagina em logs estruturados. Isso permite detectar degradacao antes que o usuario reclame.

## Checklist final

- [ ] Escolhi o metodo com base no volume e na taxa de atualizacao dos dados
- [ ] Defini limit maximo e valor padrao no contrato
- [ ] Inclui total, next_cursor ou next_page, e has_more na resposta
- [ ] Adicionei ORDER BY com coluna unica ou composta
- [ ] Filtros aplicados antes da paginacao, com COUNT correto
- [ ] Cursor codificado em base64, sem expor dados internos
- [ ] Rate limit e limites de limit configurados
- [ ] Testes de carga com dataset grande e paginas profundas

## FAQ

### O que e paginacao em API?

Paginacao em API e a tecnica de dividir uma grande lista de resultados em blocos menores, chamados paginas. O cliente solicita uma pagina por vez usando parametros como page e limit ou cursor. Isso reduz o tamanho da resposta, melhora a performance e evita sobrecarga no servidor.

### Qual a diferenca entre offset e cursor pagination?

Offset usa page e limit para pular registros, sendo simples mas lento em paginas profundas. Cursor usa um identificador opaco que marca a posicao exata, mantendo a performance estavel e evitando duplicatas quando novos dados sao inseridos. Offset e melhor para listas pequenas; cursor, para dados em tempo real.

### Quando usar keyset pagination?

Keyset e indicado quando a ordenacao e por uma coluna natural, como data de criacao, e o volume de dados e moderado. Ele e transparente e facil de debugar, mas nao permite acesso a pagina arbitraria. Exige indice na coluna de ordenacao para funcionar bem.

### Como evitar duplicatas na paginacao?

Duplicatas ocorrem quando novos registros sao inseridos entre requisicoes com offset. Use cursor ou keyset, que ancoram a posicao no ultimo item visto. Se precisar de offset, ordene por coluna unica e aceite a possibilidade de pequenas inconsistencias em dados muito dinamicos.

### Qual o limite ideal de itens por pagina?

O limite ideal varia, mas valores entre 20 e 100 sao comuns. Defina um maximo, como 100, para evitar respostas gigantes. O valor padrao deve ser menor, por exemplo 20, para manter a latencia baixa e o payload enxuto.

### O que fazer quando a API precisa de pagina arbitraria?

Se o cliente precisa acessar a pagina 5 diretamente, offset e a unica opcao pratica. Nesse caso, use ORDER BY indexado e limite o limit a 100. Para volumes acima de 100 mil registros, avalie se a pagina arbitraria e realmente necessaria ou se o cursor atende ao caso de uso.

A paginacao nao e um detalhe de implementacao. Ela define a experiencia de quem consome a API e a saude do servidor. Observe o comportamento dos dados, meca a performance e escolha o metodo que se ajusta ao cenario, nao ao mais popular. API que escala nao e a que retorna tudo, e a que entrega o suficiente, na hora certa.

---

Fonte (canonical): https://netpropaganda.com.br/apps-e-software/paginacao-api-como-implementar-de-forma-eficiente/
