> ## Documentation Index
> Fetch the complete documentation index at: https://sorank.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Utiliser l’API publique SORANK

> S’authentifier, consulter les quotas, créer du contenu et suivre les opérations API.

## Quelle adresse utiliser ?

Connectez-vous et gérez vos clés dans l’application sur `https://app.sorank.com/settings/api`. Envoyez les requêtes à `https://app.sorank.com/api/public/v1` : vous n’avez aucun serveur API à créer.

## Premier appel, étape par étape

1. Ouvrez **Paramètres → Accès API** dans l’application SORANK et cliquez sur **Créer une clé**.
2. Nommez la clé, sélectionnez au moins un site, cochez **Voir les sites** (`profiles:read`), choisissez une durée de validité et créez-la. Ajoutez **Voir les quotas** (`quotas:read`) pour effectuer le second appel ci-dessous.
3. Copiez immédiatement la clé complète : elle n’est affichée qu’une fois. Conservez-la dans un gestionnaire de secrets ou une variable d’environnement locale. Le préfixe affiché ensuite dans les paramètres ne permet pas de s’authentifier.
4. Choisissez un exemple ci-dessous. Avec cURL, saisissez la clé lorsqu’elle est demandée. Avec JavaScript ou Go, fournissez-la dans la variable d’environnement `SORANK_API_KEY` du processus serveur. L’onglet HTML contient aussi un petit backend Node.js local : lancez-le avec la clé dans son environnement, puis ouvrez `http://127.0.0.1:3100`. La clé ne doit jamais figurer dans le HTML ou le JavaScript du navigateur. Ajoutez l’authentification de votre application avant de reprendre ce modèle hors d’une démonstration locale.

**cURL**

```sh theme={null}
printf 'SORANK API key: '; read -rs SORANK_API_KEY; echo
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $SORANK_API_KEY" \
  'https://app.sorank.com/api/public/v1/profiles'

# Copy an authorized data[].id from the response above.
PROFILE_ID='<profile_id>'
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $SORANK_API_KEY" \
  "https://app.sorank.com/api/public/v1/profiles/$PROFILE_ID/quotas"
unset SORANK_API_KEY
```

**HTML**

```html index.html theme={null}
<button id="load">Load my SORANK websites</button>
<pre id="result"></pre>
<script>
  document.querySelector('#load').addEventListener('click', async () => {
    const output = document.querySelector('#result');
    try {
      // Your own backend authenticates to SORANK. Keep the key out of this page.
      const response = await fetch('/api/sorank/profiles');
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const profiles = await response.json();
      output.textContent = JSON.stringify(profiles, null, 2);
    } catch (error) {
      output.textContent = String(error);
    }
  });
</script>
```

```js server.mjs theme={null}
// Local demo backend. Run with SORANK_API_KEY in the environment.
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';

const key = process.env.SORANK_API_KEY;
if (!key) throw new Error('Set SORANK_API_KEY before running this file');
const html = await readFile(new URL('./index.html', import.meta.url));

createServer(async (request, response) => {
  if (request.method === 'GET' && request.url === '/') {
    response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    response.end(html);
    return;
  }
  if (request.method === 'GET' && request.url === '/api/sorank/profiles') {
    try {
      const upstream = await fetch('https://app.sorank.com/api/public/v1/profiles', {
        headers: { Authorization: `Bearer ${key}` },
      });
      response.writeHead(upstream.status, {
        'Content-Type': 'application/json; charset=utf-8',
        'Cache-Control': 'no-store',
      });
      response.end(await upstream.text());
    } catch {
      response.writeHead(502).end('Upstream unavailable');
    }
    return;
  }
  response.writeHead(404).end('Not found');
}).listen(3100, '127.0.0.1');
// Open http://127.0.0.1:3100 after starting this server.
```

**JavaScript**

```js profiles.mjs theme={null}
// Run on Node.js 20+ with SORANK_API_KEY set in the process environment.
const token = process.env.SORANK_API_KEY;
if (!token) throw new Error('Set SORANK_API_KEY before running this file');
const base = 'https://app.sorank.com/api/public/v1';

async function get(path) {
  const response = await fetch(`${base}${path}`, {
    headers: { Authorization: `Bearer ${token}` },
  });
  const body = await response.json();
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${JSON.stringify(body)}`);
  return body;
}

const profiles = await get('/profiles');
console.log(profiles);
const profileId = profiles.data[0]?.id;
if (profileId) console.log(await get(`/profiles/${encodeURIComponent(profileId)}/quotas`));
```

**Go**

```go main.go theme={null}
package main

import (
  "encoding/json"
  "fmt"
  "io"
  "net/http"
  "os"
  "time"
)

func main() {
  key := os.Getenv("SORANK_API_KEY")
  if key == "" { panic("set SORANK_API_KEY before running this file") }
  client := &http.Client{Timeout: 15 * time.Second}
  get := func(path string) []byte {
    req, err := http.NewRequest(http.MethodGet, "https://app.sorank.com/api/public/v1"+path, nil)
    if err != nil { panic(err) }
    req.Header.Set("Authorization", "Bearer "+key)
    resp, err := client.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()
    body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
    if err != nil { panic(err) }
    if resp.StatusCode != http.StatusOK { panic(fmt.Sprintf("HTTP %d: %s", resp.StatusCode, body)) }
    return body
  }

  body := get("/profiles")
  fmt.Println(string(body))
  var profiles struct { Data []struct { ID string `json:"id"` } `json:"data"` }
  if err := json.Unmarshal(body, &profiles); err != nil { panic(err) }
  if len(profiles.Data) > 0 {
    fmt.Println(string(get("/profiles/"+profiles.Data[0].ID+"/quotas")))
  }
}
```

La réponse contient une liste `data` des sites autorisés. Copiez la valeur `data[].id` de l’un d’eux pour les appels demandant un `profile_id`. Si la liste est vide, vérifiez les sites sélectionnés pour la clé et vos droits sur ces sites. Les exemples cURL, JavaScript et Go lisent aussi le quota de ce profil si vous avez accordé `quotas:read`.

La **Référence de l’API publique** dans la navigation développeur détaille les chemins, droits requis, paramètres et réponses. Ce premier appel est en lecture seule et ne consomme pas le quota du compte.

## S’authentifier

Créez une clé personnelle dans **Paramètres → Accès API** pour vos scripts, ou autorisez un client OAuth avec PKCE S256. Limitez l’accès aux profils et aux scopes nécessaires. Envoyez le jeton avec Bearer. Les cookies du navigateur et les jetons Supabase ne donnent pas accès à cette API.

Pour une application agissant au nom d’un utilisateur, commencez par `https://app.sorank.com/.well-known/oauth-protected-resource/api/public/v1` et `https://app.sorank.com/.well-known/oauth-authorization-server`. Découvrez les points d’autorisation, de jeton et d’enregistrement annoncés. Enregistrez un client public si nécessaire, puis utilisez authorization code avec PKCE S256, les droits nécessaires et la valeur exacte `resource=https://app.sorank.com/api/public/v1`. Envoyez le jeton obtenu en Bearer. Un jeton OAuth destiné à MCP ne fonctionne pas pour REST.

## Autorisations et quota

Consultez `getQuotas` avant une génération. L’exemple exige `profiles:read`, `quotas:read`, `calendar:read`, `calendar:write`, `articles:read`, `articles:generate` et `articles:publish`. La création du calendrier exige aussi les droits de génération et de publication. Le quota est partagé entre l’application, REST et MCP ; plusieurs clés ne l’augmentent pas.

Les droits de lecture **Voir les produits** (`products:read`), **Voir les statistiques** (`analytics:read`), **Voir la visibilité IA** (`geo:read`) et **Voir les paramètres** (`settings:read`) ne consomment jamais de quota. **Modifier les paramètres** (`settings:write`) modifie les mêmes réglages que l’application ; ne l’accordez que si une intégration doit les changer.

## Vérifier l’éligibilité

L’API est disponible une fois l’onboarding Sorank terminé et si le site dispose d’un abonnement valide. Un collaborateur d’agence utilise l’abonnement du propriétaire du site. Sorank le vérifie à la création d’une clé ou à l’approbation d’une connexion OAuth, puis à chaque requête.

* `listProfiles` ne renvoie que les sites éligibles.
* Un onboarding inachevé renvoie `403 onboarding_required`.
* Un site sans abonnement valide renvoie `402 subscription_required`, avec `details.reason` (`paused`, `past_due`, `incomplete` ou `inactive`) lorsqu’il est connu.

Terminez l’onboarding ou mettez à jour l’abonnement dans **Paramètres → Facturation**. Votre clé existante fonctionne de nouveau dès la requête suivante.

## Lire les produits, statistiques, visibilité IA et paramètres

Ces lectures renvoient ce que l’application Sorank vous affiche. Elles ne lancent jamais d’analyse, de synchronisation ni de génération.

* **Produits** (`products:read`) : `listProducts` parcourt le catalogue produits avec un curseur. Après `409 product_catalog_cursor_stale`, reprenez depuis la première page.
* **Statistiques** (`analytics:read`) : performances, requêtes, pages, pays et appareils Search Console, et trafic Google Analytics issu des assistants IA. `start` et `end` (`YYYY-MM-DD`) sont obligatoires. Chaque appel interroge Google en direct et a une limite de débit plus basse. Connectez d’abord Google dans Sorank ; sinon vous recevez `409 gsc_not_connected` ou `409 ga_not_connected`, et `403 google_access_lost` si l’accès a été perdu.
* **Visibilité IA** (`geo:read`) : statistiques GEO, évolution des mentions, chronologie des citations, sources citantes et concurrents cités dans une analyse.
* **Paramètres** (`settings:read`) : `getSettings` renvoie le site, le brief métier avec sa `revision`, les concurrents, les réglages des articles, les visuels et la chaîne YouTube.

## Modifier les paramètres

Avec `settings:write`, vous pouvez modifier le nom de l’entreprise, la couleur de marque et le marché, remplacer le brief métier, remplacer la liste des concurrents, changer les réglages des articles du calendrier, le mode d’image et les couvertures, et connecter, suspendre ou déconnecter la chaîne YouTube. L’URL du site ne peut pas être modifiée par l’API (`400 field_not_writable`).

Chaque modification exige un `Idempotency-Key`. La réponse contient les paramètres obtenus, un reçu et une liste `effects` qui nomme les traitements lancés en arrière-plan, comme dans l’application : `geo_questions_regeneration_queued`, `backlink_refresh_queued`, `calendar_slots_shifted`, `image_gallery_analysis_queued`, `youtube_sync_queued` ou `youtube_purge_queued`.

Pour remplacer le brief métier, envoyez l’`expected_revision` lue dans `getSettings`. Si le brief a changé entre-temps, vous recevez `409 business_brief_revision_conflict` : relisez les paramètres avant de réessayer.

## Essayer un parcours REST complet

Cet exemple avancé crée du contenu et peut consommer du quota. Remplacez les identifiants, dates futures, clés d’idempotence et clé API d’exemple avant de l’exécuter. Utilisez une clé dotée des droits ci-dessus. Les dates du calendrier sont locales au fuseau IANA indiqué. Consultez `getCapabilities` avant de choisir une destination ou catégorie CMS.

```sh theme={null}
# L’API de production est hébergée par l’application SORANK.
API_ORIGIN="https://app.sorank.com"
PROFILE_ID="00000000-0000-4000-8000-000000000000"
ARTICLE_ID="11111111-1111-4111-8111-111111111111"
TOKEN="<PERSONAL_API_KEY>"

curl -H "Authorization: Bearer $TOKEN" "$API_ORIGIN/api/public/v1/profiles/$PROFILE_ID/quotas"
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: 22222222-2222-4222-8222-222222222222" \
  -d '{"settings":{"timezone":"Europe/Paris","local_time":"09:30"},"start_date":"2026-11-01","end_date":"2026-12-31","subjects":[{"subject":"Sustainable travel checklist"}]}' \
  "$API_ORIGIN/api/public/v1/profiles/$PROFILE_ID/calendar"
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: 33333333-3333-4333-8333-333333333333" \
  -d '{"seeds":[{"keyword":"sustainable travel tips","title":"Practical sustainable travel"}]}' \
  "$API_ORIGIN/api/public/v1/profiles/$PROFILE_ID/article-generations"
curl -H "Authorization: Bearer $TOKEN" "$API_ORIGIN/api/public/v1/operations/<OPERATION_UUID>"
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: 44444444-4444-4444-8444-444444444444" \
  -d '{"target":"draft"}' "$API_ORIGIN/api/public/v1/profiles/$PROFILE_ID/articles/$ARTICLE_ID/publications"
```

La référence de l’API publique décrit chaque opération, paramètre, réponse et erreur.

## Suivre et reprendre

Chaque modification exige un `Idempotency-Key` unique. Réutilisez la même clé pour répéter la même commande. Lisez `data.receipt.operation_id`, puis interrogez `getOperation` jusqu’à `succeeded`, `failed` ou `verification_pending`. Un reçu accepté ne signifie pas que la publication est terminée. Un lot de génération d’articles renvoie `202` avec un reçu déjà `succeeded` ; suivez chaque article listé dans `selection.accepted_ids` avec `getArticle`. Les droits sont revérifiés à chaque consultation : une révocation s’applique immédiatement.

## Comprendre les erreurs

Chaque erreur a la même forme en REST et en MCP : `{"error": {"code", "message", "request_id", "retryable", "details"}}`. Fondez votre logique sur `code`, pas sur le message. Ne réessayez que si `retryable` vaut `true`, après `Retry-After` lorsqu’il est présent.

* `401 invalid_credential` : identifiant invalide, expiré ou révoqué.
* `403 missing_scope` ou `403 onboarding_required` ; `402 subscription_required` ou `402 insufficient_quota`.
* `404 not_found` : ressource absente ou inaccessible avec cet identifiant.
* `409` : conflit, par exemple `idempotency_conflict` ; `429 rate_limited` : attendez `Retry-After`.
* `503 auth_state_unavailable` : l’accès est refusé jusqu’au rétablissement de l’état d’autorisation ; réessayez plus tard.

La référence de l’API publique liste les codes de chaque opération. Transmettez `code` et `request_id` au support, jamais votre jeton.

Pour connecter un assistant, consultez le guide MCP.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.