Skip to content

Rate limiting — Public API

Limites par défaut

Chaque clé API a deux compteurs indépendants :

FenêtreDéfautSurchargeable par clé
Par minute60 requêtesOui (rateLimitPerMinute)
Par jour10 000 requêtesOui (rateLimitPerDay)

Les compteurs sont per-clé (pas per-agence ni per-IP). Reset rolling : la fenêtre minute se réinitialise 60s après le 1er hit, idem pour la fenêtre jour à 24h.

Source : server/publicApi/rateLimit.ts

Filet de sécurité global

En complément, un rate limit global Express (apiLimiter) protège tout /api/* à 200 req/min par IP (filtré par Traefik en prod via trust proxy). En pratique, vous toucherez votre limite per-clé bien avant.

Headers de réponse

Toutes les réponses (succès ou erreur) incluent :

HeaderDescription
X-RateLimit-LimitLimite max par minute pour cette clé
X-RateLimit-RemainingRequêtes restantes dans la fenêtre minute courante
X-RateLimit-ResetTimestamp Unix (secondes) du prochain reset minute

En cas de dépassement quotidien :

HeaderDescription
X-RateLimit-Limit-DayLimite max par jour
X-RateLimit-Remaining-Day0 (vous êtes à la limite)
Retry-AfterSecondes avant le prochain reset

Réponse 429

http
HTTP/1.1 429 Too Many Requests
Retry-After: 47
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1730019660
Content-Type: application/json

{
  "error": "Rate limit exceeded: 60 requests per minute",
  "code": "RATE_LIMIT_EXCEEDED",
  "retryAfter": 47
}

Stratégie de retry recommandée

  1. Respecter Retry-After : ne pas retenter avant ce délai
  2. Backoff exponentiel sur les erreurs 5xx (pas sur 429 — le serveur dit déjà combien attendre)
  3. Jitter (±20%) pour éviter le thundering herd si plusieurs clients retentent en même temps
  4. Limite max de retries : 3-5 selon criticité

Pseudo-code :

javascript
async function callApi(url, options, attempt = 0) {
  const res = await fetch(url, options);
  if (res.status === 429) {
    const retryAfter = parseInt(res.headers.get('Retry-After') || '60', 10);
    await sleep(retryAfter * 1000 + Math.random() * 200);
    return callApi(url, options, attempt + 1);
  }
  if (res.status >= 500 && attempt < 3) {
    await sleep(Math.pow(2, attempt) * 1000);
    return callApi(url, options, attempt + 1);
  }
  return res;
}

Surveiller votre consommation

Endpoint /me (voir authentication.md) renvoie les compteurs en temps réel — utile en pré-vol avant un batch lourd :

json
{
  "usage": {
    "minuteCount": 58,        // ← attention, presque à la limite
    "minuteResetAt": "2026-04-27T10:01:00.000Z",
    "dayCount": 9847,
    "dayResetAt": "2026-04-28T00:00:00.000Z"
  }
}

Augmenter les limites

Les limites par-clé sont éditables côté admin (PATCH /api/public-api/admin/keys/:id). Pour des cas d'usage à fort volume, contactez support@stormeo.io avec :

  • Volume estimé (req/min, req/jour)
  • Cas d'usage (Zapier interne ? sync inverse ? script de migration ?)
  • Pic de charge attendu

Note technique

  • Le bucket est in-memory (Map) — reset au redémarrage du serveur. Acceptable car le filet apiLimiter global protège quand même.
  • Les buckets abandonnés (>25h sans hit) sont nettoyés toutes les 10 min pour éviter les fuites mémoire.
  • Pas de file d'attente côté serveur : si vous saturez, vous recevez 429 immédiatement (fail fast).

StormeoOS API