api

POST /api/audits

Encola una auditoría SEO completa con Core Web Vitals. Devuelve audit_id + status: queued en 1-2s. Cuenta contra plan, respeta cooldown por dominio.

Qué hace este endpoint

POST /api/audits encola una auditoría SEO y de legibilidad por IA completa y devuelve de inmediato el envoltorio. Es la contraparte autenticada y completa de POST /api/public/audit.

  • Encola de forma asíncrona y responde en 1-2 segundos con status: "queued" y un audit_id real.
  • Trae Core Web Vitals (LCP, CLS, INP) de Google PageSpeed Insights como parte del run.
  • Cuenta contra el cupo del plan (Free 10/mes, Starter 50/mes, Pro ilimitado).
  • Respeta cooldowns por dominio: 24h entre auditorías de la misma URL exacta, más un breve cooldown global entre dos auditorías cualesquiera.
  • Devuelve el mismo shape que GET /api/audits/:id. Los findings están vacíos hasta que la auditoría pase a completed.

Por qué importa

POST /api/audits es el endpoint correcto cuando necesitas una auditoría persistente que puedas consultar luego, un PDF, datos de tráfico orgánico o Core Web Vitals. Las auditorías anónimas cubren unas 90 comprobaciones pero saltan PSI; este endpoint lo cubre todo.

Workflows concretos:

  • Un bot de auditoría sobre PR llama a POST /api/audits cuando se despliega un preview, guarda el audit_id, hace polling de GET /api/audits/:id cada 5s hasta 60s y publica un comentario con el delta frente a la última auditoría de la misma URL.
  • Un cron semanal recorre las landings más visitadas del usuario, encola una nueva auditoría para cada una y manda por correo un resumen leído desde GET /api/audits.

Cómo usarlo

El endpoint es asíncrono por diseño. Trata el 201 Created inmediato como acuse de recibo y haz polling de GET /api/audits/:id con el audit_id devuelto cada pocos segundos. La finalización típica es de 10 a 30 segundos; permite hasta 90 segundos en sitios lentos.

Petición

POST /api/audits HTTP/1.1
Host: app.metricspot.com
Authorization: Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{ "url": "https://example.com" }

El campo url es obligatorio, debe ser absoluto (https://...), parseable y de máximo 2000 caracteres.

curl

curl -X POST https://app.metricspot.com/api/audits \
  -H "authorization: Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "content-type: application/json" \
  -d '{"url": "https://example.com"}'

Node (encolar y polling)

const headers = {
  authorization: "Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx",
  "content-type": "application/json",
};

const queueRes = await fetch("https://app.metricspot.com/api/audits", {
  method: "POST",
  headers,
  body: JSON.stringify({ url: "https://example.com" }),
});
const { audit } = await queueRes.json();
const id = audit.id;

while (true) {
  await new Promise((r) => setTimeout(r, 3000));
  const r = await fetch(`https://app.metricspot.com/api/audits/${id}`, { headers });
  const data = await r.json();
  if (["completed", "failed"].includes(data.audit.status)) {
    console.log(data.audit.status, data.audit.score);
    break;
  }
}

Python httpx

import httpx, time

HEADERS = {"authorization": "Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx"}

r = httpx.post(
    "https://app.metricspot.com/api/audits",
    headers=HEADERS,
    json={"url": "https://example.com"},
    timeout=30.0,
)
audit_id = r.json()["audit"]["id"]

for _ in range(30):
    time.sleep(3)
    r = httpx.get(f"https://app.metricspot.com/api/audits/{audit_id}", headers=HEADERS, timeout=30.0)
    audit = r.json()["audit"]
    if audit["status"] in ("completed", "failed"):
        print(audit["status"], audit["score"])
        break

Respuesta

201 Created:

{
  "audit": {
    "id": 12345,
    "domain": "example.com",
    "url": "https://example.com",
    "status": "queued",
    "score": null,
    "created_at": "2026-05-14T10:18:04.000Z"
  }
}

Campos en la respuesta de encolado:

  • id: id entero de auditoría, usado en cada llamada posterior (GET /api/audits/:id, PDF, Google).
  • domain: hostname extraído de la URL.
  • url: eco del request.
  • status: siempre "queued" en la respuesta inicial. Pasa a "running" y luego a "completed" o "failed".
  • score: null hasta que la auditoría termine.
  • created_at: timestamp ISO 8601.

Una vez status === "completed", GET /api/audits/:id devuelve el envoltorio completo con score, module_scores y el array findings (ver api-get-audit).

Envoltorio de error

{ "error": "Quota exceeded", "message": "Upgrade to Starter for 50 audits per month." }

Errores comunes

CódigoCuándoAcción
UNAUTHORIZED (401)Bearer ausente o inválidoCrea una clave en app.metricspot.com/settings/api-keys
QUOTA_EXCEEDED (402)Cupo mensual del plan agotadoSube de plan en app.metricspot.com/billing
INVALID_URL (400)URL inválida o de más de 2000 caracteresPasa una URL absoluta https://
RATE_LIMITED (429)Cooldown por dominio o globalEspera la ventana indicada antes de reintentar la misma URL
UPSTREAM_FAILED (5xx)Caída de PSI o crawler de origenReintenta con backoff

Preguntas frecuentes

¿Cuánto tarda en terminar la auditoría?

Entre 10 y 30 segundos en sitios típicos. Objetivos cargados de JS o páginas con muchos structured data pueden llegar a 90 segundos. Haz polling de GET /api/audits/:id cada 3-5 segundos: la auditoría está calculada del todo cuando status cambia a completed.

¿Una auditoría fallida consume cupo?

No. Una auditoría failed no consume el cupo del plan. La cuota solo decrementa cuando el run termina con éxito y se guardan los findings. Los fallos por rate-limit de PSI se reintentan internamente antes de aflorar.

¿Por qué hay cooldown por dominio?

La misma URL auditada dos veces en cinco minutos devuelve los mismos findings: gasta cuota PSI y confunde los dashboards de tráfico. El cooldown de 24h por URL se aplica del lado del servidor. El mensaje de error incluye el retry_at exacto para que los clientes puedan reprogramar.

¿Puedo encolar muchas auditorías en paralelo?

Sí, hasta el cooldown global (unos pocos segundos entre dos auditorías cualesquiera por cuenta). Para lotes de 50+ URLs, espácialas en cliente o usa Pro, que sube la cuota mensual y afina el throttle global.

Fuentes

Última actualización 2026-05-14