api

POST /api/public/audit

Lancia un audit SEO anonimo one-shot senza chiave API. Risposta sincrona sotto i 60s, 1 audit per IP ogni 24h, senza Core Web Vitals.

Cosa fa questo endpoint

POST /api/public/audit lancia un audit SEO sincrono su qualsiasi URL pubblico senza chiave API. Restituisce la busta completa dell’audit nella stessa risposta: punteggio totale, punteggi per modulo e findings a livello di regola. PageSpeed Insights è saltato per mantenere la chiamata economica e la risposta sotto i 60 secondi.

  • Sincrono: blocca fino alla fine dell’audit, senza polling.
  • Anonimo: identificato dall’IP del client, limitato a 1 audit per IP ogni 24h.
  • Salta Core Web Vitals (LCP, CLS, INP). Per dati PSI, usa il POST /api/audits autenticato.
  • Il risultato non è persistito su un account utente: una riga di rate-limit è salvata sulla tabella audits con user_id = NULL.

Perché è importante

L’audit anonimo è il percorso di minor frizione per il widget “free audit” della home page e per strumenti di terze parti che vogliono mostrare il punteggio MetricSpot prima di chiedere all’utente di iscriversi. Niente OAuth, niente generazione di chiave, niente piano: solo un POST HTTPS.

Flussi concreti:

  • Un builder di landing page incorpora un form “testa il tuo sito” che chiama l’endpoint direttamente dal browser dell’utente e renderizza il punteggio inline.
  • Un bot Slack accetta /audit example.com da qualsiasi collega e riposta il punteggio senza che nessuno nel workspace si debba autenticare contro MetricSpot.

Come usarlo

Invia un POST con un body JSON contenente url. L’URL deve essere assoluto (https://...), parsabile e al massimo 2000 caratteri.

Richiesta

POST /api/public/audit HTTP/1.1
Host: app.metricspot.com
Content-Type: application/json

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

curl

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

Node fetch

const res = await fetch("https://app.metricspot.com/api/public/audit", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ url: "https://example.com" }),
});
const { audit } = await res.json();
console.log(`Punteggio ${audit.total_score} per ${audit.domain}`);

Python httpx

import httpx

r = httpx.post(
    "https://app.metricspot.com/api/public/audit",
    json={"url": "https://example.com"},
    timeout=90.0,
)
print(r.json()["audit"]["total_score"])

Risposta

200 OK con la busta completa dell’audit:

{
  "audit": {
    "url": "https://example.com",
    "domain": "example.com",
    "total_score": 78,
    "module_scores": {
      "technical": 92,
      "onpage": 71,
      "performance": null,
      "ai": 65,
      "modern_seo": 80,
      "social": 88,
      "accessibility": 74,
      "privacy": 82,
      "readability": 70,
      "tech_stack": 100,
      "organic_traffic": 100
    },
    "rules": [
      {
        "module": "onpage",
        "rule_id": "onpage.meta-description",
        "passed": false,
        "severity": "major",
        "title": "Missing meta description",
        "recommendation": "Add a 140-160 character meta description summarizing the page.",
        "data": {}
      }
    ]
  },
  "note": "Anonymous audits are limited and do not include Core Web Vitals. Sign up free for full audits and PDF reports."
}

Campi:

  • audit.url: l’URL auditato, riecheggiato.
  • audit.domain: hostname estratto dall’URL.
  • audit.total_score: punteggio ponderato su tutti i moduli, 0 a 100.
  • audit.module_scores: punteggio per modulo 0 a 100, o null quando il modulo è stato saltato (performance è sempre null per audit anonimi).
  • audit.rules: array di ogni regola valutata. Ogni voce ha module, rule_id, passed (boolean), severity (info | minor | major | critical), title, recommendation opzionale, e data (payload specifico della regola).
  • note: stringa che spiega i limiti anonimi, per visualizzazione UI.

Busta di errore

{ "error": "Rate limit reached", "message": "Sign up free for more reports." }

Errori comuni

CodiceQuandoAzione
INVALID_URL (400)URL non parsabile, schema mancante, o > 2000 caratteriPassa un URL assoluto https://
RATE_LIMITED (429)IP ha già auditato nelle ultime 24hAspetta 24h, o iscriviti a un piano Free (10 audit/mese)
UPSTREAM_FAILED (502)Glitch del crawlerRiprova con backoff breve
INTERNAL (500)Errore backend inattesoRiprova; se persiste, accedi e usa POST /api/audits per un retry in coda

L’endpoint non restituisce 401, 402 o 404: è non-autenticato per design e non ha risorse da cercare.

Domande frequenti

Perché performance è sempre null?

L’endpoint anonimo salta il Google PageSpeed Insights per mantenere il tempo totale sotto i 60 secondi ed evitare di bruciare quota PSI su traffico non-throttlato. Per Core Web Vitals (LCP, CLS, INP), usa il POST /api/audits autenticato, che fa girare PSI in parallelo al crawler.

Come viene applicato il rate limit?

Una riga è inserita nella tabella audits a ogni esecuzione anonima con user_id = NULL e ip_address impostato all’IP del chiamante. Chiamate successive dallo stesso IP entro 24h restituiscono 429. L’IP è letto dagli header standard di reverse proxy (X-Forwarded-For), quindi audit dietro un proxy condiviso possono collidere.

Posso usarlo dal browser?

Sì. L’endpoint imposta CORS permissivo per metricspot.com e accetta POST cross-origin. Per il tuo dominio, fai girare una funzione serverless come proxy sottile: permette di aggiungere rate limit per tenant ed evita di esporre gli IP dei tuoi visitatori a MetricSpot direttamente.

Il risultato è persistito da qualche parte?

Una riga esiste per fini di rate-limit, ma non è collegata a nessun account utente e non è visibile in nessuna dashboard. Per salvare e rivisitare un audit, iscriviti gratis e chiama POST /api/audits.

Fonti

Ultimo aggiornamento 2026-05-14