# Extrair prospecções e atividades para um dashboard

> Puxe suas prospecções e atividades do GS Engage de forma paginada para montar um dashboard de funil e calcular sua taxa de conversão.

- URL canônica: https://docs.growthstation.app/docs/api/guias/extrair-para-dashboard
- Idioma: pt-BR
- Última atualização: 2026-09-20T08:21:20.450Z
- Produto: GS Engage
- Mantido por: Produto GS Engage



<PageHero emoji="📊" title="Extrair prospecções e atividades para um dashboard" description="Puxe suas prospecções e o histórico de atividades do GS Engage de forma paginada e confiável para montar um painel de funil e calcular a taxa de conversão." gradient="brand" />

Este guia percorre a extração de ponta a ponta: listar as prospecções, paginar sem perder registros, traduzir o status técnico para o vocabulário de vendas e calcular a conversão pronta para carregar no seu BI.

🎯 O que você vai conseguir [#-o-que-você-vai-conseguir]

Você é o Rafael, cuida de BI, e o time de vendas quer enxergar o funil: quantas prospecções estão rodando, quantas foram ganhas, quantas foram perdidas — e qual a **taxa de conversão**. Esse número não mora num relatório pronto: você precisa **extrair os dados brutos** do GS Engage e montar o painel do seu jeito (Metabase, Power BI, Looker Studio, uma planilha, o que for).

Este guia mostra como puxar todas as suas prospecções e, quando precisar, o histórico de atividades de cada uma — de forma **paginada e confiável** — para alimentar um dashboard.

<Callout type="tip" title="Em uma frase">
  Você lista as prospecções com `GET /api/v1/prospections`, percorre todas as páginas, traduz o status técnico para o vocabulário de vendas e calcula a conversão dividindo as Ganhas pelo total que já foi decidido.
</Callout>

🧩 Os dois endpoints que alimentam o painel [#-os-dois-endpoints-que-alimentam-o-painel]

Um **endpoint** é só um endereço da API que devolve dados. Para montar o dashboard de funil, você usa dois:

| Endpoint                                   | O que devolve                                                      | Quando usar                                           |
| ------------------------------------------ | ------------------------------------------------------------------ | ----------------------------------------------------- |
| `GET /api/v1/prospections`                 | A lista de prospecções, com o status de cada uma                   | Base do funil — é daqui que sai a conversão           |
| `GET /api/v1/prospections/{id}/activities` | As atividades (ligações, mensagens, e-mails) de **uma** prospecção | Detalhe: esforço por prospecção, cadência de trabalho |

Na prática: comece sempre pela lista de prospecções. Só desça para as atividades quando o painel precisar detalhar o que aconteceu dentro de cada prospecção específica.

<Callout type="warning" title="Toda chamada é real (não há sandbox)">
  O GS Engage não tem ambiente de teste separado. Mas fique tranquilo: os dois endpoints deste guia são **somente leitura** (GET). Eles não alteram nada — apenas leem seus dados de produção. É seguro experimentar à vontade.
</Callout>

<Callout type="danger" title="Sua apiKey é uma senha">
  A `apiKey` (cerca de 40 caracteres, criada em <TextHighlight>Configurações > Configurações de API</TextHighlight>) vai na URL como parâmetro `?apiKey=...`. Isso significa que ela pode aparecer em logs de servidor, no histórico do navegador e em links compartilhados. Guarde em variável de ambiente e **nunca** cole em canais públicos — nem numa consulta salva do seu BI que outras pessoas veem.
</Callout>

🗣️ Traduzindo o status para o vocabulário de vendas [#️-traduzindo-o-status-para-o-vocabulário-de-vendas]

A API devolve o status de cada prospecção como um código técnico em inglês. O time de vendas não fala essa língua — ele fala "Ganha", "Perdida". Antes de montar qualquer gráfico, faça essa tradução na sua camada de dados. É simples e direto:

| Valor na API (`status`) | Rótulo que o cliente vê no app | O que significa                                            |
| ----------------------- | ------------------------------ | ---------------------------------------------------------- |
| `IN_PROGRESS`           | **Em andamento**               | Prospecção ativa; o vendedor ainda está trabalhando o lead |
| `FROZEN`                | **Congelada**                  | Pausada temporariamente; nada acontece até ser retomada    |
| `WON`                   | **Ganha**                      | Deu certo — o lead foi convertido                          |
| `LOST`                  | **Perdida**                    | Não deu certo; a prospecção foi encerrada sem venda        |

<Callout type="tip" title="Faça a tradução uma vez só">
  Guarde esse mapeamento em uma tabela de referência (ou num `CASE WHEN` na sua query) e reaproveite em todos os gráficos. Assim o painel inteiro fala a mesma língua do vendedor, e você nunca mostra `IN_PROGRESS` cru para o time comercial.
</Callout>

📐 Como calcular a taxa de conversão [#-como-calcular-a-taxa-de-conversão]

Conversão é a pergunta central do funil: **de tudo que foi decidido, quanto virou venda?** A conta usa só dois status — `WON` e `LOST` — porque prospecções `IN_PROGRESS` e `FROZEN` ainda **não têm desfecho** e distorceriam o número se entrassem na base.

```
Taxa de conversão = Ganhas (WON) ÷ (Ganhas (WON) + Perdidas (LOST))
```

Exemplo concreto: em um período você tem 120 `WON` e 380 `LOST`. As `IN_PROGRESS` e `FROZEN` ficam de fora do cálculo.

```
120 ÷ (120 + 380) = 120 ÷ 500 = 0,24 → 24% de conversão
```

<DoDont>
  <DoDontItem type="do">
    Considere apenas prospecções já decididas (WON + LOST) no denominador da conversão.
  </DoDontItem>

  <DoDontItem type="dont">
    Não jogue IN_PROGRESS ou FROZEN no denominador — elas ainda podem virar Ganha ou Perdida e derrubariam artificialmente a taxa.
  </DoDontItem>

  <DoDontItem type="do">
    Mostre também um card separado com quantas prospecções seguem Em andamento — é o funil "vivo".
  </DoDontItem>

  <DoDontItem type="dont">
    Não confunda "total de prospecções" com "total decidido"; são bases diferentes.
  </DoDontItem>
</DoDont>

🔎 Filtrando o que entra no painel [#-filtrando-o-que-entra-no-painel]

O `GET /api/v1/prospections` aceita filtros que você pode combinar com a paginação. Use-os para reduzir o volume e focar só no recorte que o dashboard precisa.

<FieldInfoGroup>
  <FieldInfo title="status">
    Traz só as prospecções em um estado específico. Aceita os valores técnicos: `IN_PROGRESS`, `FROZEN`, `WON`, `LOST`. Útil, por exemplo, para um card que conta apenas as Ganhas.
  </FieldInfo>

  <FieldInfo title="routineIds">
    Limita a uma ou mais **Cadências** (as sequências de atividades que o vendedor executa). Ideal para comparar a conversão entre cadências diferentes.
  </FieldInfo>

  <FieldInfo title="leadId">
    Traz as prospecções de **um lead** específico. Serve mais para telas de detalhe do que para o funil geral.
  </FieldInfo>
</FieldInfoGroup>

<Callout type="info" title="Recorte por status ou puxe tudo?">
  Para calcular conversão você precisa de `WON` e `LOST` juntos. Você pode fazer duas chamadas (uma com `status=WON`, outra com `status=LOST`) e usar o campo `total` de cada uma — é a forma mais barata de obter as contagens. Ou puxar tudo sem filtro de status e agrupar do seu lado. As duas abordagens funcionam; a primeira gasta menos requisições.
</Callout>

📄 Paginando corretamente [#-paginando-corretamente]

A lista de prospecções não vem toda de uma vez — ela vem **paginada**. A resposta tem sempre dois blocos: `data` (o pedaço de registros daquela página) e `meta` (as informações que dizem onde você está e quanto falta).

```json
{
  "data": [
    { "id": "...", "status": "WON", "leadId": "...", "createdAt": "2026-07-10T14:03:00Z" }
  ],
  "meta": {
    "limit": 50,
    "count": 50,
    "total": 1240,
    "page": 1,
    "totalPages": 25
  }
}
```

O que cada campo de `meta` significa para você:

| Campo        | O que é                                                            |
| ------------ | ------------------------------------------------------------------ |
| `limit`      | Quantos registros por página você pediu                            |
| `count`      | Quantos vieram nesta página (pode ser menor que `limit` na última) |
| `total`      | O total de registros que batem com seus filtros                    |
| `page`       | Em que página você está agora (começa em 1)                        |
| `totalPages` | Quantas páginas existem no total                                   |

A regra de ouro da paginação: **continue pedindo a próxima página enquanto `page < totalPages`**. Você controla a página com o parâmetro `page` e o tamanho com `limit`.

<Callout type="warning" title="Nem todo endpoint tem meta">
  A maioria dos endpoints paginados segue o formato `{ data, meta }`. Só `GET /api/v1/webhooks` vem **sem `meta`** (ainda com os itens em `data`). Para os deste guia (`prospections` e as `activities`), você pode contar com o `meta`.
</Callout>

🕒 Dica de ordenação: sempre por createdAt [#-dica-de-ordenação-sempre-por-createdat]

Ao paginar uma extração, você precisa de uma ordem **estável** — senão registros novos entrando durante a leitura podem fazer você pular ou repetir linhas entre páginas. Ordene explicitamente por data de criação usando os parâmetros `orderBy` e `orderDirection` (`asc` ou `desc`):

```
GET /api/v1/prospections?apiKey=SUA_CHAVE&orderBy=createdAt&orderDirection=asc&limit=100&page=1
```

<Callout type="tip" title="Por que asc por createdAt">
  Ordenar do mais antigo para o mais novo (`asc`) deixa a base histórica no começo e os registros recentes no fim. Para extrações incrementais (só o que é novo desde a última carga), isso facilita: você lê até alcançar registros mais recentes que a sua última sincronização e para por ali.
</Callout>

🚀 Exemplo de extração paginada [#-exemplo-de-extração-paginada]

Este é o coração do guia: um loop que percorre **todas as páginas** de prospecções e junta tudo numa lista única, pronta para carregar no seu dashboard. Troque `SUA_CHAVE` pela sua chave (de preferência via variável de ambiente).

<Tabs items={['Python', 'Node.js', 'cURL']}>
  <Tab value="Python">
    ```python
    import os
    import time
    import requests

    api_key = os.environ["GS_API_KEY"]  # guarde a chave em variável de ambiente
    url = "https://api.gsengage.com/api/v1/prospections"

    def fetch_all_prospections():
        todas = []
        page = 1
        while True:
            resp = requests.get(url, params={
                "apiKey": api_key,
                "limit": 100,
                "page": page,
                "orderBy": "createdAt",
                "orderDirection": "asc",
            })

            # Respeita o rate limit (200 leituras/min)
            if resp.status_code == 429:
                espera = int(resp.headers.get("Retry-After", "5"))
                time.sleep(espera)
                continue

            resp.raise_for_status()
            body = resp.json()
            todas.extend(body["data"])

            meta = body["meta"]
            if meta["page"] >= meta["totalPages"]:
                break
            page += 1

        return todas

    prospeccoes = fetch_all_prospections()
    print(f"Extraídas {len(prospeccoes)} prospecções")

    # Traduz o status e calcula a conversão
    rotulos = {
        "IN_PROGRESS": "Em andamento",
        "FROZEN": "Congelada",
        "WON": "Ganha",
        "LOST": "Perdida",
    }
    ganhas = sum(1 for p in prospeccoes if p["status"] == "WON")
    perdidas = sum(1 for p in prospeccoes if p["status"] == "LOST")
    decididas = ganhas + perdidas
    conversao = (ganhas / decididas) if decididas else 0
    print(f"Ganhas: {ganhas} | Perdidas: {perdidas} | Conversão: {conversao:.1%}")
    ```
  </Tab>

  <Tab value="Node.js">
    ```js
    const apiKey = process.env.GS_API_KEY; // guarde a chave em variável de ambiente
    const baseUrl = "https://api.gsengage.com/api/v1/prospections";

    async function fetchAllProspections() {
      const todas = [];
      let page = 1;

      while (true) {
        const params = new URLSearchParams({
          apiKey,
          limit: "100",
          page: String(page),
          orderBy: "createdAt",
          orderDirection: "asc",
        });

        const resp = await fetch(`${baseUrl}?${params}`);

        // Respeita o rate limit (200 leituras/min)
        if (resp.status === 429) {
          const espera = Number(resp.headers.get("Retry-After") ?? 5);
          await new Promise((r) => setTimeout(r, espera * 1000));
          continue;
        }

        if (!resp.ok) throw new Error(`Erro ${resp.status}`);

        const body = await resp.json();
        todas.push(...body.data);

        const { page: atual, totalPages } = body.meta;
        if (atual >= totalPages) break;
        page += 1;
      }

      return todas;
    }

    const prospeccoes = await fetchAllProspections();
    const ganhas = prospeccoes.filter((p) => p.status === "WON").length;
    const perdidas = prospeccoes.filter((p) => p.status === "LOST").length;
    const decididas = ganhas + perdidas;
    const conversao = decididas ? ganhas / decididas : 0;
    console.log(`Ganhas: ${ganhas} | Perdidas: ${perdidas} | Conversão: ${(conversao * 100).toFixed(1)}%`);
    ```
  </Tab>

  <Tab value="cURL">
    No terminal, você busca página por página. Primeiro a página 1, olhe o `meta.totalPages` da resposta e repita incrementando `page` até chegar lá:

    ```bash
    # Página 1 — veja meta.totalPages na resposta
    curl "https://api.gsengage.com/api/v1/prospections?apiKey=SUA_CHAVE&orderBy=createdAt&orderDirection=asc&limit=100&page=1"

    # Página 2, 3, ... até page == totalPages
    curl "https://api.gsengage.com/api/v1/prospections?apiKey=SUA_CHAVE&orderBy=createdAt&orderDirection=asc&limit=100&page=2"
    ```

    Para uma extração de verdade, prefira o exemplo em Python ou Node — o loop cuida de percorrer todas as páginas sozinho.
  </Tab>
</Tabs>

O que a resposta significa para o negócio: com essa lista completa em mãos, você tem o **funil inteiro** — dá para contar quantas prospecções estão em cada estado, calcular a conversão do período e quebrar por cadência (`routineIds`) ou por vendedor, dependendo dos campos que o seu painel expõe.

🔬 Detalhando as atividades de uma prospecção [#-detalhando-as-atividades-de-uma-prospecção]

Quando o painel precisar ir além do funil — por exemplo, medir **quanto esforço** cada prospecção exigiu (número de ligações, mensagens, e-mails) — você desce para as atividades de uma prospecção específica:

```bash
curl "https://api.gsengage.com/api/v1/prospections/ID_DA_PROSPECCAO/activities?apiKey=SUA_CHAVE&limit=100&page=1"
```

Essa resposta também é paginada no formato `{ data, meta }` — use exatamente o mesmo loop do exemplo acima para percorrer todas as páginas. Como você faria uma chamada por prospecção, use isso com parcimônia: puxe as atividades só das prospecções que o dashboard realmente detalha, para não estourar o limite de requisições.

<Callout type="warning" title="Cuidado com o rate limit ao descer para atividades">
  São **200 requisições de leitura por minuto** (janela de 60s). Se você buscar as atividades de milhares de prospecções em sequência, vai bater no limite e receber **429**. A resposta traz o header `Retry-After` (em segundos) — espere esse tempo antes de tentar de novo, de preferência com **backoff exponencial** (dobrando a espera a cada nova falha).
</Callout>

🗺️ O fluxo da extração em um diagrama [#️-o-fluxo-da-extração-em-um-diagrama]

<Mermaid
  chart={`graph TD
A["Inicia extracao page=1"] --> B["GET /prospections?page=N"]
B --> C["Junta body.data na lista"]
C --> D{"meta.page < meta.totalPages?"}
D -->|Sim| E["page = page + 1"]
E --> B
D -->|Nao| F["Lista completa em maos"]
F --> G["Traduz status e calcula conversao"]
G --> H["Carrega no dashboard de funil"]`}
/>

✅ Checklist do BI antes de publicar o painel [#-checklist-do-bi-antes-de-publicar-o-painel]

<Checklist id="dashboard-extracao" title="Antes de subir o dashboard">
  <ChecklistItem>
    Percorri 

    **todas**

     as páginas (

    `page`

     até 

    `totalPages`

    ), não só a primeira?
  </ChecklistItem>

  <ChecklistItem>
    Ordenei por 

    `createdAt`

     com 

    `orderDirection`

     fixo para a paginação ficar estável?
  </ChecklistItem>

  <ChecklistItem>
    Traduzi 

    `IN_PROGRESS`

    /

    `FROZEN`

    /

    `WON`

    /

    `LOST`

     para os rótulos que o vendedor vê?
  </ChecklistItem>

  <ChecklistItem>
    Calculei a conversão só sobre as decididas (

    `WON`

     \+ 

    `LOST`

    ), sem incluir as em aberto?
  </ChecklistItem>

  <ChecklistItem>
    Tratei o 

    **429**

     respeitando o 

    `Retry-After`

     com backoff?
  </ChecklistItem>

  <ChecklistItem>
    A 

    `apiKey`

     está em variável de ambiente, e não colada numa query salva compartilhada?
  </ChecklistItem>
</Checklist>

🩹 Erros comuns e como resolver [#-erros-comuns-e-como-resolver]

As mensagens de erro do GS Engage já vêm em português — leia sempre o campo `message` da resposta. Os tropeços mais frequentes nessa extração:

<FieldInfoGroup>
  <FieldInfo title="401 Unauthorized — apiKey ausente ou inválida">
    **O que houve:** a chave não foi enviada ou está errada. **Por quê:** faltou o `?apiKey=...` na URL, ou a chave foi revogada. **Como corrigir:** confira o parâmetro na URL e valide a chave com uma chamada leve, como `GET /api/v1/custom-fields` — um 200 confirma que ela funciona.
  </FieldInfo>

  <FieldInfo title="429 Too Many Requests — passou do limite de leitura">
    **O que houve:** você fez mais de 200 requisições de leitura em 60 segundos. **Por quê:** paginação ou busca de atividades rápida demais, sem pausa. **Como corrigir:** leia o header `Retry-After` (segundos), espere esse tempo e retome com backoff exponencial. Os headers `X-RateLimit-Remaining` e `X-RateLimit-Reset` ajudam a antecipar o momento de desacelerar.
  </FieldInfo>

  <FieldInfo title="Só vieram 50 registros de milhares — paginação incompleta">
    **O que houve:** o painel mostra menos prospecções do que deveria. **Por quê:** você leu só a primeira página e ignorou o `meta.totalPages`. **Como corrigir:** implemente o loop de paginação — continue enquanto `meta.page < meta.totalPages`.
  </FieldInfo>
</FieldInfoGroup>

❓ Perguntas frequentes [#-perguntas-frequentes]

<FAQ>
  <FAQItem question="Preciso incluir as prospecções Congeladas (FROZEN) na conversão?">
    Não. Assim como `IN_PROGRESS`, uma prospecção `FROZEN` ainda não tem desfecho — ela pode ser retomada e virar Ganha ou Perdida. A conversão usa só `WON` e `LOST`. Mostre as Congeladas em um card à parte, se o time quiser acompanhá-las.
  </FAQItem>

  <FAQItem question="Qual o melhor valor de limit para extrair mais rápido?">
    Um `limit` maior (por exemplo, 100) reduz o número de requisições para percorrer tudo, o que ajuda a não bater no rate limit. Comece com 100 e ajuste se precisar. Lembre que a última página normalmente vem com `count` menor que o `limit` — isso é esperado.
  </FAQItem>

  <FAQItem question="Como faço uma carga incremental, só do que é novo?">
    Ordene por `createdAt` em ordem crescente (`asc`) e guarde a data do último registro que você já carregou. Na próxima extração, percorra as páginas até alcançar registros com `createdAt` mais recente que essa marca — e ignore o que você já tinha. Assim você não reprocessa a base inteira todo dia.
  </FAQItem>

  <FAQItem question="Consigo fazer isso sem escrever código, direto no meu BI?">
    Ferramentas como Make e n8n conseguem consumir a API REST e cuidar da paginação em um fluxo visual, entregando os dados para o seu destino. Muitos BIs também importam de um conector HTTP. O importante é que qualquer que seja a ferramenta, ela precise respeitar a paginação `{ data, meta }` e o rate limit.
  </FAQItem>
</FAQ>

Artigos Relacionados [#artigos-relacionados]

<RelatedArticles>
  <RelatedArticle href="/docs/api/conceitos/glossario" title="Glossário de status e enums" description="A tabela completa dos status da prospecção e dos demais enums que o app usa." />

  <RelatedArticle href="/docs/api/guias/avisos-de-venda-webhook" title="Receber avisos de venda por webhook" description="Em vez de puxar, seja avisado em tempo real quando uma prospecção é ganha ou perdida." />

  <RelatedArticle href="/docs/api/guias/adicionar-lead-cadencia" title="Adicionar um lead e iniciar uma cadência" description="Como as prospecções nascem: cadastrar o lead e dar o start no trabalho do vendedor." />

  <RelatedArticle href="/docs/api/referencia" title="Referência da API" description="A lista completa de endpoints, filtros e formatos de resposta." />
</RelatedArticles>
