API Marina
Endpoint REST pour générer un rapport d'audit SEO + GEO + IA en marque blanche, depuis n'importe quel site, en ~3 minutes. Conçu pour être intégré en lead-magnet sur une landing page externe, depuis un agent IA, ou via un workflow no-code (Make, n8n, Zapier).
1. Quickstart
Trois appels suffisent : (1) obtenir une clé depuis le dashboard, (2) POST avec une URL cible, (3) polling GET jusqu'à status: "completed".
# 1. Récupérez votre clé depuis Console → API → Marina
export MARINA_KEY="marina_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# 2. Lancez un audit
JOB=$(curl -s -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
-H "x-marina-key: $MARINA_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","lang":"fr"}' | jq -r .job_id)
# 3. Polling toutes les 5s jusqu'à completion
while true; do
R=$(curl -s "https://tutlimtasnjabdfhpewu.functions.supabase.co/marina?job_id=$JOB" -H "x-marina-key: $MARINA_KEY")
echo "$R" | jq .status
echo "$R" | jq -e '.status == "completed"' >/dev/null && break
sleep 5
done2. Authentification
Toutes les requêtes externes utilisent le header x-marina-key. La clé est liée à votre compte Crawlers et hérite de votre plan (quotas, branding, langues disponibles). Deux variantes sont également acceptées : le champ api_key dans le body JSON, et un Authorization: Bearer <JWT> pour un utilisateur connecté au dashboard.
POST /functions/v1/marina HTTP/1.1
Host: tutlimtasnjabdfhpewu.functions.supabase.co
x-marina-key: marina_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/jsonGénérer / régénérer une clé : Console → API → Marina → Générer une clé (équivalent technique : GET ?action=generate_key avec le JWT utilisateur, et non une clé Marina). La régénération invalide immédiatement l'ancienne clé.
3. Créer un rapport
Body
| Champ | Type | Requis | Description |
|---|---|---|---|
| url | string | oui | URL à auditer (http ou https, page d'accueil recommandée). |
| lang | "fr" | "en" | "es" | non | Force la langue du rapport. Auto-détectée si absent. |
| callback_url | string (URL) | non | Webhook appelé en POST à la fin du job (voir §5). |
Réponse 200 — Job créé
{
"job_id": "8f3a9b2c-1234-5678-90ab-cdef01234567",
"status": "pending" // ou "queued" si un autre job est en cours
// "queue_position": 2 // présent uniquement si status === "queued"
}4. Récupérer le résultat
Réponses possibles
En cours
{
"status": "pending" | "processing",
"progress": 42, // pourcentage 0-100
"phase": "audit_seo" | "cocoon" | "geo" | "rendering"
}Terminé
{
"success": true,
"status": "completed",
"data": {
"url": "https://example.com",
"domain": "example.com",
"language": "fr",
"report_url": "https://.../rapport.html", // URL signée du rapport HTML
"report_view_url": "https://crawlers.fr/r/8f3a9b2c-...", // lecteur public
"report_path": "reports/8f3a9b2c/rapport.html",
"expert_seo_score": 78,
"expert_seo_max": 100,
"strategic_score": 64, // couche stratégique GEO / IA
"cocoon_nodes": 42,
"cocoon_clusters": 6,
"visual_capture": { "desktop": "https://...", "mobile": "https://..." },
"partial": false,
"degraded_reasons": [],
"generated_at": "2026-05-26T14:32:11Z"
}
}Livraison partielle
Le rapport est livré mais une couche non bloquante (le plus souvent la couche stratégique GEO) est indisponible. Traitez ce cas comme un succès exploitable.
{
"success": true,
"status": "partial",
"warning": "Couche stratégique indisponible",
"data": { "...": "identique à completed", "partial": true, "degraded_reasons": ["strategic_timeout"] }
}Échec
{
"success": false,
"status": "failed",
"error": "Crawl timeout after 180s"
}Polling recommandé : toutes les 5 à 10 secondes. Durée typique d'un job : 2 à 4 minutes. Au-delà de 10 min sans complétion, le job est marqué failed automatiquement.
5. Webhook callback (optionnel)
Plutôt que de poller, fournissez un callback_url à la création du job. Marina enverra un POST JSON à cette URL à la fin du job (succès, livraison partielle ou échec). Le nom de l'événement est répété dans le header x-marina-event.
POST {callback_url}
Content-Type: application/json
x-marina-event: marina.report.completed // ou marina.report.partial | marina.report.failed
// Succès (ou partial) — le corps reprend exactement l'objet "data" du polling
{
"event": "marina.report.completed",
"job_id": "8f3a9b2c-...",
"url": "https://example.com",
"domain": "example.com",
"language": "fr",
"report_url": "https://.../rapport.html",
"report_view_url": "https://crawlers.fr/r/8f3a9b2c-...",
"expert_seo_score": 78,
"expert_seo_max": 100,
"strategic_score": 64,
"partial": false,
"generated_at": "2026-05-26T14:32:11Z"
}
// Échec
{
"event": "marina.report.failed",
"job_id": "8f3a9b2c-...",
"url": "https://example.com",
"domain": "example.com",
"phase": "phase2",
"error": "Crawl timeout",
"failed_at": "2026-05-26T14:32:11Z"
}Le webhook est appelé une seule fois, sans retry. Votre endpoint doit répondre 2xx en moins de 10 secondes.
6. Langues
Marina supporte FR, EN, ES. Si lang n'est pas fourni, l'algorithme analyse <title>, <meta description>, H1/H2 puis l'attribut <html lang> en dernier recours. Le fallback final est fr.
7. Lister / annuler / supprimer un job
Lister vos jobs récents
curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
-H "x-marina-key: $MARINA_KEY" \
-H "Content-Type: application/json" \
-d '{"action":"list_jobs","limit":50}'Annuler un job en cours
curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
-H "x-marina-key: $MARINA_KEY" \
-H "Content-Type: application/json" \
-d '{"action":"cancel_job","job_id":"8f3a9b2c-..."}'Supprimer un job terminé
curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
-H "x-marina-key: $MARINA_KEY" \
-H "Content-Type: application/json" \
-d '{"action":"delete_job","job_id":"8f3a9b2c-..."}'8. Codes d'erreur
| Code | Cas | Action |
|---|---|---|
| 400 | url manquante ou callback_url invalide | Corriger le body. |
| 401 | x-marina-key absent ou invalide | Vérifier la clé dans Console → API. |
| 404 | job_id introuvable (poll) | Vérifier l'ID retourné par le POST initial. |
| 409 | Un job est déjà en cours sur ce domaine | Le job est mis en file (status "queued") plutôt que rejeté. |
| 500 | Erreur interne pipeline | Réessayer ; si persistant, contacter le support. |
9. Exemple complet TypeScript
// marina.ts — client minimal, zéro dépendance
const MARINA_KEY = process.env.MARINA_KEY!;
const BASE = "https://tutlimtasnjabdfhpewu.functions.supabase.co/marina";
export async function auditWithMarina(url: string, lang?: "fr" | "en" | "es") {
// 1. Créer le job
const create = await fetch(BASE, {
method: "POST",
headers: { "x-marina-key": MARINA_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ url, lang }),
});
if (!create.ok) throw new Error(`Create failed: ${create.status}`);
const { job_id } = await create.json();
// 2. Poll toutes les 5s, timeout 10 min
const deadline = Date.now() + 10 * 60_000;
while (Date.now() < deadline) {
await new Promise((r) => setTimeout(r, 5000));
const poll = await fetch(`${BASE}?job_id=${job_id}`, {
headers: { "x-marina-key": MARINA_KEY },
});
const json = await poll.json();
if (json.status === "completed" || json.status === "partial") return json.data; // { report_url, expert_seo_score, strategic_score, ... }
if (json.status === "failed") throw new Error(json.error);
}
throw new Error("Timeout");
}
// Usage
const report = await auditWithMarina("https://example.com", "fr");
console.log(report.report_url);10. Intégration depuis Lovable / Edge Function
Stockez MARINA_KEY en secret, puis appelez l'API depuis une edge function. Exemple Deno :
// supabase/functions/audit-prospect/index.ts
import { corsHeaders } from "../_shared/cors.ts";
Deno.serve(async (req) => {
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
const { url } = await req.json();
const res = await fetch("https://tutlimtasnjabdfhpewu.functions.supabase.co/marina", {
method: "POST",
headers: {
"x-marina-key": Deno.env.get("MARINA_KEY")!,
"Content-Type": "application/json",
},
body: JSON.stringify({
url,
lang: "fr",
callback_url: `${Deno.env.get("SUPABASE_URL")}/functions/v1/marina-webhook`,
}),
});
const { job_id } = await res.json();
return new Response(JSON.stringify({ job_id }), {
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
});11. Carte d'identité (phase 0, pré-crawl)
Avant de lancer un audit, Marina résout une carte d'identité du site (secteur, modèle commercial, cible, offre, zone commerciale). Elle calibre les seuils de sévérité et les cibles de mix de gabarits. Vous pouvez la lire, la recalculer avec vos propres valeurs, puis la verrouiller pour qu'elle soit utilisée par l'audit.
| action | Body | Effet |
|---|---|---|
| identity_options | — | Listes de référence (secteurs, modèles commerciaux, types d'entité). |
| identity_resolve | url, force? | Résolution automatique (cache, sinon inférence). Réponse { card, locked, options }. |
| identity_recompute | url, fields | Prévisualisation déterministe (aucune écriture) avec vos champs. |
| identity_lock | url, fields | Persiste et verrouille la carte : l'audit l'utilisera telle quelle. |
curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
-H "x-marina-key: $MARINA_KEY" -H "Content-Type: application/json" \
-d '{"action":"identity_lock","url":"https://example.com","fields":{
"sector":"renovation_batiment",
"commercialModel":"local_service",
"productsServices":"Rénovation complète, isolation, toiture",
"targetAudience":"Propriétaires particuliers",
"commercialArea":"Provence"}}'Le verrouillage nécessite que le domaine soit suivi par votre compte ; sinon la carte est renvoyée en prévisualisation seulement.
12. Multipages
Il n'existe pas d'action batch côté API : un audit multipages est une boucle client sur l'endpoint standard, une URL par job (jusqu'à 15 URLs dans l'interface Marina, 5 crédits par rapport). Espacez les créations de jobs d'environ 20 secondes ; les jobs du même domaine sont mis en file et partagent le crawl en cours plutôt que de le relancer.
Besoin d'aller plus loin ? Consultez la liste de toutes les intégrations API ou contactez le support.