API et webhooks, version 1
Tout ce que Crawloscope sait de vos sites et de vos crawls : en JSON par l'API, dans Claude, ChatGPT ou Cursor par le serveur MCP, et poussé vers vos outils par des webhooks signés.
Serveur MCP · Ressources · Webhooks · Codes d'erreur
Contrat de la v1 : jamais de changement incompatible (il arriverait sous /api/v2). Les champs peuvent s'ajouter, jamais disparaître ni changer de sens.
Authentification
Trois sortes de jetons, tous présentés en en-tête Authorization: Bearer <jeton> :
- Jeton d'import à distance (application, menu du compte › Import à distance, offre Solo ou Agence) : ouvre toute l'API, import compris.
- Jeton de lecture (
crawl:read, même menu, section « Jetons de lecture », offre Solo ou Agence) : pour le serveur MCP et les outils tiers. Il n'ouvre que les routesGETde l'API v1 listées ci-dessous (ni journal, ni imports, ni comptes, ni jetons) ; l'offre est relue à chaque requête (402si elle ne couvre plus le MCP) ; il est lié à l'espace de travail où il a été créé, expire (365 jours au plus), peut être limité à certains sites, et meurt avec l'appartenance à l'espace. Il n'est jamais accepté dans l'URL (?token=), seulement en en-tête. Les droits du compte sont relus à chaque requête.
Depuis le 13 octobre 2026, le secret d'un jeton n'est plus conservé par Crawloscope : il est affiché une seule fois à la création. Les jetons créés avant restent valides.
curl -H "Authorization: Bearer $CRAWLOSCOPE_TOKEN" https://crawloscope.com/api/v1/crawls
Serveur MCP : brancher Claude, Cursor et les autres assistants
Crawloscope s'interroge depuis un client MCP (Model Context Protocol) grâce au serveur crawloscope-mcp, servi par ce site et installé par npx : rien à cloner, rien à compiler. Quatorze outils, tous en lecture seule, décrits plus bas (« Première question »). Chaque réponse porte des identifiants stables, des dates et un lien de preuve vers Crawloscope.
Trois façons de le brancher. Connecteur OAuth, la voie normale : vous donnez l'adresse https://mcp.crawloscope.com/mcp à l'assistant, il vous renvoie sur crawloscope.com pour autoriser l'accès en un clic ; claude.ai, Claude Desktop, ChatGPT, Claude Code, Cursor et tout client MCP conforme se branchent ainsi par OAuth. Jeton de lecture en en-tête, pour un script ou un client sans OAuth. Sur votre poste par npx, pour les clients en stdio (Node.js 20 ou plus). Dans tous les cas, une offre Solo ou Agence.
1. Connecteur OAuth : l'adresse suffit
Donnez https://mcp.crawloscope.com/mcp à votre assistant. Il découvre le serveur d'autorisation de Crawloscope, vous ouvre une page de connexion et de consentement sur crawloscope.com, puis reçoit un jeton de lecture d'une heure qu'il renouvelle lui-même tant que vous ne révoquez pas. Rien à copier. Révocation : menu du compte › Import à distance › Assistants connectés.
- claude.ai et Claude Desktop : Paramètres › Connecteurs › Ajouter un connecteur personnalisé, collez l'adresse, « Connecter », autorisez.
- ChatGPT : Paramètres › Connecteurs › Créer (mode développeur), adresse du serveur, authentification OAuth, autorisez.
- Claude Code :
claude mcp add --transport http crawloscope https://mcp.crawloscope.com/mcp, puis/mcp› crawloscope › Authenticate. - Cursor :
{"mcpServers": {"crawloscope": {"url": "https://mcp.crawloscope.com/mcp"}}}dansmcp.json, puis le bouton « Login » du serveur.
Le consentement est donné par un compte éditeur d'une offre Solo ou Agence ; un compte client en lecture ne peut pas brancher d'assistant. L'assistant voit les sites de votre espace de travail, avec vos droits, relus à chaque requête.
2. Jeton de lecture en en-tête : scripts et clients sans OAuth
Dans l'application : menu du compte (avatar) › Import à distance › API et scripts › Créer un jeton de lecture. Nom libre, durée de 30 jours à 1 an, et si vous le souhaitez une liste de sites : le lecteur ne verra que ceux-là. Le jeton est affiché une seule fois, avec un exemple d'appel à l'API ; l'usage avec un client MCP sans OAuth est documenté ici seulement, avec VOTRE_JETON à remplacer.
Le serveur https://mcp.crawloscope.com/mcp est sans état : chaque requête porte le jeton dans l'en-tête Authorization, il n'y a ni session ni donnée conservée. Limite : 120 requêtes par minute et par jeton.
Claude Code :
claude mcp add --transport http crawloscope https://mcp.crawloscope.com/mcp --header "Authorization: Bearer VOTRE_JETON" claude mcp list # crawloscope : ✓ Connected
Cursor (~/.cursor/mcp.json ou .cursor/mcp.json) et tout client qui accepte une URL et des en-têtes :
{
"mcpServers": {
"crawloscope": {
"url": "https://mcp.crawloscope.com/mcp",
"headers": { "Authorization": "Bearer VOTRE_JETON" }
}
}
}
3. Sur votre poste, par npx
Même serveur, lancé par le client, pour les clients en stdio ou sans accès réseau sortant maîtrisé.
Claude Code, dans un terminal, une seule fois :
claude mcp add crawloscope -e CRAWLOSCOPE_TOKEN=VOTRE_JETON -e CRAWLOSCOPE_API_URL=https://crawloscope.com -- npx -y https://crawloscope.com/mcp/crawloscope-mcp.tgz claude mcp list # crawloscope : ✓ Connected
Claude Desktop : Paramètres › Développeur › Modifier la configuration ouvre claude_desktop_config.json. Collez ce bloc (ou ajoutez l'entrée crawloscope dans votre mcpServers existant), puis quittez et relancez Claude Desktop. Le serveur apparaît dans le menu des outils de la conversation.
{
"mcpServers": {
"crawloscope": {
"command": "npx",
"args": ["-y", "https://crawloscope.com/mcp/crawloscope-mcp.tgz"],
"env": {
"CRAWLOSCOPE_TOKEN": "VOTRE_JETON",
"CRAWLOSCOPE_API_URL": "https://crawloscope.com"
}
}
}
}
Sous Windows, Claude Desktop lance mieux les commandes via l'interpréteur : "command": "cmd" et "args": ["/c", "npx", "-y", "https://crawloscope.com/mcp/crawloscope-mcp.tgz"].
Cursor : même bloc JSON dans ~/.cursor/mcp.json (tous vos projets) ou dans .cursor/mcp.json à la racine d'un projet, puis Cursor › Settings › MCP pour vérifier que le serveur est actif.
Autre client MCP en stdio : commande npx -y https://crawloscope.com/mcp/crawloscope-mcp.tgz, variables d'environnement CRAWLOSCOPE_TOKEN et CRAWLOSCOPE_API_URL. Le jeton voyage dans l'en-tête Authorization, jamais dans l'URL ni dans les journaux (JSON sur stderr). Variables optionnelles : CRAWLOSCOPE_TIMEOUT_MS (défaut 20 000), CRAWLOSCOPE_MAX_RESPONSE_BYTES (défaut 8 Mo), CRAWLOSCOPE_LOG_LEVEL (debug, info, warn, error, silent).
4. Première question
« Quels sont mes sites dans Crawloscope ? », puis « Quels sont les problèmes critiques du dernier crawl de mon-site.fr ? », « Qu'est-ce qui reste ouvert côté actions ? », « Résume ce qui a changé depuis le crawl précédent ». Séquence type que l'assistant suit de lui-même : list_sites → get_crawl_summary → list_issues → list_tickets → get_site_report.
| Outil | Ce qu'il renvoie |
|---|---|
list_sites | Vos profils de site (recherche par nom ou domaine), pour obtenir un siteId. |
list_crawls | Les versions d'un site : état (complete, partial, failed), date de mesure, score, synthèse. |
get_crawl_summary | Score de santé, catégories, problèmes par gravité, couverture, limites, lien de preuve. Par défaut le dernier crawl complet du site. |
list_issues | Les problèmes d'un crawl par règle, filtrables par gravité, catégorie, règle ; les contrôles non applicables sont dits tels. |
list_tickets | Les actions suivies, avec leur état déclaré par l'équipe et leur état observé dans le crawl. |
get_site_report | Le compte rendu du site : période, changements, corrections, problèmes restants, limites. |
search_pages | Les pages d'un crawl complet par texte (adresse, title, H1), code HTTP, indexabilité, profondeur ou règle déclenchée, avec un lien de preuve. |
get_page | Tout ce que le crawl sait d'une URL : champs mesurés, règles déclenchées avec leur gravité, échantillon des pages qui la citent et de celles qu'elle cite. |
list_issue_urls | Les URLs qui déclenchent une règle (ruleId de list_issues), paginées ; le total est exactement le compteur de list_issues. |
compare_crawls | Un crawl complet comparé à une autre version (par défaut la précédente) : pages ajoutées et retirées, compte avant et après par règle ; avec ruleId, les URLs nouvelles, corrigées ou disparues. |
export_rows | Un fichier CSV de toutes les lignes d'une sélection (URLs d'une règle ou d'une recherche, ou liens), en lien de téléchargement de 15 minutes, sans passer par la conversation. |
get_search_console | Clics, impressions, CTR et position Google du site, par page ou par requête, comparés à la période précédente ; tri par plus forte baisse. |
get_analytics | Sessions, engagement et conversions GA4 par page d'entrée, comparés à la période précédente. Agrégats par page, aucune donnée personnelle. |
find_traffic_at_risk | Le trafic réel croisé avec le dernier crawl : pour chaque page qui reçoit clics, sessions ou conversions, son code HTTP, son indexabilité, sa profondeur et ses problèmes. « Quels problèmes touchent les pages qui rapportent ? » |
Search Console et GA4 sont lus en direct chez Google avec la connexion Google du compte, sur les propriétés associées au site dans Crawloscope (offres Solo et Agence). Si une source n'est pas connectée ou pas associée, la réponse dit quoi faire.
Pour les développeurs : le serveur d'autorisation
Conforme à la spécification MCP « Authorization » (2025-11-25) : le serveur MCP publie ses métadonnées de ressource protégée (RFC 9728) à https://mcp.crawloscope.com/.well-known/oauth-protected-resource et renvoie WWW-Authenticate: Bearer resource_metadata="…", scope="crawl:read" sur un 401 ; le serveur d'autorisation est crawloscope.com, métadonnées RFC 8414 à https://crawloscope.com/.well-known/oauth-authorization-server.
- Inscription dynamique (RFC 7591) sur
/oauth/register:redirect_urisen https (ou http://localhost pour une application locale), clients publics (token_endpoint_auth_method: none) ou confidentiels (secret affiché une fois). Pas de Client ID Metadata Documents pour l'instant. - Autorisation sur
/oauth/authorize:response_type=code, PKCES256obligatoire,redirect_uricomparée à l'exact,scope=crawl:read(seul scope),resource(RFC 8707) parmihttps://mcp.crawloscope.com/mcpet l'API. Une erreur sur le client ou la redirect_uri est affichée, jamais redirigée. - Jetons sur
/oauth/token: code à usage unique (10 minutes, réemploi = consentement révoqué), jeton d'accès d'une heure, jeton de rafraîchissement de 30 jours avec rotation (réemploi = consentement révoqué). Le jeton d'accès est un jeton de lecture ordinaire : liste blanche GET de l'API v1, espace de travail et droits du compte relus à chaque requête. - Validation côté serveur MCP : chaque jeton est vérifié par introspection (RFC 7662,
/oauth/introspect, réservée au serveur MCP) avant tout traitement, audience comprise ; cache d'une minute au plus. Révocation (RFC 7009) sur/oauth/revoke.
Ce qu'il faut savoir
- Lecture seule, par construction. Le jeton de lecture n'ouvre que les routes
GETde l'API v1 listées plus bas : ni import, ni modification, ni partage, ni comptes, ni jetons. L'API elle-même refuse le reste (403). - Périmètre et durée. Un assistant connecté par OAuth se révoque dans « Assistants connectés » ; un jeton de lecture est lié à votre espace de travail, expire à la date choisie, peut être limité à certains sites et se révoque dans la même section. Un collègue retiré de l'espace perd ses accès immédiatement.
- Détail par URL (recherche de pages, fiche d'une page, URLs d'un problème, comparaison de deux crawls) : lu dans un index construit en arrière-plan après chaque import complet, en une minute au plus. Tant qu'il se construit, l'assistant le dit (« en construction, réessayez ») au lieu de répondre « aucun résultat ». Au-delà de 150 000 URLs ou 4,2 millions de liens par crawl, le détail par URL n'est pas disponible et l'assistant le dit ; les compteurs, eux, le restent. Les règles personnalisées d'un site restent au niveau des compteurs.
- Mise à jour. Le serveur distant est toujours à jour. Sur votre poste,
npxtélécharge le package au premier lancement et le garde en cache. Pour forcer une nouvelle version :npx clear-npx-cache, puis relancez le client. - Erreurs. « Jeton refusé » : jeton expiré, révoqué ou mal copié, créez-en un nouveau. Code
402: votre offre ne couvre pas le MCP, le jeton reste valide. « Introuvable » : le site est hors du périmètre du jeton.
Pagination et filtres
Les listes (sites, crawls, issues, tickets) acceptent limit (500 au plus) et cursor ; la réponse porte total et nextCursor (nul sur la dernière page). Le curseur est opaque : renvoyez-le tel quel. Un curseur qui ne correspond plus à rien renvoie 400 plutôt qu'une page vide. Le tri est total et stable : date de mesure décroissante pour les crawls, nom pour les sites, gravité puis volume pour les problèmes, non terminées d'abord pour les actions.
Ressources
| Méthode et chemin | Ce qu'elle renvoie |
|---|---|
GET /api/v1 | Version, lien vers cette page, liste des points d'entrée et limites de pagination. Seule route publique : elle répond sans jeton (scope vaut alors public) et ne porte aucune donnée. |
GET /api/v1/me | Le compte, l'espace de travail courant et, pour un jeton de lecture, son périmètre (sites, expiration). Jamais le secret. |
GET /api/v1/crawls?site=…&state=…&host=…&since=…&until=… | Les versions de crawl visibles, les plus récentes d'abord, paginées : identifiant, nom, hôte, site, URLs, provenance, dates, state (complete, partial = candidate, failed avec failure), measuredAt, score et catégories quand une synthèse existe, summary (date, version des règles, colonnes couvertes, graphe) ou null, lien de preuve. |
GET /api/v1/crawls/:id | Une version. |
GET /api/v1/crawls/:id/issues?severity=…&category=…&rule=… | Les problèmes comptés à l'import (natifs puis personnalisés du site), triés par gravité puis volume : règle, titre, sévérité (critique, alerte, info), catégorie, nombre d'URLs, lien de preuve. Avec status (ready ou unavailable si aucune synthèse : ce n'est pas « zéro problème »), bySeverity, et notApplicable : les contrôles que l'export ne permettait pas d'évaluer (colonne absente). |
GET /api/v1/crawls/:id/pages | Recherche de pages dans l'index détaillé : q (adresse, title, H1), status (404 ou 4xx), indexable, rule, minDepth, maxDepth, allTypes. Chaque page porte ses règles déclenchées et un lien de preuve. Paginé (limit ≤ 200). |
GET /api/v1/crawls/:id/page?url= | Fiche d'une page : enregistrement complet, règles déclenchées, liens entrants et sortants (totaux exacts, échantillon de 10). |
GET /api/v1/crawls/:id/issues/:ruleId/urls | URLs qui déclenchent une règle ; total égale le compteur de /issues. status: "not-applicable" si la colonne manquait dans l'export. |
GET /api/v1/crawls/:id/compare | Comparaison avec with (défaut : version complète précédente du site) : pages ajoutées et retirées, compte avant/après par règle, et avec rule et kind (new, fixed, gone, persisting) les URLs. Une URL disparue du crawl est gone, jamais fixed. |
GET /api/v1/crawls/:id/export | Export CSV de toutes les lignes d'une sélection, en flux : avec rule, les URLs d'une règle ; sinon les filtres de /pages (q, status, indexable, minDepth, maxDepth, allTypes). columns : liste séparée par des virgules, parmi les colonnes du crawl (title, metaDesc, h1, canonical…) et linksIn, linksOut, rules ; une colonne absente répond 400 avec la liste des disponibles. separator : comma (défaut), semicolon, tab. UTF-8 avec BOM ; les cellules qui commencent par =, +, - ou @ sont préfixées d'une apostrophe (protection contre les formules). Le nombre de lignes est dans l'en-tête X-Export-Rows. Règle non applicable : 409. Pour une URL hors 2xx, les colonnes linkSources, linkSourcesTemplate et linkSourcesContent donnent le nombre de pages qui la lient, dont depuis un gabarit (menu, en-tête, pied) et depuis le contenu.kind=links : où sont maillées les URLs hors 2xx (3xx, 4xx, 5xx, bloquées). Une ligne par page source et par lien : source, sourceStatus, target, targetStatus, redirectUrl (le lien à mettre à la place d'une redirection), anchor, zone (nav, header, footer, content), type, follow, occurrences. Cibles filtrées par rule (redirect3xx, err4xx, err5xx… : URLs du site) ou status (3xx, 404… : inclut les liens sortants vers d'autres domaines, pour repérer les liens externes cassés). Tous les liens vers ces URLs sont gardés dans l'index, pas un échantillon (plafond : 2 millions par crawl, signalé s'il est atteint). |
GET /api/v1/crawls/:id/export-link | Pour un client MCP, qui n'a pas le jeton : mêmes paramètres que /export, renvoie le nombre de lignes, les colonnes, un aperçu de 5 lignes et un lien /api/v1/exports/… valable 15 minutes, téléchargeable sans en-tête. Le lien ne vaut que pour cet export et les droits sont relus au téléchargement : jeton révoqué, compte retiré de l'espace ou offre changée, le lien est refusé aussitôt. Réservé aux jetons de lecture. |
Ces routes (de /pages à /export-link) lisent l'index détaillé du crawl. S'il n'est pas prêt, elles répondent 200 avec status: "pending" (construction en cours, retryAfterSeconds) ou "unavailable" (index.reason : version-incomplete, plafond, erreur), jamais une liste vide. | |
GET /api/v1/crawls/:id/report | La configuration du rapport client (résumé, sections, commentaires), la synthèse complète, l'état de la version et status. |
POST /api/v1/crawls?gzip=1&name=…&host=…&links=0|1&source=api | Import d'un crawl : corps application/octet-stream, payload JSON gzippé tel que produit par crawloscope-upload.mjs. Réponse : { crawl: { id, name } }. Alias exact de POST /api/crawls. |
GET /api/v1/sites?q=… | Les profils site, paginés, cherchables par nom ou domaine : identité, suivi (cadence, seuil de régression), lastCompleteCrawlId, compteurs de segments, règles et tickets ouverts. |
GET /api/v1/sites/:id | Un profil site. |
GET /api/v1/sites/:id/crawls?state=complete&limit=1 | Les versions d'un site (mêmes filtres que /crawls). Avec state=complete&limit=1 : la dernière version complète. |
GET /api/v1/sites/:id/tickets?workState=…&observed=… | Les actions du site, avec leurs deux états : workState (déclaré par l'équipe : todo, doing, done ; filtre open = non terminées) et observed (constaté par le dernier crawl comparable : present, absent, unverifiable avec reason et reasonText, unknown). Plus responsable, échéance, ancrage (règle ou URL), crawl de preuve, proofUrl, référence externe, compteurs par état et dictionnaire des motifs. |
GET /api/v1/sites/:id/report | Le compte rendu du site (dernière version complète contre la précédente) : période, changements, corrections observées, problèmes nouveaux ou persistants, actions prioritaires, limites. Identique à la vue et au PDF de l'application. |
GET /api/v1/sites/:id/search | Search Console du site : days (défaut 28, fin à J-3), by (page ou query), sort (clicks, drop, impressions), q, limit. Totaux et période précédente inclus. |
GET /api/v1/sites/:id/analytics | GA4 du site par page d'entrée : sessions, sessions engagées, conversions ; totaux et période précédente. |
GET /api/v1/sites/:id/traffic | Le trafic (Search Console et/ou GA4) croisé avec l'index du dernier crawl complet : only (issues par défaut, critical, not-in-crawl, all), résumé des clics et conversions exposés. |
GET /api/v1/journal?site=… | Le journal d'exécution : fichier reçu, converti, publié, échec, crawl manquant, régression ; 90 jours. |
GET /api/v1/imports | Les conversions en ligne du compte et leurs statuts (queued, converting, uploading, partial, done, error) avec diagnostics fermés et durées mesurées. |
score: null et une liste de problèmes vide : ouvrez-le et enregistrez-le une fois pour le noter.Webhooks
Chaque profil site (offre Agence) peut déclarer un webhook générique. Crawloscope y envoie un POST JSON avec une enveloppe versionnée :
{
"version": "1",
"id": "9b7c…", // identifiant unique de l'envoi
"event": "import.failed", // voir la liste
"at": "2026-08-26T10:12:00.000Z",
"site": { "id": "…", "name": "gold.fr", "host": "gold.fr" },
"data": { … }, // dépend de l'événement
"url": "https://crawloscope.com/app"
}
| Événement | Quand | data |
|---|---|---|
crawl.missing | Le suivi du site est en retard sur sa cadence (une fois par épisode). | entry du journal, text |
import.failed | Une conversion en ligne a échoué (après temporisation, sauf acquittement ou publication entre-temps). | entry avec failure.code et failure.phase |
score.regression | Le score de santé baisse d'au moins le seuil du site par rapport au crawl précédent comparable. | entry.detail |
crawl.published | Un rapport est publié pour le site (option « Envoyer aussi chaque rapport publié »). | crawl, même forme que GET /api/v1/crawls/:id |
test | Message de test depuis la fiche du site. | text |
En-têtes et signature
Chaque envoi porte X-Crawloscope-Event, X-Crawloscope-Version: 1 et, si un secret est enregistré sur le site, X-Crawloscope-Signature: sha256=<HMAC-SHA256 du corps brut avec le secret>. Vérifiez la signature sur le corps tel que reçu, avant tout parsing :
const crypto = require("node:crypto");
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-crawloscope-signature"] || ""));
Politique d'envoi : trois tentatives puis abandon consigné dans le journal du site ; aucun renvoi d'un événement déjà livré. Répondez en moins de huit secondes avec un code 2xx.
Codes d'erreur
| Code | Sens |
|---|---|
401 | Jeton absent, invalide ou révoqué. |
402 | Limite de l'offre atteinte : le corps porte { error, upgrade: true }. |
403 | Le jeton ne permet pas cette action (jeton d'appareil limité à l'import, compte client). |
404 | Ressource introuvable ou invisible pour ce jeton. |
413 | Corps trop volumineux (320 Mo). |
Questions et demandes d'évolution : support@crawloscope.com.