api

POST /api/public/audit

Lancez un audit SEO anonyme sans clé API. Réponse synchrone en moins de 60s, plafonné à 1 audit par IP toutes les 24h, sans Core Web Vitals.

Ce que fait cet endpoint

POST /api/public/audit lance un audit SEO synchrone sur toute URL publique sans clé API. Il renvoie l’enveloppe complète dans la même réponse : score total, scores par module et findings par règle. PageSpeed Insights est omis pour garder l’appel peu coûteux et la réponse sous 60 secondes.

  • Synchrone : bloque jusqu’à la fin, sans polling.
  • Anonyme : identifié par IP client, plafonné à 1 audit par IP toutes les 24h.
  • Omet Core Web Vitals (LCP, CLS, INP). Pour les données PSI, utilisez l’authentifié POST /api/audits.
  • Le résultat n’est pas persisté sur un compte : seule une ligne de rate-limit est stockée avec user_id = NULL.

Pourquoi c’est important

L’audit anonyme est le chemin de moindre friction pour le widget “audit gratuit” d’une landing et pour les outils tiers qui veulent montrer le score MetricSpot avant de demander une inscription. Pas d’OAuth, pas de clé, pas de plan : juste un POST HTTPS.

Workflows concrets :

  • Un builder de landing intègre un formulaire “testez votre site” qui appelle l’endpoint directement depuis le navigateur de l’utilisateur et affiche le score inline.
  • Un bot Slack accepte /audit example.com de tout coéquipier et reposte le score sans que personne dans le workspace n’ait à s’authentifier auprès de MetricSpot.

Comment l’utiliser

Envoyez un POST avec un corps JSON contenant url. L’URL doit être absolue (https://...), parsable et de 2000 caractères maximum.

Requête

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(`Score ${audit.total_score} for ${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"])

Réponse

200 OK avec l’enveloppe complète :

{
  "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."
}

Champs :

  • audit.url : l’URL auditée, renvoyée en écho.
  • audit.domain : hostname extrait de l’URL.
  • audit.total_score : score pondéré tous modules, de 0 à 100.
  • audit.module_scores : score par module de 0 à 100, ou null si le module a été sauté (par ex. performance est toujours null en audit anonyme).
  • audit.rules : tableau de chaque règle évaluée. Chaque entrée a module, rule_id, passed (booléen), severity (info | minor | major | critical), title, recommendation optionnelle et data (payload propre à la règle).
  • note : chaîne expliquant les limites anonymes, pour affichage UI.

Enveloppe d’erreur

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

Erreurs courantes

CodeQuandAction
INVALID_URL (400)URL non parsable, sans schéma ou > 2000 caractèresPassez une URL absolue https://
RATE_LIMITED (429)Cette IP a déjà audité dans les 24 dernières heuresAttendez 24h ou inscrivez-vous sur Free (10 audits/mois)
UPSTREAM_FAILED (502)Aléa de crawler en amontRéessayez une fois après un court délai
INTERNAL (500)Erreur backend inattendueRéessayez ; si persistant, connectez-vous et utilisez POST /api/audits

L’endpoint ne renvoie pas 401, 402 ni 404 : il est non authentifié par conception et n’a aucune ressource à résoudre.

Questions fréquentes

Pourquoi performance est-il toujours null ?

L’endpoint anonyme omet Google PageSpeed Insights pour garder le temps total sous 60 secondes et ne pas brûler le quota PSI sur du trafic non plafonné. Pour Core Web Vitals (LCP, CLS, INP), utilisez l’authentifié POST /api/audits, qui lance PSI en parallèle du crawler.

Comment le rate limit est-il appliqué ?

Une ligne est insérée dans la table audits à chaque lancement anonyme avec user_id = NULL et ip_address à l’IP de l’appelant. Les appels ultérieurs depuis la même IP sous 24h renvoient 429. L’IP est lue depuis les en-têtes reverse-proxy standard (X-Forwarded-For), donc les audits derrière un proxy partagé peuvent entrer en collision.

Puis-je l’utiliser depuis le navigateur ?

Oui. L’endpoint définit un CORS permissif pour metricspot.com et accepte les POST cross-origin. Pour votre propre domaine, lancez une fonction serverless comme proxy fin : elle permet d’ajouter un rate-limit par tenant et évite d’exposer les IP de vos visiteurs à MetricSpot directement.

Le résultat est-il persisté quelque part ?

Une ligne existe pour le rate-limit, mais elle n’est attachée à aucun compte et n’est visible dans aucun tableau de bord. Pour sauvegarder et revisiter un audit, inscrivez-vous gratuitement et appelez POST /api/audits à la place.

Sources

Dernière mise à jour 2026-05-14