---
agentTools:
  projectIndex: https://developers.pipe.run/llms.txt
---

# Migrando a paginação antiga

Migrando sua integração de paginação por página usando parâmetro `page` para o `cursor`

Para garantir a melhor experiência e performance, estamos migrando nossa paginação de `page` para `cursor`. Essa mudança traz um ganho significativo, especialmente para grandes volumes de dados.

A paginação por cursor oferece mais estabilidade e eficiência, pois aponta para uma posição fixa na lista de resultados. Isso minimiza problemas caso os dados sejam alterados durante a sua iteração.

Incentivamos você a migrar para o novo método para aproveitar todos os benefícios de uma integração mais robusta e performática.

> ❗️ Esta migração não é só uma recomendação de uso
>
> A migração para a paginação por cursor é crucial para a excelência das operações que a PipeRun oferece. Por isso, a adoção deste novo método não é apenas uma recomendação, mas sim um passo que se tornará obrigatório em breve.

[block:html]
{
  "html": "<details>\n  <summary><strong>Migre sua integração com ajuda de IA: paginação <code>page</code> → <code>cursor</code></strong></summary>Se você usa uma ferramenta de IA (Claude, ChatGPT, Copilot, Gemini, Cursor etc.), copie o bloco abaixo, cole junto com o código da sua integração e peça a refatoração. Revise o resultado antes de aplicar em produção.\n\n<div class=\"CodeTabs CodeTabs_initial theme-light\"><div class=\"CodeTabs-toolbar\" role=\"tablist\"><button aria-selected=\"true\" class=\"CodeTabs_active\" role=\"tab\" type=\"button\">Markdown</button></div><div class=\"CodeTabs-inner\" role=\"tabpanel\"><pre><code class=\"rdmd-code lang-markdown theme-light\" data-lang=\"markdown\" name=\"\" tabindex=\"0\"><div class=\"cm-s-neo\" data-testid=\"SyntaxHighlighter\">Refatore minha integração com a API do CRM PipeRun (`api.pipe.run`) para migrar da paginação antiga por número de página (parâmetro `page`) para a nova paginação por cursor (parâmetro `cursor`). Analise o código que vou fornecer, identifique **todos** os pontos que listam dados paginados da PipeRun e reescreva-os no formato novo, mantendo a linguagem e o estilo do código original.\n\nSe o meu código for **Python**, antes de refatorar, avalie a biblioteca oficial `piperun-extractor` (https://github.com/crmpiperun/piperun-extractor). Ela já implementa a paginação por cursor, faz carga incremental por data (`after`) e exporta para JSONL/Parquet. Se ela cobrir o meu caso de uso, recomende substituí-la ao código customizado em vez de refatorar; mantenha código próprio apenas se houver requisito que a biblioteca não atende, e justifique.\n\n## Formato antigo (a remover)\n\nRequisição:\n\n```http\nGET /v1/deals?show=10&amp;page=1\n```\nResposta (campos de paginação):\n\n```json\n{\n  \"success\": true,\n  \"data\": [{}, {}],\n  \"meta\": {\n    \"total\": 50,\n    \"count\": 10,\n    \"per_page\": 10,\n    \"current_page\": 1,\n    \"total_pages\": 5,\n    \"links\": { \"next\": \"https://api.pipe.run/v1/deals?show=10&amp;page=2\" }\n  }\n}\n```\nA iteração incrementava `page` até atingir `total_pages` ou `data` vir vazio.\n\n## Formato novo (a adotar)\n\nPrimeira requisição — o `cursor` vazio é proposital:\n\n```http\nGET /v1/deals?show=10&amp;cursor=\n```\nResposta (campos de paginação):\n\n```json\n{\n  \"success\": true,\n  \"data\": [{}, {}],\n  \"meta\": {\n    \"cursor\": {\n      \"current\": null,\n      \"prev\": null,\n      \"next\": \"eyJpZCI7NDA2NTA3UTYsIl9wb2ludHNUb05leHRJdGVtRyI6dHJ1ZX0\",\n      \"count\": null\n    }\n  }\n}\n```\nCada requisição seguinte repete a chamada passando em `cursor` o valor exato de `meta.cursor.next` da resposta anterior. A iteração termina quando `meta.cursor.next` vier `null`/vazio ou `data` vier vazio.\n\nPseudocódigo de referência (adapte à linguagem do meu código):\n\n```python\ncursor = \"\"  # primeira chamada com cursor vazio\nwhile True:\n    resp = api.get(f\"/v1/deals?show=10&amp;cursor={cursor}\")\n    processar(resp[\"data\"])\n    cursor = resp[\"meta\"][\"cursor\"][\"next\"]\n    if not cursor or not resp[\"data\"]:\n        break\n```\n## Regras obrigatórias\n\n1. **As requisições de paginação DEVEM ser sequenciais.** Se o código atual busca páginas em paralelo — threads, workers, pool de conexões, `Promise.all`, `asyncio.gather`, goroutines, jobs em fila disparados por número de página, ou qualquer padrão do tipo \"descobre `total_pages` e dispara N requisições\" — remova esse paralelismo do loop de paginação. No formato novo, cada requisição depende do cursor retornado pela anterior; não existe como calcular cursors antecipadamente. Isso é uma decisão de arquitetura da PipeRun, não uma limitação a ser contornada. Paralelismo em outras partes do código (ex.: processamento dos dados após a busca) pode ser mantido.\n2. Trate o cursor como uma string opaca: não decodifique, não modifique e não construa cursors manualmente. Apenas repasse o valor recebido em `meta.cursor.next`.\n3. Remova toda dependência de `meta.total`, `meta.total_pages`, `meta.current_page`, `meta.per_page` e `meta.links` — esses campos não existem na resposta nova. Se houver barra de progresso ou log do tipo \"página X de Y\", substitua por contagem acumulada de registros processados.\n4. Não altere o processamento dos dados (`data`), filtros de negócio, autenticação, retries ou tratamento de erros existentes. A mudança é apenas no mecanismo de paginação.\n5. Mantenha o parâmetro `show` (tamanho da página). Para compensar a perda do paralelismo, considere aumentá-lo até o máximo permitido pela documentação da PipeRun, reduzindo o número total de requisições.\n6. Evite ordenações avançadas/customizadas nas consultas paginadas por cursor. Se o código usa parâmetros de ordenação, sinalize isso na entrega.\n\n## Casos que você deve sinalizar, não resolver silenciosamente\n\n- Código que pula direto para uma página específica (`page=N`): não existe equivalente no cursor, que só avança sequencialmente.\n- Checkpoints que persistem o número da página para retomar uma extração interrompida: o número de página não tem significado no formato novo.\n- Lógica que precisa saber o total de registros ou de páginas antes de iterar.\n\nPara cada caso encontrado, mostre o trecho de código, explique por que ele quebra e proponha uma alternativa (ex.: reiniciar a extração com filtros incrementais por data em vez de retomar por página).\n\n## Entrega esperada\n\n1. O código refatorado completo de todos os trechos alterados.\n2. Um resumo das mudanças: arquivo/função e o que mudou em cada um.\n3. A lista de pontos de atenção sinalizados acima, se existirem.\n</div></code></pre></div></div>\n</details>"
}
[/block]

## 1. **Paginação Antiga (`page`)**

Neste modelo, a navegação entre as páginas era feita de forma sequencial, utilizando o parâmetro `page`. Você informava o número da página que desejava acessar (`page=1`, `page=2`, etc.) para buscar o próximo conjunto de dados. Então você tinha uma requisição assim:

```http
GET /v1/deals?show=10&page=1
```

E recebia esta resposta:

```json
{
  "success": true,
  "message": "OK",
  "data": [{...},{...}],
  "meta": {
    "total": 50,
    "count": 10,
    "per_page": 10,
    "current_page": 1,
    "total_pages": 5,
    "links": {
      "next": "https://api.pipe.run/v1/deals?show=1&page=2"
    }
  }
}
```

Para navegar em todas as paginas você iria iterar pagina a pagina, incrementando o número da pagina até atingir o limite de paginas ou não ter mais dados para serem recuperados. Exemplo de pseudo código em Python:

```python
pagina = 1
while True:
    response = api.get(f"/deals?show=10&page={pagina}")
    // processamento dos dados
    if pagina >= response["meta"]["total_pages"] or not response["data"]:
        break
    pagina += 1
```

***

## 2. **Paginação Nova (`cursor`)**

A paginação por `cursor` oferece um método mais robusto e eficiente, ideal para lidar com grandes conjuntos de dados. Em vez de usar um número de página, você usa o valor de `cursor.next` retornado na resposta da API para buscar a próxima página. Esse `cursor` atua como um "marcador" que aponta para o último item do conjunto de dados anterior, garantindo uma busca contínua e precisa, mesmo que os dados sejam alterados durante a sua navegação. Então, para este novo formato você terá uma requisição assim:

```http
GET /v1/deals?show=10&cursor=
```

> Note que o conteúdo da primeira requisição é uma string vazia, e isto é proposital.

Obtendo uma resposta como a descrita abaixo:

```json
{
  "success": true,
  "message": "OK",
  "data": [{...},{...}],
  "meta": {
    "cursor": {
      "current": null,
      "prev": null,
      "next": "eyJpZCI7NDA2NTA3UTYsIl9wb2ludHNUb05leHRJdGVtRyI6dHJ1ZX0",
      "count": null
    }
  }
}
```

Para navegar em todas as paginas você irá iterar pagina a pagina, usando a hash `cursor.next` da requisição anterior, até ela ser `null` ou não ter mais dados para serem recuperados. Neste exemplo, a segunda requisição seria assim:

```http
GET /v1/deals?show=10&cursor=eyJpZCI7NDA2NTA3UTYsIl9wb2ludHNUb05leHRJdGVtRyI6dHJ1ZX0
```

Exemplo de pseudo código em Python:

```python
cursor = ""  # começa como string vazia
while True:
    response = api.get(f"/deals?show=10&cursor={cursor}")
    // processamento dos dados
    cursor = response["meta"]["cursor"]["next"]
    if not cursor or not response["data"]:
        break
```

***

## 3. **O que muda em relação ao sistema antigo?**

### Diferenças Práticas

| **Antigo**                                              | **Novo**                                                            |
| ------------------------------------------------------- | ------------------------------------------------------------------- |
| Parâmetro `page=N`                                      | Parâmetro `cursor=valor`                                            |
| Pode ir direto à página N                               | Só avança para próxima "página" conhecida                           |
| Possível saber total de páginas/itens                   | Não é possivel saber o total de páginas de imediato                 |
| Gera problemas se os dados mudam no meio do processo    | Cursor aponta diretamente para o ponto certo, reduz esses problemas |
| Muito lento                                             | Muito rápido                                                        |
| Ordenações avançadas são possíveis mas não recomendadas | Ordenações avançadas podem deduplicar dados com itens semelhantes   |

### Por quê mudar?

* **Melhor para grandes volumes**e menos erros se os dados mudam enquanto você percorre
* **Mais escalável** O cursor aponta para um ponto fixo do resultado, e não depende de contagens que podem variar enquanto a lista muda.