api
POST /api/audits
Mettez en file un audit SEO complet avec Core Web Vitals. Renvoie audit_id + status: queued en 1-2s. Compte contre le plan, respecte le cooldown par domaine.
Ce que fait cet endpoint
POST /api/audits met en file un audit SEO et lisibilité IA complet et renvoie immédiatement l’enveloppe. C’est la contrepartie authentifiée et complète de POST /api/public/audit.
- Met en file de manière asynchrone et répond en 1-2 secondes avec
status: "queued"et un vraiaudit_id. - Tire Core Web Vitals (LCP, CLS, INP) depuis Google PageSpeed Insights dans le cadre du run.
- Compte contre le quota du plan (Free 10/mois, Starter 50/mois, Pro illimité).
- Respecte les cooldowns par domaine : 24h entre audits de la même URL exacte, plus un court cooldown global entre deux audits quelconques.
- Renvoie la même forme que
GET /api/audits/:id. Les findings sont vides jusqu’à ce que l’audit passe àcompleted.
Pourquoi c’est important
POST /api/audits est le bon endpoint dès qu’il faut un audit persistant à récupérer plus tard, un PDF, des données de trafic organique ou Core Web Vitals. Les audits anonymes couvrent environ 90 vérifications mais sautent PSI ; cet endpoint couvre tout.
Workflows concrets :
- Un bot audit-sur-PR appelle
POST /api/auditsquand un preview-deploy part, capture l’audit_id, pollGET /api/audits/:idtoutes les 5 secondes jusqu’à 60 secondes et poste un commentaire delta vs le dernier audit de la même URL. - Un cron hebdomadaire itère sur les landings les plus visitées du compte, met en file un nouvel audit pour chacune et envoie un digest depuis
GET /api/audits.
Comment l’utiliser
L’endpoint est asynchrone par conception. Traitez le 201 Created immédiat comme un accusé de réception et poll GET /api/audits/:id avec l’audit_id renvoyé toutes les quelques secondes. Le temps bout-en-bout typique est de 10 à 30 secondes ; jusqu’à 90 secondes pour les cibles lentes.
Requête
POST /api/audits HTTP/1.1
Host: app.metricspot.com
Authorization: Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{ "url": "https://example.com" }
Le champ url est requis, doit être absolu (https://...), parsable et de 2000 caractères maximum.
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 (mise en file + 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
Réponse
201 Created :
{
"audit": {
"id": 12345,
"domain": "example.com",
"url": "https://example.com",
"status": "queued",
"score": null,
"created_at": "2026-05-14T10:18:04.000Z"
}
}
Champs de la réponse de mise en file :
id: id entier d’audit, utilisé dans chaque appel suivant (GET /api/audits/:id, PDF, Google).domain: hostname extrait de l’URL.url: écho de la requête.status: toujours"queued"en réponse initiale. Passe à"running"puis"completed"ou"failed".score:nulljusqu’à la fin.created_at: horodatage ISO 8601.
Une fois status === "completed", GET /api/audits/:id renvoie l’enveloppe complète avec score, module_scores et le tableau findings (voir api-get-audit).
Enveloppe d’erreur
{ "error": "Quota exceeded", "message": "Upgrade to Starter for 50 audits per month." }
Erreurs courantes
| Code | Quand | Action |
|---|---|---|
UNAUTHORIZED (401) | Bearer manquant ou invalide | Créez une clé sur app.metricspot.com/settings/api-keys |
QUOTA_EXCEEDED (402) | Quota mensuel épuisé | Upgrade sur app.metricspot.com/billing |
INVALID_URL (400) | URL non parsable ou > 2000 caractères | Passez une URL absolue https:// |
RATE_LIMITED (429) | Cooldown par domaine ou global | Attendez la fenêtre indiquée avant de retenter la même URL |
UPSTREAM_FAILED (5xx) | Aléa PSI ou crawler en amont | Réessayez une fois avec backoff |
Questions fréquentes
Combien de temps avant que l’audit soit completed ?
10 à 30 secondes sur les sites typiques. Cibles riches en JS ou pages avec beaucoup de structured data peuvent prendre 90 secondes. Poll GET /api/audits/:id toutes les 3-5 secondes : l’audit est complet quand status bascule sur completed.
Un audit échoué consomme-t-il du quota ?
Un audit failed ne consomme pas de quota. Le compteur ne décrémente que lorsque le run réussit et que les findings sont stockés. Les échecs de rate-limit PSI sont retentés en interne avant remontée.
Pourquoi un cooldown par domaine ?
La même URL auditée deux fois en cinq minutes renvoie les mêmes findings : ça brûle le quota PSI et brouille les dashboards de trafic. Le cooldown de 24h par URL est appliqué côté serveur. Le message d’erreur inclut l’horodatage retry_at exact pour que les clients puissent reprogrammer.
Puis-je mettre en file plusieurs audits en parallèle ?
Oui, jusqu’au cooldown global (quelques secondes entre deux audits quelconques par compte). Pour des lots de 50+ URL, espacez-les côté client ou passez à Pro, qui lève à la fois le quota mensuel et resserre le throttle global.
Sources
Dernière mise à jour 2026-05-14