API Reference · v1

API Crawlers

Une seule clé crw_live_… pour interroger les 18 modules d'analyse SEO, GEO et IA de Crawlers. Modèle asynchrone par polling : vous créez un job, vous interrogez son statut, vous récupérez le résultat structuré en JSON.

1. Vue d'ensemble

L'API Crawlers expose chaque fonctionnalité de la plateforme en endpoint individuel, via un dispatcher unique. Vous postez { feature, input } sur /v1/jobs, vous récupérez un job_id, puis vous interrogez /v1/jobs/{id} jusqu'à status = "succeeded".

Base URL

https://tutlimtasnjabdfhpewu.functions.supabase.co/crawlers-api/v1

Pour le pipeline éditorial complet (publication CMS), voir l'API Périclès. Pour la génération de rapports B2B en marque blanche, voir l'API Marina.

2. Authentification

Toutes les requêtes (sauf /v1/features et /health) requièrent une clé API personnelle au format crw_live_…. La clé est stockée côté Crawlers sous forme de hash SHA-256 ; elle n'est affichée qu'une seule fois à sa création.

Deux modes équivalents :

Authorization: Bearer crw_live_xxxxxxxxxxxxxxxxxxxxxxxx
# ou
x-crawlers-key: crw_live_xxxxxxxxxxxxxxxxxxxxxxxx

Création depuis votre compte → onglet API. La clé peut être révoquée à tout moment.

3. Cycle de vie d'un job

  1. POST /v1/jobs → 202 Accepted, statut queued.
  2. Le worker passe le job en running dans la seconde qui suit.
  3. Vous polliez GET /v1/jobs/{id} toutes les 2–10 s (selon module).
  4. Statut final : succeeded (champ result rempli) ou failed (champ error).
  5. Un job peut être annulé avec POST /v1/jobs/{id}/cancel tant qu'il n'est pas terminé.

Durées typiques : pagespeed 10–30 s, geo_score 20–60 s, audit_expert 1–3 min, site_crawl 2–15 min selon la taille.

4. Endpoints

GET
/v1/features

Liste les modules disponibles (public, sans clé).

curl https://tutlimtasnjabdfhpewu.functions.supabase.co/crawlers-api/v1/features
POST
/v1/jobs

Crée un job pour le module choisi. Débite 0,10 € du wallet (atomique : si le solde est insuffisant, le job est marqué failed et aucun débit n'est appliqué).

Requête

curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/crawlers-api/v1/jobs \
  -H "Authorization: Bearer crw_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "feature": "geo_score",
    "input": { "url": "https://example.com/blog/article" }
  }'

202 Accepted — job créé, débit effectué

Header Location: /v1/jobs/<id>. Le client poll poll_url.

{
  "id": "5d6e7f12-...-9a0b",
  "feature": "geo_score",
  "status": "queued",
  "created_at": "2026-05-26T10:00:00Z",
  "cost_cents": 10,
  "poll_url": "/v1/jobs/5d6e7f12-...-9a0b"
}

400 Bad Request — payload invalide

// JSON malformé
{ "error": "invalid_json" }

// feature absent
{ "error": "missing_field", "field": "feature" }

// feature inconnue
{ "error": "unknown_feature", "feature": "foo", "see": "/v1/features" }

// input n'est pas un objet
{ "error": "invalid_input", "expected": "object" }

401 Unauthorized — authentification

// Aucune clé fournie
{ "error": "missing_api_key", "docs": "/docs/api/crawlers#authentication" }

// Clé inconnue, révoquée ou mal formée
{ "error": "invalid_api_key", "docs": "/docs/api/crawlers#authentication" }

402 Payment Required — solde wallet insuffisant

Le job est créé puis marqué failed avec error.message = "insufficient_balance". Aucun débit n'a lieu. Rechargez sur /developers/profil?tab=facturation.

{
  "error": "insufficient_balance",
  "message": "Recharge ton wallet sur /developers/profil?tab=facturation",
  "job_id": "5d6e7f12-...-9a0b"
}

500 Internal Server Error — erreur serveur

// Erreur base de données (insert job)
{ "error": "db_error", "detail": "<message Postgres>" }

// Exception non gérée dans le router
{ "error": "internal_error", "message": "<message>" }

À retraiter avec backoff exponentiel (1 s, 2 s, 4 s, max 5 tentatives).

GET
/v1/jobs/{id}

Récupère l'état d'un job et son résultat si status = "succeeded".

{
  "id": "5d6e7f12-...-9a0b",
  "feature": "geo_score",
  "status": "succeeded",
  "input": { "url": "https://example.com/blog/article" },
  "result": {
    "score": 78,
    "breakdown": { "machine_readability": 82, "fan_out": 71, "citation": 80 }
  },
  "error": null,
  "created_at": "2026-05-26T10:00:00Z",
  "started_at": "2026-05-26T10:00:01Z",
  "completed_at": "2026-05-26T10:00:42Z"
}
GET
/v1/jobs

Liste vos derniers jobs (paramètre ?limit=20, max 100).

POST
/v1/jobs/{id}/cancel

Annule un job queued ou running. Renvoie 409 si le job est déjà terminé.

5. Catalogue des 18 modules

Chaque module est appelé via { "feature": "<id>", "input": { … } }.

IDModuleInput attenduStatut
audit_expert
Audit Expert (168 critères)
Audit technique, sémantique et E-E-A-T sur 168 critères.
url: string (https)
preview
machine_layer
Machine Layer Scanner
Lisibilité bots IA, JSON-LD, robots.txt, sitemap, fan-out.
url: string
preview
eeat
Score E-E-A-T
Expertise, autorité, fiabilité (scoring v3).
url: string
preview
site_crawl
Crawl de site
Crawl BFS d'un domaine (3 niveaux de profondeur max).
domain: string
depth: number?
limit: number?
preview
pagespeed
PageSpeed Insights
Mobile + desktop, Core Web Vitals (LCP, CLS, INP).
url: string
preview
audit_matrix
Matrice d'audit
Multi-pages, export CSV-ready.
domain: string
urls: string[]?
preview
semantic_audit
Audit sémantique
Champ lexical, entités, alignement intention (Lexical Footprint v2).
url: string
keyword: string?
preview
cocoon
Cocoon sémantique 3D
Clusters, cannibalisation, profondeur, liens internes.
domain: string
preview
content_architect
Content Architect
Recommandation éditoriale en 4 étages (brief / stratège / rédacteur / tonalisateur).
domain: string
topic: string
target_audience: string?
preview
autopilot_status
Autopilote (statut)
Lecture du statut Périclès pour un domaine.
domain: string
preview
conversion_optimizer
Conversion Optimizer
CTA, friction, GA4 behavioral metrics.
url: string
preview
social_hub
Social Hub
Génère des variations sociales depuis un article.
url: string
platforms: string[]?
preview
geo_score
Score GEO
Generative Engine Optimization (lisibilité par moteurs IA).
url: string
preview
llm_visibility
Visibilité LLM
Présence d'une marque dans ChatGPT, Perplexity, Gemini, Claude.
brand: string
queries: string[]
preview
ai_bots_analysis
Analyse Bots IA
Hits bots IA (GPTBot, ClaudeBot, PerplexityBot, …) sur 30 jours.
domain: string
preview
observatory
Observatoire sectoriel
Benchmarks (positions moyennes, mix bots, IAS) par secteur.
sector: string
preview
serp_ranking
Ranking SERP
Positions multi-providers (DataForSEO + SerpAPI + Serper + Bright Data).
keyword: string
domain: string?
location: string?
preview
competitors
Concurrence
Audit concurrentiel (SEO + GEO + SERP delta) sur 1 à 3 URLs.
domain: string
competitors: string[]
preview

6. Codes d'erreur

HTTPCodeDescription
400missing_fieldChamp obligatoire manquant (souvent feature).
400unknown_featureL'ID de module n'existe pas. Voir GET /v1/features.
400invalid_inputLe champ input doit être un objet JSON.
400invalid_jsonLe corps de la requête n'est pas du JSON valide.
401missing_api_keyAucune clé fournie dans Authorization ou x-crawlers-key.
401invalid_api_keyClé inconnue, mal formée ou révoquée.
402insufficient_balanceSolde wallet < 0,10 €. Le job est créé puis marqué failed sans débit. Rechargez sur /developers/profil?tab=facturation.
404job_not_foundLe job n'existe pas, ou ne vous appartient pas.
409not_cancellableLe job est déjà terminé (succeeded/failed/cancelled).
429rate_limitedQuota dépassé. Voir l'en-tête Retry-After.
500internal_errorErreur côté Crawlers. Réessayez avec backoff exponentiel.

7. Exemples

Node.js (fetch natif)

const KEY = process.env.CRAWLERS_API_KEY;
const BASE = "https://tutlimtasnjabdfhpewu.functions.supabase.co/crawlers-api/v1";

async function run(feature, input) {
  // 1. créer le job
  const r = await fetch(`${BASE}/jobs`, {
    method: "POST",
    headers: { "Authorization": `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ feature, input }),
  });
  const { id } = await r.json();

  // 2. polling
  while (true) {
    await new Promise(r => setTimeout(r, 3000));
    const s = await fetch(`${BASE}/jobs/${id}`, { headers: { "Authorization": `Bearer ${KEY}` } });
    const job = await s.json();
    if (job.status === "succeeded") return job.result;
    if (job.status === "failed")    throw new Error(JSON.stringify(job.error));
  }
}

const score = await run("geo_score", { url: "https://example.com/blog/article" });
console.log(score);

PHP (cURL)

<?php
$KEY = getenv('CRAWLERS_API_KEY');
$BASE = 'https://tutlimtasnjabdfhpewu.functions.supabase.co/crawlers-api/v1';

function crawlers_run($feature, $input) {
  global $KEY, $BASE;
  $ch = curl_init("$BASE/jobs");
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $KEY", "Content-Type: application/json"],
    CURLOPT_POSTFIELDS => json_encode(["feature" => $feature, "input" => $input]),
  ]);
  $job = json_decode(curl_exec($ch), true); curl_close($ch);

  while (true) {
    sleep(3);
    $ch = curl_init("$BASE/jobs/{$job['id']}");
    curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $KEY"]]);
    $r = json_decode(curl_exec($ch), true); curl_close($ch);
    if ($r['status'] === 'succeeded') return $r['result'];
    if ($r['status'] === 'failed')    throw new RuntimeException(json_encode($r['error']));
  }
}

Python (requests)

import os, time, requests

KEY = os.environ["CRAWLERS_API_KEY"]
BASE = "https://tutlimtasnjabdfhpewu.functions.supabase.co/crawlers-api/v1"
H = {"Authorization": f"Bearer {KEY}"}

def run(feature, input):
    r = requests.post(f"{BASE}/jobs", headers={**H, "Content-Type": "application/json"},
                      json={"feature": feature, "input": input})
    job_id = r.json()["id"]
    while True:
        time.sleep(3)
        job = requests.get(f"{BASE}/jobs/{job_id}", headers=H).json()
        if job["status"] == "succeeded": return job["result"]
        if job["status"] == "failed":    raise RuntimeError(job["error"])

print(run("geo_score", {"url": "https://example.com/blog/article"}))

8. Quotas & limites

  • Free : 20 jobs / jour, 1 job concurrent.
  • Premium : 500 jobs / jour, 3 jobs concurrents.
  • Pro Agency : 5 000 jobs / jour, 10 jobs concurrents.
  • Pro Agency Premium : 25 000 jobs / jour, 30 jobs concurrents.

Les dépassements renvoient 429 avec un en-tête Retry-After (secondes).

Besoin d'aide pour intégrer ? Notre équipe peut vous accompagner sur les schémas d'input et le pipeline asynchrone.

Nous contacter