Último día de Halloween: +30% en una recargaEl bono se aplica una sola vez: recarga de una vez todo el importe que necesites. Echa un vistazo a tu panel: hay sorpresas escondidas. 00h00min00s Aprovechar el bono

PromoPilot API

Una única API REST para todo el ecosistema de PromoPilot: lanza promoción de enlaces, rastrea posiciones, ejecuta auditorías SEO y escaneos de seguridad desde tu propio código: CRM, SaaS o un panel de agencia.

  • URL base: https://promopilot.link/api/v1
  • El formato es JSON sobre HTTPS; cada respuesta contiene un campo ok.
  • Los proyectos creados mediante la API aparecen en el panel en una sección aparte llamada “Proyectos de API” y llevan una insignia: no se mezclan con los personales.
  • Especificación legible por máquina: openapi.json
curl https://promopilot.link/api/v1/ping
# {"ok":true,"pong":true,"time":"2026-07-20T12:00:00Z"}

Autorización y claves

Las claves de API se emiten en el panel del cliente: Panel → API para desarrolladores. Al crear una clave eliges a qué servicios puede acceder (scopes): promotion, rank, audit, shield. La clave se muestra completa solo una vez: no almacenamos nada salvo su huella SHA-256.

Authorization: Bearer ppk_your_key# или
X-Api-Key: ppk_your_key
Seguridad: guarda la clave en variables de entorno o en un almacén de secretos, y nunca la publiques en código del lado del cliente ni en repositorios. Revoca de inmediato en el panel una clave comprometida: la revocación surte efecto al instante. Usa claves distintas, con los scopes mínimos necesarios, para cada integración.

Facturación y saldo

  • Todas las operaciones de pago se cargan al saldo personal de la cuenta, el mismo que ves en el panel. No existe un “saldo de API” aparte.
  • Promoción: el precio proviene del plan, de la cascada flexible o del precio global, menos tu descuento personal; los suscriptores Pro obtienen automáticamente un 10% adicional de descuento.
  • Puedes conocer el precio exacto antes de lanzar: POST /promotion/quote (sin cargo).
  • Rank Tracker cobra por las comprobaciones realizadas realmente; SEO Audit completo se prepaga según la estimación de tamaño; Shield deep/comprehensive tiene un precio fijo por escaneo.
  • Si no hay fondos suficientes recibes HTTP 402 insufficient_funds con los campos required, balance, shortfall y un enlace topup_url. La recarga se realiza en el panel.

Formato de errores

{
  "ok": false,
  "error": {
    "code": "insufficient_funds",
    "message": "No hay fondos suficientes en tu saldo. Recárgalo en el panel y repite la solicitud.",
    "details": {"required": 44.1, "balance": 10.0, "shortfall": 34.1, "topup_url": "…"}
  }
}
CódigoHTTPDescripción
unauthorized401La clave no se proporcionó, no se encontró o fue revocada
forbidden_scope403La clave no tiene acceso a este servicio: emite una clave con el alcance necesario
pro_required403La función requiere el plan Pro (por ejemplo, la marca blanca)
forbidden403La acción está prohibida (por ejemplo, eliminar un proyecto personal mediante la API)
not_found404El objeto no se encontró o pertenece a otra cuenta
insufficient_funds402Fondos insuficientes: details contendrá required, balance, shortfall y topup_url
validation_error422Parámetros no válidos; el campo se indica en details.field
domain_mismatch422El enlace no apunta al dominio del proyecto
already_running409El proceso ya está en ejecución (por ejemplo, una auditoría del proyecto)
needs_verify409Shield: el dominio no está verificado
need_estimate409Audit: primero ejecuta una auditoría exprés para estimar el tamaño
rate_limited429Límite de solicitudes excedido; vuelve a intentarlo después de Retry-After segundos
report_archived410El informe fue archivado por la política de retención; las cuentas Pro obtienen acceso automáticamente
level1_disabled503La promoción está desactivada temporalmente por la plataforma
api_disabled503La API pública está desactivada temporalmente
server_error500Error interno: inténtalo de nuevo más tarde

Límites

  • 120 solicitudes por minuto por clave (el límite base).
  • Lanzamientos de promoción: hasta 20 por minuto; creación de proyectos: hasta 30 por hora; lanzamientos de auditorías y escaneos: hasta 10 por minuto.
  • Si superas el límite recibes HTTP 429 con un encabezado Retry-After. El intervalo recomendado para consultar el estado es una vez cada 30–60 segundos.

Localización

Los mensajes legibles (message) se devuelven en el idioma del parámetro ?lang= (ru, en, uk, pl) o del encabezado Accept-Language; por defecto, el idioma de tu cuenta. Los códigos de error y los estados para máquinas nunca se traducen: basa tu lógica en ellos.

Referencia de endpoints

Cuenta

GET /me
Perfil: saldo, moneda, descuento personal, estado Pro, alcances de la clave.
Ejemplo de respuesta
{"ok":true,"user":{"id":2,"balance":124.50,"currency":"USD","promotion_discount_percent":10,"is_pro":true,...},"key":{"label":"CRM","scopes":["promotion"]}}
GET /balance
El saldo actual y un enlace para recargar.
Ejemplo de respuesta
{"ok":true,"balance":124.50,"currency":"USD","topup_url":"…/client/balance.php"}
GET /key
Información sobre la clave presentada: alcances y estadísticas de uso.
Ejemplo de respuesta
{"ok":true,"key":{"label":"CRM","scopes":["promotion","rank"],"requests_total":1520,...}}

Proyectos y enlaces scope: promotion

GET /projects
Una lista de proyectos. De forma predeterminada solo los creados mediante la API; ?all=1 incluye también los personales.
ParámetroEntradaDescripción
all query “1”: devuelve tanto los proyectos personales como los de la API
Ejemplo de respuesta
{"ok":true,"projects":[{"id":311,"name":"Client A","created_via":"api","links_count":3,"active_runs":1,...}]}
POST /projects
Crea un proyecto. Se marca como created_via=api y aparece en el panel en «Proyectos API». Puedes pasar enlaces de inmediato: su dominio debe coincidir con el dominio del proyecto.
ParámetroEntradaDescripción
name * body Nombre del proyecto
url * body Dirección del sitio (página principal)
language body Idioma del contenido (ru/en/uk/pl/…), ru por defecto
region body Región de la audiencia
topic body Tema
wishes body Notas para el texto
links body Un array de objetos {url, anchor, language, wish}
Ejemplo de respuesta
{"ok":true,"project":{"id":312,"created_via":"api",...},"links":{"added":2,"domain_errors":0}}
GET /projects/{id}
El proyecto con sus enlaces y las últimas ejecuciones.
Ejemplo de respuesta
{"ok":true,"project":{"id":312,"links":[...],"runs":[...]}}
PATCH /projects/{id}
Actualiza el nombre / la descripción / el idioma / los deseos / la región / la temática.
Ejemplo de respuesta
{"ok":true,"project":{...}}
DELETE /projects/{id}
Elimina un proyecto. Solo se permite para proyectos creados mediante la API.
Ejemplo de respuesta
{"ok":true,"deleted":true}
POST /projects/{id}/links
Añade un enlace para promocionar. El dominio del enlace debe coincidir con el dominio del proyecto (o ser un subdominio de este); de lo contrario, recibirás el error domain_mismatch.
ParámetroEntradaDescripción
url * body La URL completa de la página
anchor body Anchor (déjalo vacío y elegiremos uno automáticamente)
language body Idioma de la página
wish body Notas del texto para este enlace en concreto
Ejemplo de respuesta
{"ok":true,"link":{"id":915,"url":"https://site.com/page",...},"links_count":4}
DELETE /projects/{id}/links/{linkId}
Elimina un enlace del proyecto.
Ejemplo de respuesta
{"ok":true,"deleted":true}

Promoción (cascadas) scope: promotion

GET /promotion/tariffs
Planes activos (para tariff_id), precios unitarios de la cascada flexible, el precio global y tu descuento personal.
Ejemplo de respuesta
{"ok":true,"tariffs":[{"id":3,"slug":"pro","name":"Pro","price_per_link":49.0,"cascade":{...}}],"your_discount_percent":10}
POST /promotion/quote
Una estimación de precio para un lanzamiento sin realizar el cobro: los mismos parámetros que el propio lanzamiento.
ParámetroEntradaDescripción
tariff_id body ID del plan
level1_count…crowd_per_article body Parámetros de la cascada flexible
brand_anchors body true: incluir el recargo de anchors de marca
Ejemplo de respuesta
{"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
Nombre de la marca para la opción «Anchors de marca»: confirmado anteriormente o encontrado textualmente en el sitio web (el nombre del proyecto nunca se trata como marca). La primera llamada de un proyecto puede tardar unos segundos.
ParámetroEntradaDescripción
project_id * query ID del proyecto
url query Página promocionada (por defecto, el primer enlace del proyecto)
Ejemplo de respuesta
{"ok":true,"project_id":744,"brand":"Ventana V","source":"page","brand_anchors_price":5}
POST /promotion/runs
Lanza la promoción de un enlace. El costo se cobra de tu saldo personal (los descuentos y el bono Pro se aplican automáticamente). Si lanzas el mismo enlace de nuevo mientras hay una cascada activa, se devuelve already_active.
ParámetroEntradaDescripción
project_id * body ID del proyecto
url * body El enlace a promocionar (o pasa link_id)
link_id body El ID del enlace del proyecto: una alternativa a url
tariff_id body Lanzar con un plan
level1_count body Cascada flexible: publicaciones L1
level2_per_level1 body L2 por cada L1
level3_per_level2 body L3 por cada L2
crowd_per_article body Enlaces crowd por artículo
schedule_start_at body Inicio diferido (fecha y hora ISO)
brand_anchors body true: anchors de marca (+$5 después del descuento): aproximadamente cada tercer artículo L1 y cada octavo artículo L2 incluye un enlace con el nombre de la marca a la página principal del sitio. Tus propios anchors se mantienen en todos los artículos.
brand_name body Nombre de la marca para los anchors de marca. Si se omite, usamos el confirmado anteriormente o el encontrado en el sitio web; si no se encuentra ninguno, error 422 brand_name_required.
Ejemplo de respuesta
{"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
Una lista paginada de ejecuciones. Filtros: project_id, status (active | queued | running | completed | failed | …), initiated_via (api | web).
ParámetroEntradaDescripción
page, per_page query Paginación (hasta 100 por página)
Ejemplo de respuesta
{"ok":true,"runs":[{"id":501,"status":"level1_active","progress":{"done":3,"total":12,"pct":25},...}],"pagination":{...}}
GET /promotion/runs/{id}
Estado de la ejecución: etapa, progreso y desglose por nivel (total / success / verified).
Ejemplo de respuesta
{"ok":true,"run":{"id":501,"status":"level2_active","levels":[{"level":1,"total":5,"verified":5},...]}}
GET /promotion/runs/{id}/nodes
Todas las colocaciones de la cascada: red, URL de la publicación, estado de verificación. ?verified=1 devuelve solo los enlaces confirmados.
ParámetroEntradaDescripción
verified query «1»: solo colocaciones verificadas
Ejemplo de respuesta
{"ok":true,"run_id":501,"nodes":[{"level":1,"network":"telegraph","url":"https://telegra.ph/…","verified":true,...}]}

Informes y marca blanca (Pro) scope: promotion

GET /promotion/runs/{id}/report
Informe final (solo ubicaciones verificadas, como en el panel). ?format=pdf|xlsx|csv|csv_comma devuelve un enlace al archivo (PDF con gráficos, XLSX con las hojas “Resumen” y “Ubicaciones”, CSV para Excel o separado por comas para Google Sheets). En el plan Pro el informe lleva tu marca blanca (consulta /branding); los campos se pueden sobrescribir con los parámetros brand_*.
ParámetroEntradaDescripción
format query json (por defecto), pdf, xlsx, csv o csv_comma
lang query Idioma del documento: ru, en, uk, pl (por defecto, el idioma de la solicitud)
brand_company query Pro: nombre de la empresa en el encabezado
brand_logo_url query Pro: URL del logotipo
brand_accent_color query Pro: color de acento en HEX
brand_footer_text query Pro: texto del pie de página
Ejemplo de respuesta
{"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
La marca blanca actual y el estado Pro.
Ejemplo de respuesta
{"ok":true,"is_pro":true,"branding_active":true,"branding":{"company_name":"Acme","logo_url":"…","accent_color":"#6366f1",...}}
PUT /branding
Guarda la marca blanca (solo Pro). La misma marca se aplica en el panel (Configuración → Marca blanca).
ParámetroEntradaDescripción
company_name * body Nombre de la empresa (obligatorio)
logo_url, website, email, phone, address, footer_text, accent_color body Otros campos de la marca. Los campos que no envíes conservan sus valores; una cadena vacía borra el campo.
Ejemplo de respuesta
{"ok":true,"branding":{...}}

Rank Tracker scope: rank

GET /rank/projects
Proyectos de monitoreo de posiciones.
Ejemplo de respuesta
{"ok":true,"projects":[{"id":7,"domain":"site.com","region":"UA","keywords_count":50,"top10":12,...}]}
POST /rank/projects
Crea un proyecto: dominio, región (UA/US/PL/…), dispositivo (escritorio/móvil).
ParámetroEntradaDescripción
domain * body Dominio o URL del sitio
name body Nombre (el dominio por defecto)
region body Región de búsqueda, UA por defecto
device body desktop | mobile
Ejemplo de respuesta
{"ok":true,"project":{"id":8,...}}
GET /rank/projects/{id}
El proyecto y todas las palabras clave con sus últimas posiciones.
Ejemplo de respuesta
{"ok":true,"project":{...},"keywords":[{"id":91,"keyword":"ventanas de compra","last_position":4,"last_checked_at":"…"}]}
POST /rank/projects/{id}/keywords
Añade palabras clave: una cadena separada por saltos de línea o un arreglo.
ParámetroEntradaDescripción
keywords * body Una cadena “kw1\nkw2” o un arreglo de cadenas
tag body Etiqueta del grupo
target_url body Página de destino
Ejemplo de respuesta
{"ok":true,"added":25,"skipped":0}
POST /rank/projects/{id}/check
Pon en cola una verificación de posiciones (todas las palabras clave o keyword_ids[]). Se te cobran las verificaciones realmente realizadas según las tarifas internas del servicio. Obtén los resultados desde GET /rank/projects/{id}.
ParámetroEntradaDescripción
keyword_ids body Un arreglo de IDs de palabras clave (todas por defecto)
Ejemplo de respuesta
{"ok":true,"queued":50,"note":"…"}

SEO Audit scope: audit

GET /audit/projects
Proyectos de auditoría.
Ejemplo de respuesta
{"ok":true,"projects":[{"id":4,"domain":"site.com","start_url":"https://site.com/",...}]}
POST /audit/projects
Crea un proyecto de auditoría.
ParámetroEntradaDescripción
url * body Dirección del sitio
name body Nombre
include_subdomains body true: rastrear subdominios
Ejemplo de respuesta
{"ok":true,"project":{"id":5,...}}
GET /audit/projects/{id}/estimate
La estimación del tamaño del sitio y el precio de una auditoría completa (para la estimación se necesita al menos una auditoría exprés).
Ejemplo de respuesta
{"ok":true,"has_estimate":true,"estimated_pages":430,"full_audit_price":5.0,"currency":"USD"}
POST /audit/projects/{id}/crawls
Inicia una auditoría. La exprés es gratuita (límite diario); la completa es la auditoría integral, prepagada desde tu saldo según la estimación del tamaño.
ParámetroEntradaDescripción
mode body express (por defecto) | full
Ejemplo de respuesta
{"ok":true,"crawl_id":88,"mode":"full","max_pages":500,"charged":5.0}
GET /audit/crawls/{id}
Estado y resultados: Health Score, errores/advertencias y un resumen de problemas por categoría (al finalizar).
Ejemplo de respuesta
{"ok":true,"crawl":{"id":88,"status":"done","health_score":79,"pages_crawled":430,"issues":{...}}}
GET /audit/projects/{id}/crawls
El historial de auditorías del proyecto.
Ejemplo de respuesta
{"ok":true,"crawls":[...]}

Security Shield scope: shield

GET /shield/projects
Sitios protegidos.
Ejemplo de respuesta
{"ok":true,"projects":[{"id":3,"domain":"site.com","verified":true,...}]}
POST /shield/projects
Crea o encuentra un proyecto por URL. La respuesta contiene un verify_token para la verificación del dominio.
ParámetroEntradaDescripción
url * body Dirección del sitio
name body Nombre
Ejemplo de respuesta
{"ok":true,"project":{"id":3,"verified":false,"verify_token":"pp-verify-…"},"verification_hint":"…"}
POST /shield/projects/{id}/verify
Comprueba la verificación del dominio (un registro DNS TXT, un archivo .well-known o una etiqueta meta con verify_token). Obligatoria para los escaneos de pago.
Ejemplo de respuesta
{"ok":true,"verified":true,"method":"dns"}
POST /shield/projects/{id}/scans
Inicia un escaneo: gratuito (básico, límite diario), profundo o completo; estos dos últimos se cobran de tu saldo.
ParámetroEntradaDescripción
mode body free | deep | comprehensive
lang body Idioma del informe de IA (ru/uk/en/pl)
Ejemplo de respuesta
{"ok":true,"scan_id":41,"mode":"deep","charged":19.0}
GET /shield/scans/{id}
Estado, calificación (score/grade), el veredicto tipo semáforo, un resumen de hallazgos por gravedad y un enlace al informe público.
Ejemplo de respuesta
{"ok":true,"scan":{"id":41,"status":"done","score":86,"grade":"B","verdict":{...},"findings_summary":{"critical":0,"high":1,...},"share_report_url":"…"}}

Ejemplos de código

cURL — crear un proyecto y lanzar la promoción

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": "servicios de la 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 guardes la clave en tu 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') {
        // recarga el saldo y vuelve a intentarlo
    }
}

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"Recarga tu saldo: te faltan {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 del estado (polling)
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("Recarga tu 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

La especificación completa legible por máquina para generar clientes e importarla en Postman/Insomnia: https://promopilot.link/api/v1/openapi.json

Las preguntas y sugerencias sobre la API se gestionan a través del soporte en el panel o en Telegram. Compatibilidad hacia atrás: los cambios incompatibles se publican solo como una nueva versión (/api/v2), y los campos actuales nunca se eliminan sin un aviso en el registro de cambios.

¿Listo para empezar?
Crea una clave de API en el panel: te toma un minuto.
Obtener una clave de API