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 unaudit_idreal. - 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 acompleted.
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/auditscuando se despliega un preview, guarda elaudit_id, hace polling deGET /api/audits/:idcada 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:nullhasta 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ódigo | Cuándo | Acción |
|---|---|---|
UNAUTHORIZED (401) | Bearer ausente o inválido | Crea una clave en app.metricspot.com/settings/api-keys |
QUOTA_EXCEEDED (402) | Cupo mensual del plan agotado | Sube de plan en app.metricspot.com/billing |
INVALID_URL (400) | URL inválida o de más de 2000 caracteres | Pasa una URL absoluta https:// |
RATE_LIMITED (429) | Cooldown por dominio o global | Espera la ventana indicada antes de reintentar la misma URL |
UPSTREAM_FAILED (5xx) | Caída de PSI o crawler de origen | Reintenta 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