API para desenvolvedores
Versão 1 · só leitura · planos Agência e Escala
Leve os dados da sua agência para o Looker Studio, Google Sheets, Excel, n8n, Make ou o seu sistema: clientes, fichas, medições do geogrid, Ranqal Score, avaliações, visibilidade em IA, plano de ação e leads. A API só lê: ela nunca altera dados nem dispara coletas pagas.
Referência completa · openapi.yaml · Webhooks · MCP (Claude e Cursor)
Começo rápido
- No Ranqal, o Dono ou um Admin abre Configurações › Desenvolvedores, dá um nome à chave e clica em Criar chave.
- Copie a chave na hora: ela aparece uma vez só.
- Teste:
curl -H "Authorization: Bearer rq_live_SUA_CHAVE" \ https://app.ranqal.com.br/api/v1/me
Depois, liste as fichas:
curl -H "Authorization: Bearer rq_live_SUA_CHAVE" \ "https://app.ranqal.com.br/api/v1/businesses?limit=100"
Autenticação e escopos
Envie a chave no header Authorization: Bearer rq_live_... em toda chamada. Nunca na URL: chamada com a chave na URL é recusada. A chave dá acesso aos dados da agência inteira, então guarde-a como uma senha (num cofre ou nas credenciais da ferramenta) e revogue a que vazar.
| Escopo | Libera |
|---|---|
read | Todas as rotas, menos /usage. |
usage:read | /usage: plano, uso e gasto do mês. |
Só Dono e Admin criam e revogam chaves. A chave pode ter validade (30 dias, 90 dias ou 1 ano). Chave revogada ou vencida recebe 401 na hora. A agência vem sempre da chave: não há como pedir dados de outra agência.
Paginação, erros e limites
Paginação
Listas devolvem data e next_cursor. Use limit (1 a 100, padrão 50) e passe o next_cursor em cursor para a próxima página. next_cursor: null = acabou.
{
"data": [ { "id": "6f1c...", "name": "Clínica Sorriso", "rating": 4.8, ... } ],
"next_cursor": "WyJjbGluaWNhIHNvcnJpc28iLCI2ZjFj..."
}curl -H "Authorization: Bearer rq_live_SUA_CHAVE" \ "https://app.ranqal.com.br/api/v1/businesses?limit=100&cursor=WyJjbGluaWNhIHNvcnJpc28iLCI2ZjFj..."
CSV
Toda lista aceita format=csv (UTF-8, vírgula). O cursor da próxima página vem no header X-Next-Cursor. Campos com listas viram JSON na célula.
Datas
Datas saem em ISO 8601 (UTC). O filtro since aceita 2026-10-01 ou data e hora ISO.
Erros
{ "error": { "code": "rate_limited", "message": "Limite de 60 chamadas por minuto e 5000 por dia. Tente de novo em 17 s." } }| HTTP | code | Quando |
|---|---|---|
| 400 | validation | Parâmetro inválido (limit, cursor, since...) ou chave na URL. |
| 401 | invalid_key | Chave ausente, inválida, revogada ou vencida. |
| 403 | insufficient_scope | A chave não tem o escopo da rota. |
| 403 | plan_required | O plano da conta não inclui a API. |
| 403 | plan_blocked | O plano está bloqueado (pagamento atrasado ou cancelado). |
| 404 | not_found | Não existe nesta agência. |
| 429 | rate_limited | Passou do limite. Espere os segundos do header Retry-After. |
Limites
60 chamadas por minuto e 5.000 por dia, por chave. Cada resposta traz X-RateLimit-Remaining. Acima do limite: 429 com Retry-After em segundos.
Referência
Base: https://app.ranqal.com.br/api/v1. Todas as rotas são GET. Campos e exemplos na referência completa.
| Rota | O que traz |
|---|---|
/me | Agência, chave, plano e limites. Bom primeiro teste. |
/clients | Clientes da agência. |
/businesses?client_id= | Fichas do Google (todas ou de um cliente). |
/businesses/{id} | Uma ficha. |
/businesses/{id}/score?since= | Histórico do Ranqal Score. |
/businesses/{id}/scans | Medições (geogrid) com as métricas por palavra-chave. |
/scans/{id} | Uma medição com a grade ponto a ponto e os 10 primeiros concorrentes. |
/businesses/{id}/reviews?since= | Avaliações do Google (sem dados do perfil de quem avaliou). Até as 1.000 mais recentes por consulta; use since para as antigas. |
/businesses/{id}/ai-visibility | Respostas dos assistentes de IA: citou a ficha? em que posição? |
/businesses/{id}/actions | Plano de ação da ficha. |
/leads?since= | Leads do teste grátis no seu site e testes de prospecção. |
/usage | Plano, limites x uso e gasto do mês. Escopo usage:read. |
Receita: n8n → Google Sheets → Looker Studio
Score das fichas toda manhã numa planilha, com gráfico no Looker Studio.
- No n8n, crie a credencial Header Auth: nome
Authorization, valorBearer rq_live_SUA_CHAVE. - Schedule Trigger: todo dia às 7h.
- HTTP Request: GET
https://app.ranqal.com.br/api/v1/businesses?limit=100, autenticação Header Auth. Em Options › Pagination:
Pagination Mode: Update a Parameter in Each Request
Type: Query Name: cursor Value: {{ $response.body.next_cursor }}
Pagination Complete When: Other
Complete Expression: {{ !$response.body.next_cursor }}- Split Out no campo
data(uma ficha por item). - HTTP Request: GET
https://app.ranqal.com.br/api/v1/businesses/{{ $json.id }}/score?limit=1. Em Options › Batching, 1 item a cada 1.100 ms para ficar abaixo de 60 por minuto. - Split Out em
datae Google Sheets › Append Row com ficha, data (computed_at), nota (score) e faixa (band). - No Looker Studio: Adicionar dados › Planilhas Google, escolha a planilha e crie um gráfico de série temporal com a data, a nota e a ficha como dimensão.
Receita: Excel com Power Query (CSV)
- Excel: Dados › Obter Dados › De Outras Fontes › Consulta Nula e abra o Editor Avançado.
- Cole o código abaixo, troque a chave e clique em Concluído. Ele percorre todas as páginas.
- Se o Excel pedir credencial para o endereço, escolha Anônimo (a chave já vai no header).
- Para outra lista, troque o fim de
Url(ex.:/leads). Atualize com Dados › Atualizar Tudo.
let
Chave = "rq_live_SUA_CHAVE",
Url = "https://app.ranqal.com.br/api/v1/businesses",
Pagina = (cursor as nullable text) =>
let
Query = [format = "csv", limit = "100"] & (if cursor = null then [] else [cursor = cursor]),
Resp = Web.Contents(Url, [Query = Query, Headers = [Authorization = "Bearer " & Chave]]),
H = Value.Metadata(Resp)[Headers],
Proximo = Record.FieldOrDefault(H, "X-Next-Cursor", Record.FieldOrDefault(H, "x-next-cursor", null)),
Tabela = Table.PromoteHeaders(Csv.Document(Resp, [Delimiter = ",", Encoding = 65001, QuoteStyle = QuoteStyle.Csv]))
in
[Tabela = Tabela, Proximo = Proximo],
Paginas = List.Generate(() => Pagina(null), each _ <> null, each if [Proximo] = null then null else Pagina([Proximo])),
Fichas = Table.Combine(List.Transform(Paginas, each [Tabela]))
in
FichasA chave fica dentro da pasta de trabalho: não compartilhe o arquivo com quem não deve ter acesso aos dados da agência.
Webhooks
Em vez de perguntar à API de tempos em tempos, receba um aviso na hora: o Ranqal faz um POST com JSON no seu endereço (n8n, Make, Zapier ou o seu servidor) quando chega um lead, um teste termina ou uma medição fica pronta. Mesmos planos da API (Agência: 1 endpoint; Escala: 5).
- Em Configurações › Desenvolvedores, o Dono ou um Admin cadastra o endereço (
https://) e escolhe os eventos. - Copie o segredo (
whsec_...): ele aparece uma vez só. Serve para conferir que o envio veio do Ranqal. - Clique em Enviar evento de teste e confira que chegou.
Eventos
| Evento | Quando | data |
|---|---|---|
lead.created | Chegou um lead pelo teste grátis no seu site ou a equipe criou um teste de prospecção. | O lead, igual a um item de /leads. |
test.completed | O teste do lead/prospect terminou (test_status done, com report_url) ou deu erro (test_status error). | O lead + summary (nota, avaliações, mapa, IA e ações do resultado). |
scan.completed | Uma medição (geogrid) de ficha de cliente terminou (succeeded) ou falhou (failed). | A medição, igual a um item de /businesses/{id}/scans, + business. Grade completa em /scans/{id}. |
webhook.test | Você clicou em Enviar evento de teste. | Uma mensagem e o id do endpoint. |
Todo envio tem o mesmo formato: { id, type, created_at, data }. Os campos de data são os mesmos da API (veja a referência, seção Webhooks).
POST /seu-endereco HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: Ranqal-Webhooks/1.0
X-Ranqal-Event: lead.created
X-Ranqal-Event-Id: 3f6b2a0e-8d1c-4d55-9a51-2a3f7c9e1b20
X-Ranqal-Signature: t=1760965451,v1=5f2b0c9e8d7a...
X-Ranqal-Attempt: 1
{
"id": "3f6b2a0e-8d1c-4d55-9a51-2a3f7c9e1b20",
"type": "lead.created",
"created_at": "2026-10-21T13:04:11.120Z",
"data": {
"id": "9a1c7e44-0b7d-4c1e-a0b5-6c2d3e4f5a6b",
"source": "site",
"business_name": "Padaria Pão Bom",
"city": "Campinas",
"keyword": "padaria",
"name": "Ana Souza",
"email": "ana@padariapaobom.com.br",
"phone": "+5519999990000",
"page": "https://suaagencia.com.br",
"status": "new",
"test_status": "queued",
"report_url": null,
"created_at": "2026-10-21T13:04:10.981Z"
}
}test.completed:
{
"id": "0d9e8f7a-6b5c-4d3e-8f1a-2b3c4d5e6f70",
"type": "test.completed",
"created_at": "2026-10-21T13:09:42.507Z",
"data": {
"id": "9a1c7e44-0b7d-4c1e-a0b5-6c2d3e4f5a6b",
"source": "site",
"business_name": "Padaria Pão Bom",
"name": "Ana Souza",
"email": "ana@padariapaobom.com.br",
"phone": "+5519999990000",
"test_status": "done",
"report_url": "https://app.ranqal.com.br/diagnostico/padaria-pao-bom-Xk2m9QaZ7pL4sT1v",
"summary": {
"rating": 4.6,
"reviews_count": 212,
"map": { "top3_pct": 18.4, "coverage_pct": 61.2, "avg_rank": 7.3 },
"ai": { "cited": 1, "total": 4 },
"actions": ["Responder as avaliações sem resposta", "Completar os horários da ficha"]
},
...
}
}scan.completed:
{
"id": "6c5b4a39-2817-4f60-9e8d-7c6b5a493827",
"type": "scan.completed",
"created_at": "2026-10-21T14:30:05.004Z",
"data": {
"id": "1e2d3c4b-5a69-4788-9a0b-c1d2e3f40516",
"business_id": "6f1c2b3a-4d5e-4f60-8a7b-9c0d1e2f3a4b",
"status": "succeeded",
"finished_at": "2026-10-21T14:30:04.880Z",
"grid_size": 7,
"radius_km": 3,
"keywords": [
{ "keyword": "dentista campinas", "top3": 12, "top10": 30, "arp": 5.8, "solv_pct": 24.5, "coverage_pct": 83.7, ... }
],
"business": { "id": "6f1c2b3a-...", "name": "Clínica Sorriso", "client_id": "2a3b4c5d-...", "client_name": "Clínica Sorriso" }
}
}Conferir a assinatura
O header X-Ranqal-Signature vem como t=<unix>,v1=<hex>, em que v1 é o HMAC-SHA256, com o seu segredo, do texto <t>.<corpo cru>. Recalcule, compare e recuse se não bater ou se t tiver mais de 5 minutos de diferença do seu relógio (protege contra alguém reenviar uma requisição capturada). Use o corpo exatamente como chegou, antes de converter o JSON.
Node.js (Express)
import crypto from "node:crypto";
import express from "express";
const SEGREDO = process.env.RANQAL_WEBHOOK_SECRET; // whsec_...
const app = express();
// corpo CRU: a assinatura e sobre os bytes recebidos, antes do JSON.parse
app.post("/ranqal", express.raw({ type: "application/json" }), (req, res) => {
const corpo = req.body.toString("utf8");
const partes = Object.fromEntries(
(req.get("X-Ranqal-Signature") || "").split(",").map((p) => p.split("=")),
);
const t = Number(partes.t);
const esperado = crypto.createHmac("sha256", SEGREDO).update(`${t}.${corpo}`).digest("hex");
const valido =
typeof partes.v1 === "string" &&
partes.v1.length === esperado.length &&
crypto.timingSafeEqual(Buffer.from(partes.v1), Buffer.from(esperado)) &&
Math.abs(Date.now() / 1000 - t) < 300; // no maximo 5 minutos
if (!valido) return res.status(401).send("assinatura inválida");
const evento = JSON.parse(corpo);
// idempotencia: se evento.id ja foi processado, so responda 200
res.status(200).send("ok"); // responda em ate 10 s; trabalho pesado vai para uma fila sua
});
app.listen(3000);n8n (Code node)
// Code node (JavaScript), logo depois do Webhook node.
// No Webhook node: HTTP Method POST, Respond "Immediately", Options > Raw Body ligado.
// n8n auto-hospedado: libere o modulo com NODE_FUNCTION_ALLOW_BUILTIN=crypto
const crypto = require("crypto");
const SEGREDO = "whsec_SEU_SEGREDO"; // ou $env.RANQAL_WEBHOOK_SECRET
const item = $input.first();
const corpo = (await this.helpers.getBinaryDataBuffer(0, "data")).toString("utf8");
const partes = Object.fromEntries(
String(item.json.headers["x-ranqal-signature"] || "").split(",").map((p) => p.split("=")),
);
const t = Number(partes.t);
const esperado = crypto.createHmac("sha256", SEGREDO).update(t + "." + corpo).digest("hex");
if (partes.v1 !== esperado || Math.abs(Date.now() / 1000 - t) > 300) {
throw new Error("Assinatura do Ranqal inválida");
}
const evento = JSON.parse(corpo);
return [{ json: { ...evento, event_id: item.json.headers["x-ranqal-event-id"] } }];Sem o Raw Body ligado, troque a linha do corpo por JSON.stringify($json.body): funciona porque o Ranqal envia JSON compacto, sem espaços. Ao trocar o segredo (botão Trocar segredo), o antigo para de valer na hora: atualize o n8n logo em seguida.
Novas tentativas e idempotência
- Responda com qualquer
2xxem até 10 segundos. Outra resposta, redirecionamento (não é seguido) ou silêncio = falha. - Depois de uma falha, o Ranqal tenta de novo a partir de 1 min, 5 min, 30 min, 2 h e 12 h (6 envios ao todo). Depois disso o evento para; dá para reenviar na lista de entregas.
- O mesmo evento pode chegar mais de uma vez (nova tentativa, reenvio manual ou uma resposta que se perdeu no caminho). O
iddo corpo e o headerX-Ranqal-Event-Idsão iguais em todos os envios do mesmo evento: guarde os ids já processados e ignore os repetidos. - A ordem de chegada não é garantida: use
created_at. Cada endpoint recebe o evento com o seu próprioid. - 20 falhas seguidas desligam o endpoint e o Dono recebe um e-mail. Corrija o endereço, ligue de novo e envie um evento de teste.
- As últimas 50 entregas (status, tempo e trecho da resposta) aparecem na mesma tela, por 30 dias.
Segurança
Só endereços públicos com https://. O Ranqal recusa endereços que apontam para rede privada, local ou de metadados de nuvem, inclusive quando o nome só revela isso depois do DNS. O segredo fica cifrado no Ranqal e não aparece de novo depois de criado. O corpo traz dados de contato do lead: trate o endereço e o que ele grava com o mesmo cuidado da sua base de clientes.
MCP: Claude e Cursor
Conecte o Claude ou o Cursor ao Ranqal e pergunte em português: “quais clientes caíram no Score este mês?”, “resuma as avaliações negativas da Clínica Sorriso”, “o que falta no plano de ação?”. O servidor MCP usa a mesma chave da API (mesmos escopos, planos e limite de 60 chamadas por minuto) e só lê: nenhuma ferramenta altera dados nem dispara coletas pagas. Cada chamada aparece em Últimas chamadas.
Endereço: https://app.ranqal.com.br/api/mcp (Streamable HTTP). A chave vai no header Authorization: Bearer; o assistente vê os dados da agência inteira, então use uma chave só para ele e revogue se não usar mais.
Claude Code
claude mcp add --transport http ranqal https://app.ranqal.com.br/api/mcp \ --header "Authorization: Bearer rq_live_SUA_CHAVE"
Confira com claude mcp list. Para usar em todos os projetos, acrescente --scope user.
Claude Desktop
Em Configurações › Desenvolvedor › Editar configuração, cole no claude_desktop_config.json e reinicie o app (precisa do Node.js instalado; o mcp-remote faz a ponte com o endereço remoto):
{
"mcpServers": {
"ranqal": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://app.ranqal.com.br/api/mcp", "--header", "Authorization:${RANQAL_AUTH}"],
"env": { "RANQAL_AUTH": "Bearer rq_live_SUA_CHAVE" }
}
}
}Cursor
Em .cursor/mcp.json (no projeto) ou ~/.cursor/mcp.json (todos os projetos):
{
"mcpServers": {
"ranqal": {
"url": "https://app.ranqal.com.br/api/mcp",
"headers": { "Authorization": "Bearer rq_live_SUA_CHAVE" }
}
}
}Claude.ai (web), ChatGPT e outros conectores na nuvem ainda não funcionam: eles exigem login por OAuth, que esta versão não tem. Use o Claude Code, o Claude Desktop ou o Cursor.
Ferramentas
| Ferramenta | Argumentos | O que traz |
|---|---|---|
list_businesses | include_archived, client_id | Fichas dos clientes (comece por aqui para achar o id da ficha). |
get_business_overview | business_id | Resumo: Ranqal Score, última medição, plano de ação e IA nos últimos 30 dias. |
get_score_history | business_id, since | Histórico do Ranqal Score. |
get_latest_scan | business_id, keyword, include_grid | Última medição do geogrid com métricas e os 10 primeiros concorrentes. |
list_reviews | business_id, since, max_rating, unanswered_only | Avaliações do Google (texto como untrusted_text). |
list_actions | business_id, status | Plano de ação da ficha. |
list_leads | source, since | Leads do teste grátis no seu site e testes de prospecção. |
get_usage | — | Plano, limites x uso e gasto do mês. Só aparece com o escopo usage:read. |
As respostas são JSON com os mesmos campos da API. Listas vêm em páginas de 20 (até 100 com limit); para continuar, passe o next_cursor em cursor. A agência vem sempre da chave: nenhuma ferramenta aceita agência como argumento.
Texto de terceiros. Avaliações, nomes de concorrentes e o que o visitante digitou no formulário vêm cortados e dentro de { "untrusted_text": "..." }. Assim o assistente sabe que é dado, não instrução: alguém pode escrever numa avaliação “ignore as instruções anteriores”, e esse texto não deve ser obedecido. Mesmo assim, revise antes de agir sobre o que o assistente sugerir a partir desses textos.
Mudanças
- 21/10/2026 · MCP: servidor MCP remoto só leitura em
/api/mcpcom 8 ferramentas, mesma chave, escopos e limites da API; textos de terceiros marcados comountrusted_text. - 21/10/2026 · webhooks: eventos
lead.created,test.completedescan.completed, assinatura HMAC-SHA256, novas tentativas (1 min a 12 h), reenvio e evento de teste. - 21/10/2026 · v1: primeira versão. 12 rotas só leitura, CSV, chaves com escopos e validade.
Campos novos podem aparecer a qualquer momento: ignore o que não conhece. Mudança incompatível só numa nova versão (/api/v2), com aviso antes. Dúvidas: contato@ranqal.com.br. Uso da API segue os Termos de Uso.