Aller au contenu principal

Erreurs HTTP et securite de la cle API

Gerer les erreurs et proteger vos credentials

En production, votre application doit gerer elegamment les erreurs de l’API TTS et surtout ne jamais exposer votre cle API cote client. Cette lecon couvre les codes d’erreur, les strategies de retry, et l’architecture de proxy backend.

Codes d’erreur HTTP

L’API TTS retourne les codes d’erreur standard HTTP. Voici comment reagir a chacun.

400 - Bad Request

Le serveur n’a pas pu traiter votre requete. Causes frequentes :

  • Texte vide ou depassant 15 000 caracteres
  • Codec invalide (faute de frappe dans le nom)
  • Sample rate non supporte
  • Tags expressifs mal formes (balise non fermee)
if response.status_code == 400:
    error = response.json()
    print(f"Requete invalide: {error}")
    # Corriger les parametres et reessayer

Ne reessayez pas automatiquement une erreur 400 : le probleme vient de votre requete, pas du serveur. Corrigez d’abord les parametres.

401 - Unauthorized

La cle API est invalide, expiree ou absente :

if response.status_code == 401:
    raise AuthenticationError("Cle API invalide ou expiree")

Verifiez que :

  • L’en-tete Authorization est present
  • Le format est Bearer xai-... (avec l’espace apres Bearer)
  • La cle n’a pas ete revoquee dans la console xAI

429 - Too Many Requests

Vous avez depasse la limite de requetes concurrentes (100 pour HTTP, 50 sessions pour WebSocket) :

if response.status_code == 429:
    retry_after = int(response.headers.get("Retry-After", 5))
    await asyncio.sleep(retry_after)
    # Reessayer

Implementez un backoff exponentiel pour les erreurs 429 :

async def request_with_backoff(url, payload, headers, max_retries=5):
    for attempt in range(max_retries):
        response = requests.post(url, json=payload, headers=headers)

        if response.status_code == 200:
            return response

        if response.status_code == 429:
            wait = min(2 ** attempt + random.random(), 60)
            print(f"Rate limited. Attente de {wait:.1f}s")
            await asyncio.sleep(wait)
            continue

        if response.status_code >= 500:
            wait = min(2 ** attempt + random.random(), 30)
            await asyncio.sleep(wait)
            continue

        # Erreur client (400, 401) : ne pas reessayer
        response.raise_for_status()

    raise Exception("Echec apres 5 tentatives")

500 / 503 - Server Error

Le serveur rencontre un probleme temporaire. Reessayez avec backoff :

if response.status_code in (500, 503):
    # Reessayer avec backoff exponentiel
    pass

Les erreurs 503 sont souvent liees a une surcharge temporaire. Elles se resolvent generalement en quelques secondes.

Ne jamais exposer la cle API cote client

C’est la regle de securite la plus importante. Si votre cle API est visible dans le code JavaScript du navigateur, n’importe qui peut :

  • Utiliser votre quota gratuitement
  • Accumuler des frais sur votre compte
  • Acceder a tous les endpoints de l’API xAI avec votre cle

Architecture de proxy backend

La solution est de proxier les appels TTS via votre propre backend :

Navigateur -> Votre backend -> API xAI
  (pas de cle)   (cle API)     (TTS)

Exemple Express.js

const express = require("express");
const app = express();

app.post("/api/tts", async (req, res) => {
  const { text, voice_id, language } = req.body;

  // Validation cote serveur
  if (!text || text.length > 15000) {
    return res.status(400).json({ error: "Texte invalide" });
  }

  const response = await fetch("https://api.x.ai/v1/tts", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.XAI_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ text, voice_id, language })
  });

  if (!response.ok) {
    return res.status(response.status).json({
      error: "Erreur TTS"
    });
  }

  // Transmettre l'audio au client
  res.set("Content-Type", response.headers.get("Content-Type"));
  const buffer = await response.arrayBuffer();
  res.send(Buffer.from(buffer));
});

Exemple FastAPI (Python)

from fastapi import FastAPI, HTTPException
from fastapi.responses import Response
import httpx

app = FastAPI()

@app.post("/api/tts")
async def proxy_tts(text: str, voice_id: str = "eve", language: str = "fr"):
    if len(text) > 15000:
        raise HTTPException(400, "Texte trop long")

    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://api.x.ai/v1/tts",
            headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
            json={"text": text, "voice_id": voice_id, "language": language}
        )

    if response.status_code != 200:
        raise HTTPException(response.status_code, "Erreur TTS")

    return Response(
        content=response.content,
        media_type=response.headers["content-type"]
    )

Proxy WebSocket

Pour le WebSocket TTS, votre backend doit relayer les messages entre le client et l’API xAI :

const WebSocket = require("ws");

wss.on("connection", (clientWs) => {
  const xaiWs = new WebSocket(
    "wss://api.x.ai/v1/tts?language=fr&voice=eve",
    { headers: { Authorization: `Bearer ${process.env.XAI_API_KEY}` } }
  );

  // Relayer client -> xAI
  clientWs.on("message", (msg) => xaiWs.send(msg));

  // Relayer xAI -> client
  xaiWs.on("message", (msg) => clientWs.send(msg));

  // Gerer les fermetures
  clientWs.on("close", () => xaiWs.close());
  xaiWs.on("close", () => clientWs.close());
});

Moderation fail-open

L’API TTS execute une moderation du contenu de maniere asynchrone. Cela signifie que la requete n’est pas bloquee par la moderation : l’audio est genere et retourne meme si la moderation n’a pas encore termine son analyse.

En cas de contenu detecte comme inapproprie, le signalement arrive apres coup. Votre application doit donc implementer sa propre moderation en amont si necessaire.

Points cles a retenir

  • 400 = erreur de parametres (ne pas reessayer, corriger la requete)
  • 401 = cle API invalide (verifier l’en-tete Authorization)
  • 429 = rate limited (backoff exponentiel)
  • 500/503 = erreur serveur temporaire (reessayer avec backoff)
  • Ne jamais exposer la cle API dans le code client
  • Proxier tous les appels TTS via votre backend
  • La moderation est fail-open : implementez votre propre filtrage si necessaire