PromoPilot API
Uma única API REST para todo o ecossistema PromoPilot: lance a promoção de links, acompanhe posições, execute auditorias de SEO e varreduras de segurança a partir do seu próprio código: CRM, SaaS ou painel de agência.
- URL base:
https://promopilot.link/api/v1
- O formato é JSON sobre HTTPS; toda resposta contém um campo ok.
- Os projetos criados pela API aparecem no painel em uma seção separada “Projetos de API” e têm um selo: não se misturam com os pessoais.
- Especificação legível por máquina: openapi.json
curl https://promopilot.link/api/v1/ping
# {"ok":true,"pong":true,"time":"2026-07-20T12:00:00Z"}
Autorização e chaves
As chaves de API são emitidas no painel do cliente: Painel → API para desenvolvedores. Ao criar uma chave, você escolhe a quais serviços ela pode ter acesso (escopos): promotion, rank, audit, shield. A chave é exibida por completo apenas uma vez: guardamos somente a sua impressão digital SHA-256.
Authorization: Bearer ppk_your_key# или
X-Api-Key: ppk_your_key
Segurança: mantenha a chave em variáveis de ambiente ou em um cofre de segredos e nunca a publique em código client-side ou em repositórios. Revogue imediatamente uma chave comprometida no painel: a revogação tem efeito na hora. Use chaves separadas, com os escopos mínimos necessários, para cada integração.
Cobrança e saldo
- Todas as operações pagas são cobradas do saldo pessoal da conta, o mesmo do painel. Não existe um “saldo de API” separado.
- Promoção: o preço vem do plano, da cascata flexível ou do preço global, menos o seu desconto pessoal; assinantes Pro recebem automaticamente mais 10% de desconto.
- Você pode obter o preço exato antes de lançar: POST /promotion/quote (sem cobrança).
- O Rank Tracker é cobrado pelas verificações realmente executadas; a SEO Audit full é paga antecipadamente com base na estimativa de tamanho; o Shield deep/comprehensive tem um preço fixo por varredura.
- Se não houver saldo suficiente, você recebe HTTP 402 insufficient_funds com os campos required, balance, shortfall e um link topup_url. A recarga é feita no painel.
Formato de erro
{
"ok": false,
"error": {
"code": "insufficient_funds",
"message": "Saldo insuficiente. Recarregue no painel e repita a requisição.",
"details": {"required": 44.1, "balance": 10.0, "shortfall": 34.1, "topup_url": "…"}
}
}
| Código | HTTP | Descrição |
unauthorized | 401 | A chave não foi enviada, não foi encontrada ou foi revogada |
forbidden_scope | 403 | A chave não tem acesso a este serviço: emita uma chave com o escopo necessário |
pro_required | 403 | O recurso exige o plano Pro (por exemplo, a marca white-label) |
forbidden | 403 | A ação é proibida (por exemplo, excluir um projeto pessoal pela API) |
not_found | 404 | O objeto não foi encontrado ou pertence a outra conta |
insufficient_funds | 402 | Saldo insuficiente: details conterá required, balance, shortfall e topup_url |
validation_error | 422 | Parâmetros inválidos; o campo é indicado em details.field |
domain_mismatch | 422 | O link não aponta para o domínio do projeto |
already_running | 409 | O processo já está em execução (por exemplo, uma auditoria do projeto) |
needs_verify | 409 | Shield: o domínio não está verificado |
need_estimate | 409 | Auditoria: execute primeiro uma auditoria express para estimar o tamanho |
rate_limited | 429 | Limite de requisições excedido; tente novamente após Retry-After segundos |
report_archived | 410 | O relatório foi arquivado pela política de retenção; contas Pro têm acesso automaticamente |
level1_disabled | 503 | A promoção está temporariamente desativada pela plataforma |
api_disabled | 503 | A API pública está temporariamente desativada |
server_error | 500 | Erro interno: tente novamente mais tarde |
Limites
- 120 requisições por minuto por chave (o limite base).
- Lançamentos de promoção: até 20 por minuto; criação de projetos: até 30 por hora; lançamentos de auditorias e varreduras: até 10 por minuto.
- Ao exceder o limite, você recebe HTTP 429 com o cabeçalho Retry-After. O intervalo recomendado para consultar o status é uma vez a cada 30–60 segundos.
Localização
As mensagens legíveis (message) são retornadas no idioma do parâmetro ?lang= (ru, en, uk, pl) ou do cabeçalho Accept-Language; por padrão, o idioma da sua conta. Os códigos de erro e os status para máquinas nunca são traduzidos: baseie sua lógica neles.
Referência de endpoints
Conta
GET
/me
Perfil: saldo, moeda, desconto pessoal, status Pro, escopos da chave.
Exemplo de resposta
{"ok":true,"user":{"id":2,"balance":124.50,"currency":"USD","promotion_discount_percent":10,"is_pro":true,...},"key":{"label":"CRM","scopes":["promotion"]}}
GET
/balance
O saldo atual e um link de recarga.
Exemplo de resposta
{"ok":true,"balance":124.50,"currency":"USD","topup_url":"…/client/balance.php"}
GET
/key
Informações sobre a chave apresentada: escopos e estatísticas de uso.
Exemplo de resposta
{"ok":true,"key":{"label":"CRM","scopes":["promotion","rank"],"requests_total":1520,...}}
Projetos e links escopo: promotion
GET
/projects
Uma lista de projetos. Por padrão, apenas os criados via API; ?all=1 inclui os pessoais.
| Parâmetro | Entrada | Descrição |
all |
query |
“1”: retorna projetos pessoais e de API |
Exemplo de resposta
{"ok":true,"projects":[{"id":311,"name":"Client A","created_via":"api","links_count":3,"active_runs":1,...}]}
POST
/projects
Cria um projeto. Ele é marcado como created_via=api e aparece no painel em “Projetos de API”. Você pode enviar links já na criação: o domínio deles deve coincidir com o do projeto.
| Parâmetro | Entrada | Descrição |
name * |
body |
Nome do projeto |
url * |
body |
Endereço do site (página inicial) |
language |
body |
Idioma do conteúdo (ru/en/uk/pl/…), ru por padrão |
region |
body |
Região do público |
topic |
body |
Tópico |
wishes |
body |
Observações para o texto |
links |
body |
Um array de objetos {url, anchor, language, wish} |
Exemplo de resposta
{"ok":true,"project":{"id":312,"created_via":"api",...},"links":{"added":2,"domain_errors":0}}
GET
/projects/{id}
O projeto com seus links e as últimas execuções.
Exemplo de resposta
{"ok":true,"project":{"id":312,"links":[...],"runs":[...]}}
PATCH
/projects/{id}
Atualiza nome / descrição / idioma / desejos / região / tema.
Exemplo de resposta
{"ok":true,"project":{...}}
DELETE
/projects/{id}
Exclui um projeto. Permitido apenas para projetos criados via API.
Exemplo de resposta
{"ok":true,"deleted":true}
POST
/projects/{id}/links
Adiciona um link para promover. O domínio do link deve coincidir com o do projeto (ou ser um subdomínio dele); caso contrário, você recebe o erro domain_mismatch.
| Parâmetro | Entrada | Descrição |
url * |
body |
A URL completa da página |
anchor |
body |
Anchor (deixe vazio e escolheremos uma automaticamente) |
language |
body |
Idioma da página |
wish |
body |
Observações de texto para este link específico |
Exemplo de resposta
{"ok":true,"link":{"id":915,"url":"https://site.com/page",...},"links_count":4}
DELETE
/projects/{id}/links/{linkId}
Remove um link do projeto.
Exemplo de resposta
{"ok":true,"deleted":true}
GET
/promotion/tariffs
Planos ativos (para tariff_id), preços unitários da cascata flexível, o preço global e seu desconto pessoal.
Exemplo de resposta
{"ok":true,"tariffs":[{"id":3,"slug":"pro","name":"Pro","price_per_link":49.0,"cascade":{...}}],"your_discount_percent":10}
POST
/promotion/quote
Uma estimativa de preço para um lançamento sem cobrança: os mesmos parâmetros do próprio lançamento.
| Parâmetro | Entrada | Descrição |
tariff_id |
body |
ID do plano |
level1_count…crowd_per_article |
body |
Parâmetros da cascata flexível |
brand_anchors |
body |
true: incluir o adicional de anchors de marca |
Exemplo de resposta
{"ok":true,"pricing_mode":"tariff","base_price":49.0,"discount_percent":10,"brand_anchors_price":0,"total":44.1,"balance":124.5,"sufficient_funds":true}
GET
/promotion/brand
Nome da marca para a opção “Anchors de marca”: confirmado anteriormente ou encontrado literalmente no site (o nome do projeto nunca é tratado como marca). A primeira chamada de um projeto pode levar alguns segundos.
| Parâmetro | Entrada | Descrição |
project_id * |
query |
ID do projeto |
url |
query |
Página promovida (por padrão, o primeiro link do projeto) |
Exemplo de resposta
{"ok":true,"project_id":744,"brand":"Ventana V","source":"page","brand_anchors_price":5}
POST
/promotion/runs
Lança a promoção de um link. O custo é cobrado do seu saldo pessoal (descontos e o bônus Pro são aplicados automaticamente). Lançar o mesmo link novamente enquanto uma cascata estiver ativa retorna already_active.
| Parâmetro | Entrada | Descrição |
project_id * |
body |
ID do projeto |
url * |
body |
O link a promover (ou informe link_id) |
link_id |
body |
O ID do link do projeto: uma alternativa a url |
tariff_id |
body |
Lançar em um plano |
level1_count |
body |
Cascata flexível: publicações L1 |
level2_per_level1 |
body |
L2 para cada L1 |
level3_per_level2 |
body |
L3 para cada L2 |
crowd_per_article |
body |
Links crowd por artigo |
schedule_start_at |
body |
Início adiado (data e hora ISO) |
brand_anchors |
body |
true: anchors de marca (+$5 após o desconto): aproximadamente a cada três artigos L1 e a cada oito artigos L2 recebe um link com o nome da marca para a página inicial do site. Seus próprios anchors permanecem em todos os artigos. |
brand_name |
body |
Nome da marca para os anchors de marca. Se omitido, usamos o confirmado anteriormente ou o encontrado no site; se nenhum for encontrado: erro 422 brand_name_required. |
Exemplo de resposta
{"ok":true,"run_id":501,"status":"queued","charged":49.1,"discount_percent":10,"balance_after":75.4,"currency":"USD","brand_anchors":true,"brand_name":"Ventana V"}
GET
/promotion/runs
Lista paginada de execuções. Filtros: project_id, status (active | queued | running | completed | failed | …), initiated_via (api | web).
| Parâmetro | Entrada | Descrição |
page, per_page |
query |
Paginação (até 100 por página) |
Exemplo de resposta
{"ok":true,"runs":[{"id":501,"status":"level1_active","progress":{"done":3,"total":12,"pct":25},...}],"pagination":{...}}
GET
/promotion/runs/{id}
Status da execução: etapa, progresso e detalhamento por nível (total / success / verified).
Exemplo de resposta
{"ok":true,"run":{"id":501,"status":"level2_active","levels":[{"level":1,"total":5,"verified":5},...]}}
GET
/promotion/runs/{id}/nodes
Todas as colocações na cascata: rede, URL da publicação, status de verificação. ?verified=1 retorna apenas links confirmados.
| Parâmetro | Entrada | Descrição |
verified |
query |
“1”: somente colocações verificadas |
Exemplo de resposta
{"ok":true,"run_id":501,"nodes":[{"level":1,"network":"telegraph","url":"https://telegra.ph/…","verified":true,...}]}
Relatórios e white-label (Pro) escopo: promotion
GET
/promotion/runs/{id}/report
Relatório final (apenas posicionamentos verificados, como no painel). ?format=pdf|xlsx|csv|csv_comma retorna um link para o arquivo (PDF com gráficos, XLSX com as planilhas “Resumo” e “Posicionamentos”, CSV para Excel ou separado por vírgulas para Google Sheets). No plano Pro, o relatório recebe a sua marca white-label (veja /branding); os campos podem ser substituídos com os parâmetros brand_*.
| Parâmetro | Entrada | Descrição |
format |
query |
json (padrão), pdf, xlsx, csv ou csv_comma |
lang |
query |
Idioma do documento: ru, en, uk, pl (padrão: idioma da requisição) |
brand_company |
query |
Pro: nome da empresa no cabeçalho |
brand_logo_url |
query |
Pro: URL do logotipo |
brand_accent_color |
query |
Pro: cor de destaque em HEX |
brand_footer_text |
query |
Pro: texto do rodapé |
Exemplo de resposta
{"ok":true,"run":{...},"summary":{"verified":12,...},"report":{"level1":[...],"level2":[...],"crowd":[...]},"whitelabel":{...}} — или {"ok":true,"pdf":{"url":"…/uploads/reports/api-cascade-501-….pdf"},"branded":true} — или {"ok":true,"file":{"url":"…/uploads/reports/acme-report-site.com-….xlsx","filename":"acme-report-site.com-2026-10-11.xlsx"},"format":"xlsx","branded":true}
GET
/branding
A marca white-label atual e o status Pro.
Exemplo de resposta
{"ok":true,"is_pro":true,"branding_active":true,"branding":{"company_name":"Acme","logo_url":"…","accent_color":"#6366f1",...}}
PUT
/branding
Salva a marca white-label (somente Pro). A mesma marca é aplicada no painel (Configurações → White-label).
| Parâmetro | Entrada | Descrição |
company_name * |
body |
Nome da empresa (obrigatório) |
logo_url, website, email, phone, address, footer_text, accent_color |
body |
Outros campos da marca. Os campos que você não enviar mantêm seus valores; uma string vazia limpa o campo. |
Exemplo de resposta
{"ok":true,"branding":{...}}
Rank Tracker escopo: rank
GET
/rank/projects
Projetos de monitoramento de posições.
Exemplo de resposta
{"ok":true,"projects":[{"id":7,"domain":"site.com","region":"UA","keywords_count":50,"top10":12,...}]}
POST
/rank/projects
Cria um projeto: domínio, região (UA/US/PL/…), dispositivo (desktop/mobile).
| Parâmetro | Entrada | Descrição |
domain * |
body |
Domínio ou URL do site |
name |
body |
Nome (o domínio por padrão) |
region |
body |
Região de busca, UA por padrão |
device |
body |
desktop | mobile |
Exemplo de resposta
{"ok":true,"project":{"id":8,...}}
GET
/rank/projects/{id}
O projeto com todas as palavras-chave e suas posições mais recentes.
Exemplo de resposta
{"ok":true,"project":{...},"keywords":[{"id":91,"keyword":"comprar janelas","last_position":4,"last_checked_at":"…"}]}
POST
/rank/projects/{id}/keywords
Adiciona palavras-chave: uma string separada por quebras de linha ou um array.
| Parâmetro | Entrada | Descrição |
keywords * |
body |
Uma string “kw1\nkw2” ou um array de strings |
tag |
body |
Rótulo do grupo |
target_url |
body |
Página de destino |
Exemplo de resposta
{"ok":true,"added":25,"skipped":0}
POST
/rank/projects/{id}/check
Coloca uma verificação de posições na fila (todas as palavras-chave ou keyword_ids[]). Você é cobrado pelas verificações realmente executadas, pelas tarifas internas do serviço. Obtenha os resultados em GET /rank/projects/{id}.
| Parâmetro | Entrada | Descrição |
keyword_ids |
body |
Um array de IDs de palavras-chave (todas por padrão) |
Exemplo de resposta
{"ok":true,"queued":50,"note":"…"}
SEO Audit escopo: audit
GET
/audit/projects
Projetos de auditoria.
Exemplo de resposta
{"ok":true,"projects":[{"id":4,"domain":"site.com","start_url":"https://site.com/",...}]}
POST
/audit/projects
Cria um projeto de auditoria.
| Parâmetro | Entrada | Descrição |
url * |
body |
Endereço do site |
name |
body |
Nome |
include_subdomains |
body |
true: rastrear subdomínios |
Exemplo de resposta
{"ok":true,"project":{"id":5,...}}
GET
/audit/projects/{id}/estimate
A estimativa do tamanho do site e o preço de uma auditoria completa (é necessária pelo menos uma auditoria express para a estimativa).
Exemplo de resposta
{"ok":true,"has_estimate":true,"estimated_pages":430,"full_audit_price":5.0,"currency":"USD"}
POST
/audit/projects/{id}/crawls
Inicia uma auditoria. express é gratuita (limite diário); full é a auditoria completa, paga antecipadamente com seu saldo com base na estimativa de tamanho.
| Parâmetro | Entrada | Descrição |
mode |
body |
express (padrão) | full |
Exemplo de resposta
{"ok":true,"crawl_id":88,"mode":"full","max_pages":500,"charged":5.0}
GET
/audit/crawls/{id}
Status e resultados: Health Score, erros/avisos e um resumo dos problemas por categoria (após a conclusão).
Exemplo de resposta
{"ok":true,"crawl":{"id":88,"status":"done","health_score":79,"pages_crawled":430,"issues":{...}}}
GET
/audit/projects/{id}/crawls
O histórico de auditorias do projeto.
Exemplo de resposta
{"ok":true,"crawls":[...]}
Security Shield escopo: shield
GET
/shield/projects
Sites protegidos.
Exemplo de resposta
{"ok":true,"projects":[{"id":3,"domain":"site.com","verified":true,...}]}
POST
/shield/projects
Cria ou encontra um projeto pela URL. A resposta contém um verify_token para a verificação do domínio.
| Parâmetro | Entrada | Descrição |
url * |
body |
Endereço do site |
name |
body |
Nome |
Exemplo de resposta
{"ok":true,"project":{"id":3,"verified":false,"verify_token":"pp-verify-…"},"verification_hint":"…"}
POST
/shield/projects/{id}/verify
Verifica a verificação do domínio (um registro DNS TXT, um arquivo .well-known ou uma meta tag com verify_token). Obrigatória para varreduras pagas.
Exemplo de resposta
{"ok":true,"verified":true,"method":"dns"}
POST
/shield/projects/{id}/scans
Inicia uma varredura: gratuita (básica, com limite diário), deep ou comprehensive; as duas últimas são cobradas do seu saldo.
| Parâmetro | Entrada | Descrição |
mode |
body |
free | deep | comprehensive |
lang |
body |
Idioma do relatório de IA (ru/uk/en/pl) |
Exemplo de resposta
{"ok":true,"scan_id":41,"mode":"deep","charged":19.0}
GET
/shield/scans/{id}
Status, nota (score/grade), o veredito em semáforo, um resumo dos achados por gravidade e um link público do relatório.
Exemplo de resposta
{"ok":true,"scan":{"id":41,"status":"done","score":86,"grade":"B","verdict":{...},"findings_summary":{"critical":0,"high":1,...},"share_report_url":"…"}}
Exemplos de código
cURL — criar um projeto e lançar a promoção
curl -X POST https://promopilot.link/api/v1/projects \
-H "Authorization: Bearer ppk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Client Site",
"url": "https://client-site.com",
"language": "ru",
"links": [{"url": "https://client-site.com/services", "anchor": "serviços da empresa"}]
}'
curl -X POST https://promopilot.link/api/v1/promotion/runs \
-H "Authorization: Bearer ppk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id": 312, "url": "https://client-site.com/services"}'
PHP
<?php
$key = getenv('PROMOPILOT_API_KEY'); // nunca deixe a chave no seu código
function pp_api(string $method, string $path, array $data = null) {
global $key;
$ch = curl_init('https://promopilot.link/api/v1' . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $key,
'Content-Type: application/json',
'Accept-Language: ru',
],
CURLOPT_POSTFIELDS => $data !== null ? json_encode($data) : null,
]);
$res = json_decode(curl_exec($ch), true);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if (($res['ok'] ?? false) !== true) {
throw new RuntimeException($res['error']['code'] ?? ('http_' . $code));
}
return $res;
}
$project = pp_api('POST', '/projects', ['name' => 'Client', 'url' => 'https://client-site.com']);
try {
$run = pp_api('POST', '/promotion/runs', [
'project_id' => $project['project']['id'],
'url' => 'https://client-site.com/services',
]);
echo "Iniciado, run_id={$run['run_id']}, cobrado {$run['charged']}";
} catch (RuntimeException $e) {
if ($e->getMessage() === 'insufficient_funds') {
// recarregue o saldo e tente novamente
}
}
Python
import os, requests
API = "https://promopilot.link/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PROMOPILOT_API_KEY']}"}
def api(method, path, **json_body):
r = requests.request(method, API + path, headers=HEADERS, json=json_body or None, timeout=30)
data = r.json()
if not data.get("ok"):
code = data.get("error", {}).get("code", f"http_{r.status_code}")
if code == "insufficient_funds":
details = data["error"]["details"]
raise RuntimeError(f"Recarregue seu saldo: faltam {details['shortfall']}")
raise RuntimeError(code)
return data
project = api("POST", "/projects", name="Client", url="https://client-site.com")
run = api("POST", "/promotion/runs", project_id=project["project"]["id"],
url="https://client-site.com/services")
print("run_id:", run["run_id"], "charged:", run["charged"])
# Consulta de status
import time
while True:
st = api("GET", f"/promotion/runs/{run['run_id']}")["run"]
print(st["status"], st["progress"]["pct"], "%")
if st["status"] in ("completed", "failed", "cancelled"):
break
time.sleep(60)
report = api("GET", f"/promotion/runs/{run['run_id']}/report")
Node.js
const API = "https://promopilot.link/api/v1";
const KEY = process.env.PROMOPILOT_API_KEY;
async function api(method, path, body) {
const res = await fetch(API + path, {
method,
headers: {
"Authorization": `Bearer ${KEY}`,
"Content-Type": "application/json",
"Accept-Language": "ru",
},
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json();
if (!data.ok) {
const code = data.error?.code || `http_${res.status}`;
if (code === "insufficient_funds") {
console.error("Recarregue seu saldo:", data.error.details.topup_url);
}
throw new Error(code);
}
return data;
}
const { project } = await api("POST", "/projects", { name: "Client", url: "https://client-site.com" });
const run = await api("POST", "/promotion/runs", { project_id: project.id, url: "https://client-site.com/services" });
console.log("run:", run.run_id, "charged:", run.charged);
OpenAPI
A especificação completa legível por máquina para gerar clientes e importar no Postman/Insomnia: https://promopilot.link/api/v1/openapi.json
Dúvidas e sugestões sobre a API são tratadas pelo suporte no painel ou pelo Telegram. Compatibilidade retroativa: mudanças incompatíveis são lançadas somente como uma nova versão (/api/v2), e os campos atuais nunca são removidos sem um aviso no changelog.