Growth Station

Paginação, filtros e ordenação

Percorra listas grandes do GS Engage página por página, filtre por status ou cadência e ordene os resultados — do jeito certo para relatórios de BI.

Paginação, filtros e ordenação

Traga listas grandes do GS Engage para o seu relatório sem perder nenhum registro — página por página, filtrando e ordenando o que importa.

Você quer puxar todos os leads, prospecções ou cadências para uma planilha, um dashboard ou o seu data warehouse. O problema: a API não devolve 5.000 registros de uma vez. Ela entrega em páginas — pedaços menores, um pedido de cada vez.

Esta página mostra como percorrer a lista inteira sem pular nem repetir registro, como filtrar para trazer só o que interessa (por status, por cadência, por busca) e como ordenar o resultado. Se "endpoint" ainda soa técnico, pense nele como um endereço na internet que responde com dados quando você pede.

Para quem é esta página

Se o seu trabalho é montar relatórios e cruzar dados, este é o seu manual de extração. No fim tem um laço de repetição pronto para copiar, que baixa a lista completa respeitando o limite de requisições.

Toda listagem aceita dois controles na URL:

limitopcional

Quantos itens você quer por página. Ex.: limit=50 traz 50 registros por pedido. Um valor maior significa menos pedidos, mas respostas mais pesadas.

pageopcional

Qual página você quer, começando em 1. Ex.: page=2 traz o segundo bloco de resultados.

Você combina os dois na URL, junto com a sua apiKey. Este pedido traz a primeira página com 50 leads:

curl "https://api.gsengage.com/api/v1/leads?limit=50&page=1&apiKey=SUA_CHAVE"

A resposta te diz, no bloco meta, quantos registros existem no total e quantas páginas há — é assim que você sabe até onde ir.

A maioria das listagens responde no formato data + meta: data é a lista de registros daquela página, e meta é o "painel de controle" que informa onde você está e quanto falta.

{
  "data": [
    { "id": "507f1f77bcf86cd799439011", "name": "Maria Silva" },
    { "id": "507f1f77bcf86cd799439012", "name": "João Pereira" }
  ],
  "meta": {
    "limit": 50,
    "count": 2,
    "total": 137,
    "page": 1,
    "totalPages": 3
  }
}

Cada campo do meta tem um papel prático quando você está extraindo dados:

CampoO que significaPara que serve no seu código
limitO tamanho de página que você pediuConfirma quantos itens cabem por página.
countQuantos itens vieram nesta páginaNa última página costuma ser menor que o limit.
totalQuantos itens existem no total (após os filtros)O número real que você espera ter no relatório.
pageEm qual página você estáAjuda a saber "onde estou".
totalPagesQuantas páginas existem ao todoA condição de parada: pare quando page chegar a totalPages.

A regra de ouro do laço

Continue pedindo enquanto page < totalPages. Quando page for igual a totalPages, você baixou tudo. Simples assim.

Quase toda listagem vem embrulhada em data + meta. Uma exceção: GET /api/v1/webhooks traz os itens em data, mas sem meta — retorna todos os webhooks do projeto de uma vez, sem paginação.

webhooks vem sem meta (custom-fields NÃO)

GET /api/v1/webhooks responde { "data": [ ... ] }, sem meta — não tente paginá-lo. Já GET /api/v1/custom-fields é paginado normal ({ data, meta }), como a maioria. Em todos os casos, os itens ficam em data.

Veja a diferença lado a lado. À esquerda, o formato paginado (a maioria, incluindo custom-fields); à direita, o webhooks (sem meta):

Vale para leads, routines, prospections, custom-fields, conversations/threads e as demais listagens.

{
  "data": [
    { "id": "507f1f77bcf86cd799439011", "name": "Maria Silva" }
  ],
  "meta": {
    "limit": 50,
    "count": 1,
    "total": 137,
    "page": 1,
    "totalPages": 3
  }
}

Você lê os registros em data e usa meta.totalPages para saber quando parar.

Vale para GET /api/v1/webhooks.

{
  "data": [
    { "id": "5f0a1b2c3d4e5f6a7b8c9d0e", "name": "Webhook CRM", "url": "https://..." }
  ]
}

Os itens estão em data (igual às outras listagens), mas não há meta nem paginação — a lista já vem completa.

Para decidir a ordem em que os registros voltam, use dois parâmetros:

orderByopcional

O campo pelo qual ordenar (ex.: data de criação, nome). Depende do recurso que você está listando.

orderDirectionopcional

A direção: asc (crescente, do menor/mais antigo para o maior/mais novo) ou desc (decrescente, o inverso).

Este pedido traz os leads do mais recente para o mais antigo:

curl "https://api.gsengage.com/api/v1/leads?orderBy=createdAt&orderDirection=desc&limit=50&page=1&apiKey=SUA_CHAVE"

Ordenar é útil para relatórios incrementais: ordenando por data de criação em desc, os registros novos ficam sempre no topo da primeira página.

Ordenação e paginação andam juntas

Mantenha o mesmo orderBy e orderDirection em todas as páginas de um mesmo laço. Trocar a ordem no meio da varredura embaralha os resultados e faz você pular ou repetir registros.

Filtrar é o que transforma "toda a base" em "só o que eu preciso". Cada listagem aceita filtros próprios, sempre como parâmetros na URL. Aqui estão os principais:

RecursoEndpointFiltros disponíveis
LeadsGET /api/v1/leadssearch, status, sourceIds, routineIds, createdByIds
CadênciasGET /api/v1/routinesstatus, priorities, acquisitionTypes
ProspecçõesGET /api/v1/prospectionsstatus, routineIds, leadId
ConversasGET /api/v1/conversations/threadscanais WHATSAPP e EMAIL

Alguns exemplos concretos:

Buscar um lead por nome, empresa, e-mail ou telefone — o filtro search procura em todos esses campos de uma vez:

curl "https://api.gsengage.com/api/v1/leads?search=maria@empresa.com&apiKey=SUA_CHAVE"

Listar só as prospecções de uma cadência específica — passe o identificador dela em routineIds:

curl "https://api.gsengage.com/api/v1/prospections?routineIds=507f1f77bcf86cd799439011&apiKey=SUA_CHAVE"

Trazer só as prospecções em andamento — filtre por status:

curl "https://api.gsengage.com/api/v1/prospections?status=IN_PROGRESS&apiKey=SUA_CHAVE"

O status usa valores técnicos que têm um rótulo no app. Para prospecções, os valores são:

Valor técnicoRótulo no app
IN_PROGRESSEm andamento
FROZENCongelada
WONGanha
LOSTPerdida

Filtre no servidor, não na planilha

Sempre que der, aplique o filtro na URL em vez de baixar tudo e filtrar depois. Menos dados trafegados, menos páginas para percorrer e menos chance de esbarrar no limite de requisições.

Este é o coração da extração: um laço que começa na página 1 e continua até totalPages, juntando tudo. A lógica em pseudocódigo é:

graph TD
A["página = 1"] --> B["GET /leads?page=página&limit=100"]
B --> C["Acumula resposta.data"]
C --> D{"página < meta.totalPages ?"}
D -->|"Sim"| E["página = página + 1"]
E --> B
D -->|"Não"| F["Fim: lista completa"]

Na prática, em JavaScript, com uma pausa entre pedidos e respeito ao limite de requisições (a API permite 200 leituras por minuto; se estourar, responde 429 com o header Retry-After):

const BASE = "https://api.gsengage.com/api/v1";
const apiKey = process.env.GS_ENGAGE_API_KEY; // nunca deixe a chave no código

// Repete o pedido respeitando o Retry-After quando levar 429
async function callWithBackoff(url) {
  while (true) {
    const res = await fetch(url);
    if (res.status === 429) {
      const wait = Number(res.headers.get("Retry-After") || 5);
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue; // tenta de novo depois de esperar
    }
    return res;
  }
}

// Baixa TODAS as páginas de uma listagem paginada (data + meta)
async function fetchAll(path, filtros = {}) {
  const todos = [];
  let page = 1;
  let totalPages = 1;

  do {
    const params = new URLSearchParams({
      ...filtros,
      limit: "100",
      page: String(page),
      apiKey,
    });
    const res = await callWithBackoff(`${BASE}${path}?${params}`);
    const json = await res.json();

    todos.push(...json.data);        // acumula os registros desta página
    totalPages = json.meta.totalPages; // condição de parada
    page++;
  } while (page <= totalPages);

  return todos;
}

// Exemplo: todas as prospecções em andamento de uma cadência
const prospeccoes = await fetchAll("/prospections", {
  status: "IN_PROGRESS",
  routineIds: "507f1f77bcf86cd799439011",
});
console.log(`Baixei ${prospeccoes.length} prospecções.`);

O mesmo laço em Python:

import os, time, requests

BASE = "https://api.gsengage.com/api/v1"
api_key = os.environ["GS_ENGAGE_API_KEY"]  # nunca deixe a chave no código

def call_with_backoff(url, params):
    while True:
        res = requests.get(url, params=params)
        if res.status_code == 429:
            wait = int(res.headers.get("Retry-After", "5"))
            time.sleep(wait)  # respeita o Retry-After e tenta de novo
            continue
        return res

def fetch_all(path, filtros=None):
    filtros = filtros or {}
    todos, page, total_pages = [], 1, 1

    while page <= total_pages:
        params = {**filtros, "limit": 100, "page": page, "apiKey": api_key}
        res = call_with_backoff(f"{BASE}{path}", params)
        json = res.json()

        todos.extend(json["data"])          # acumula os registros da página
        total_pages = json["meta"]["totalPages"]  # condição de parada
        page += 1

    return todos

# Exemplo: todas as prospecções em andamento de uma cadência
prospeccoes = fetch_all("/prospections", {
    "status": "IN_PROGRESS",
    "routineIds": "507f1f77bcf86cd799439011",
})
print(f"Baixei {len(prospeccoes)} prospecções.")

Ao final, todos (ou prospeccoes) tem a lista completa, pronta para virar linha de planilha ou registro no seu banco.

Esse laço NÃO serve para custom-fields nem webhooks

O fetchAlljson.meta.totalPages e json.data — funciona em todos os endpoints paginados, inclusive custom-fields. A única exceção é GET /api/v1/webhooks, que não tem meta: nele, faça um único GET e leia json.data direto, sem laço.

Fazer
  • Use a meta.totalPages como condição de parada — nunca "chute" o número de páginas.
  • Filtre na URL (status, routineIds, search) para baixar só o necessário.
  • Mantenha o mesmo orderBy/orderDirection em todas as páginas do laço.
  • Respeite o Retry-After quando receber 429 — espere e tente de novo.
Não fazer
  • Não aplique o laço de paginação a custom-fields ou webhooks — eles vêm sem meta.
  • Não dispare todas as páginas em paralelo — você estoura o limite de 200 leituras/min.
  • Não baixe a base inteira só para filtrar depois — deixe o servidor filtrar por você.

Antes de rodar sua extração

0/5
Qual o melhor valor de limit?

Depende do equilíbrio entre peso e número de pedidos. Valores como 50 ou 100 costumam funcionar bem: poucos pedidos e respostas ainda leves. Se o total for muito grande, um limit maior reduz o número de páginas — só lembre de continuar respeitando o limite de 200 leituras por minuto.

Por que a última página traz menos itens que o limit?

Porque os registros raramente terminam num número redondo. Se há 137 itens e limit=50, as duas primeiras páginas trazem 50 cada e a terceira traz 37. Nesse caso, o count da última página vem 37, menor que o limit.

Filtrei e o total mudou. Está certo?

Sim. O meta.total reflete a contagem depois dos filtros. Ao aplicar status=IN_PROGRESS, o total passa a contar só as prospecções em andamento — é o número que você quer no relatório.

Como percorro custom-fields ou webhooks então?

Você não percorre: faz um único GET /api/v1/custom-fields (ou /webhooks) e usa a resposta como uma lista direta. Não há page, limit nem meta — tudo vem de uma vez.

Posso combinar filtro, ordenação e paginação no mesmo pedido?

Pode, e é o uso normal. Basta juntar todos os parâmetros na URL: ?status=IN_PROGRESS&orderBy=createdAt&orderDirection=desc&limit=100&page=1&apiKey=SUA_CHAVE.

Este artigo foi útil?

Nesta página

Última atualização