Error Handling

HTTP Status Codes

CodeStatusDescription
200OKRequest successful
400Bad RequestMissing required fields or invalid parameters
401UnauthorizedInvalid or missing API key
402Payment RequiredInsufficient credits
404Not FoundResource not found (ex. no data for the requested date, or unknown sign/id)
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side failure

Error Response Format

Les réponses d'erreur suivent cette structure de base, enrichie du contexte selon le cas :

{
  "error": "Courte description de l'erreur",
  "message": "Message détaillé (présent sur les erreurs métier les plus expliquées)",
  "code": "CODE_NORMALISE (parfois présent, ex. MONTHLY_QUOTA_EXCEEDED)",
  "required": 1,
  "available": 0
}
  • Toutes les erreurs contiennent au minimum le champ error.
  • Les champs required/available (402) indiquent le coût attendu et le solde disponible.
  • Le champ code est optionnel et sert à distinguer programmatiquement certains cas (ex. quota dépassé).

Common Errors

400 - Bad Request

{
  "error": "Missing required fields: birthDate, birthTime"
}

Location invalide : le code renvoie Missing location information (un identifiant de lieu n'a pas pu être résolu).

Dates/heures : les messages de lib/validation/birthInputs.ts utilisent "Use YYYY-MM-DD format (e.g., 1990-12-25)" et "Use HH:MM 24-hour format".

401 - Unauthorized

Le middleware (lib/middleware/apiAuth.ts) émet deux cas :

  • Clé absente : API key required
  • Clé invalide ou inactive : Invalid or inactive API key

Il n'existe pas de message distinct « revoked » — une clé révoquée est traitée comme Invalid or inactive API key.

402 - Payment Required

{
  "error": "Insufficient credits",
  "message": "Transit analysis costs 3 credits. Please purchase more credits.",
  "required": 3,
  "available": 1
}

Quota Cosmo dépassé (cosmo/chat) :

{
  "error": "Monthly quota exceeded",
  "code": "MONTHLY_QUOTA_EXCEEDED",
  "message": "Monthly quota exceeded. Upgrade your plan or purchase credits to continue."
}

404 - Not Found

Ex. aucune donnée pour la date demandée, signe ou identifiant inconnu :

{
  "error": "No transit data available",
  "message": "Cosmic data for today is being prepared. Please try again later."
}

429 - Too Many Requests

{
  "success": false,
  "error": "Rate limit exceeded",
  "message": "Too many requests. Please try again in 30 seconds.",
  "retryAfter": 30,
  "limit": 60,
  "reset": 1640995200
}

500 - Internal Server Error

{
  "error": "Blueprint generation failed"
}

Rate Limit Headers

Chaque réponse (hors streaming Cosmo, voir rate-limits.md) inclut X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.

Error Handling Best Practices

JavaScript Example

try {
  const response = await fetch('https://mycosmicadvisor.com/api/v1/generate-blueprint', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer bgapi_your_key_here'
    },
    body: JSON.stringify({
      birthDate: '1990-12-25',
      birthTime: '14:30',
      city: 'New York',
      country: 'USA',
      fullName: 'John Doe'
    })
  });

  const data = await response.json();

  if (!response.ok) {
    // Handle HTTP errors
    switch (response.status) {
      case 400:
        console.error('Invalid request:', data.error);
        break;
      case 401:
        console.error('Authentication failed:', data.error);
        break;
      case 402:
        console.error('Insufficient credits:', data.error);
        break;
      case 500:
        console.error('Server error:', data.error);
        break;
      default:
        console.error('Unexpected error:', data.error);
    }
    return;
  }

  // Success
  console.log('Blueprint generated:', data);
} catch (error) {
  console.error('Network error:', error);
}

Python Example

import requests

try:
    response = requests.post(
        'https://mycosmicadvisor.com/api/v1/generate-blueprint',
        headers={
            'Content-Type': 'application/json',
            'Authorization': 'Bearer bgapi_your_key_here'
        },
        json={
            'birthDate': '1990-12-25',
            'birthTime': '14:30',
            'city': 'New York',
            'country': 'USA',
            'fullName': 'John Doe'
        }
    )

    data = response.json()

    if response.status_code == 200:
        print('Blueprint generated:', data)
    elif response.status_code == 400:
        print('Invalid request:', data['error'])
    elif response.status_code == 401:
        print('Authentication failed:', data['error'])
    elif response.status_code == 402:
        print('Insufficient credits:', data['error'])
    elif response.status_code == 500:
        print('Server error:', data['error'])
    else:
        print('Unexpected error:', data['error'])

except requests.exceptions.RequestException as e:
    print('Network error:', e)

Retry Logic

For transient errors (500), implement exponential backoff:

async function generateWithRetry(requestData, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('https://mycosmicadvisor.com/api/v1/generate-blueprint', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer bgapi_your_key_here'
        },
        body: JSON.stringify(requestData)
      });

      const data = await response.json();

      if (response.status === 500 && attempt < maxRetries) {
        // Wait before retrying (exponential backoff)
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      return data;
    } catch (error) {
      if (attempt === maxRetries) throw error;

      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}