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 Parménion. 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/jobs202 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 Parménion 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