api

POST /api/audits

Metti in coda un audit SEO completo con Core Web Vitals. Restituisce audit_id + status: queued in 1-2s. Conta contro il piano, rispetta cooldown per dominio.

Cosa fa questo endpoint

POST /api/audits mette in coda un audit SEO e di leggibilità per IA completo, restituendo la busta dell’audit immediatamente. È la controparte autenticata e completa di POST /api/public/audit.

  • Mette in coda in modo asincrono e restituisce in 1-2 secondi con status: "queued" e un audit_id reale.
  • Tira Core Web Vitals (LCP, CLS, INP) dal Google PageSpeed Insights come parte dell’esecuzione.
  • Conta contro la quota del piano (Free 10/mese, Starter 50/mese, Pro illimitato).
  • Rispetta cooldown per dominio: 24 ore tra audit dello stesso URL esatto, più un breve cooldown globale tra due audit qualsiasi.
  • Restituisce la stessa forma di GET /api/audits/:id. I findings sono vuoti fino a quando l’audit non transita a completed.

Perché è importante

POST /api/audits è l’endpoint giusto quando hai bisogno di un audit persistente che puoi recuperare più tardi, un PDF, dati di traffico organico, o Core Web Vitals. Gli audit anonimi coprono circa 90 check ma saltano PSI; questo endpoint copre tutto.

Flussi concreti:

  • Un bot audit-on-PR chiama POST /api/audits quando un preview deploy parte, cattura l’audit_id, polla GET /api/audits/:id ogni 5 secondi fino a 60 secondi, e posta un commento delta contro l’ultimo audit sullo stesso URL.
  • Un agent cron settimanale itera le landing page più visitate dell’utente e mette in coda un audit fresco per ognuna, poi invia un digest da GET /api/audits.

Come usarlo

L’endpoint è async per design. Tratta la risposta immediata 201 Created come acknowledgement, poi polla GET /api/audits/:id con l’audit_id restituito ogni pochi secondi. Il completamento end-to-end tipico è 10 a 30 secondi; permetti fino a 90 secondi per target lenti.

Richiesta

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

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

Il campo url è obbligatorio, deve essere assoluto (https://...), parsabile e al massimo 2000 caratteri.

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 (in coda + 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

Risposta

201 Created:

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

Campi sulla risposta in coda:

  • id: id intero dell’audit, usato in ogni chiamata successiva (GET /api/audits/:id, PDF, Google).
  • domain: hostname estratto dall’URL.
  • url: riecheggiato dalla richiesta.
  • status: sempre "queued" sulla risposta iniziale. Transita a "running", poi "completed" o "failed".
  • score: null fino al completamento dell’audit.
  • created_at: timestamp ISO 8601.

Una volta che status === "completed", GET /api/audits/:id restituisce la busta completa con score, module_scores, e l’array findings (vedi api-get-audit).

Busta di errore

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

Errori comuni

CodiceQuandoAzione
UNAUTHORIZED (401)Bearer mancante o non validoGenera una chiave su app.metricspot.com/settings/api-keys
QUOTA_EXCEEDED (402)Quota mensile del piano usataUpgrade su app.metricspot.com/billing
INVALID_URL (400)URL non parsabile o > 2000 caratteriPassa un URL assoluto https://
RATE_LIMITED (429)Cooldown per dominio o globaleAspetta la finestra indicata prima di riprovare lo stesso URL
UPSTREAM_FAILED (5xx)Glitch PSI o crawlerRiprova con backoff

Domande frequenti

Quanto tempo fino al completamento dell’audit?

10 a 30 secondi sui siti tipici. Target ricchi di JS o pagine con molti structured data possono richiedere fino a 90 secondi. Polla GET /api/audits/:id ogni 3 a 5 secondi: l’audit è completamente computato quando status passa a completed.

Un audit fallito costa un audit?

Un audit failed non consuma quota del piano. La quota decrementa solo quando l’esecuzione completa con successo e i findings sono memorizzati. I fallimenti di rate-limit PSI sono ritentati internamente prima di emergere.

Perché c’è un cooldown per dominio?

Lo stesso URL auditato due volte in cinque minuti restituisce gli stessi findings: spreca quota PSI e confonde le dashboard di traffico. Il cooldown di 24 ore per URL è applicato lato server. Il messaggio di errore include il timestamp esatto retry_at così i client possono pianificare un re-queue.

Posso mettere in coda molti audit in parallelo?

Sì, fino al cooldown globale (qualche secondo tra due audit qualsiasi per account). Per batch di 50+ URL, spaziali lato client o usa Pro, che alza la quota mensile e stringe il throttle globale.

Fonti

Ultimo aggiornamento 2026-05-14