Limites de débit et erreurs
Quotas de requêtes, en-têtes de limite de débit, formats d’erreur, codes de statut HTTP et façons de réessayer sans risque.
Limites de débit
Chaque application peut faire 800 requêtes REST par fenêtre glissante de 60 secondes pour chaque utilisateur au nom duquel elle agit. Le quota est calculé par paire (application, utilisateur) : les requêtes faites avec le jeton d’un utilisateur n’entament pas le quota d’un autre utilisateur, et les requêtes faites avec des jetons d’application partagent un compteur distinct qui leur est propre.
Chaque réponse REST comprend ces en-têtes :
| En-tête | Signification |
|---|---|
Ratelimit-Limit | Nombre de requêtes permises par fenêtre (800). |
Ratelimit-Remaining | Requêtes restantes dans la fenêtre courante. |
Ratelimit-Reset | Temps Unix (secondes depuis l’époque) auquel de la capacité se libère de nouveau. |
Lorsque le quota est épuisé, l’API répond 429 jusqu’à ce que la fenêtre se libère :
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Ratelimit-Limit: 800
Ratelimit-Remaining: 0
Ratelimit-Reset: 1790265642
{"error":"Too Many Requests","status":429,"message":"rate limit exceeded"}Remarque
Pour rester bien en deçà de la limite, mettez en cache les données publiques, utilisez la pagination avec first=100 plutôt que de nombreuses petites pages, et abonnez-vous aux événements EventSub plutôt que d’interroger l’API à répétition.
Format des erreurs REST
Les erreurs des points de terminaison https://api.woupi.buzz/v1 utilisent cette enveloppe : error est le libellé du statut HTTP, status le code numérique et message un détail lisible.
{
"error": "Forbidden",
"status": 403,
"message": "Missing scope: chat:write"
}Messages que vous voudrez peut-être traiter explicitement :
| Statut | message | Cause |
|---|---|---|
| 401 | missing user token | Le point de terminaison agit pour un utilisateur, mais vous avez envoyé un jeton d’application. |
| 401 | (erreur de jeton ou de Client-Id) | Jeton absent, invalide, expiré ou révoqué, ou en-tête Client-Id absent ou ne correspondant pas à l’application du jeton. |
| 403 | Missing scope: <scope> | Le jeton ne comprend pas la portée requise. |
| 403 | app_suspended | Le compte développeur propriétaire de l’application n’est pas approuvé (en attente, refusé ou suspendu). |
| 403 | subscription missing proper authorization | Création d’un abonnement aux événements dont la portée requise n’a pas été accordée par le bon utilisateur. |
| 404 | unknown broadcaster | Un broadcaster_id ou un user_id qui n’existe pas. |
| 404 | Unknown endpoint | Le chemin n’existe pas sur api.woupi.buzz. |
| 429 | too many websocket sessions | Plus de 3 sessions WebSocket pour la même application et le même utilisateur. |
| 503 | chat unavailable | Le clavardage est temporairement indisponible; réessayez sous peu. |
Considérez message comme une information destinée aux développeurs : affichez votre propre texte à vos utilisateurs et décidez d’abord en fonction du code de statut.
Format des erreurs OAuth
Les points de terminaison OAuth (/oauth2/token, /oauth2/device, /oauth2/revoke) suivent plutôt la norme OAuth 2.0, avec un code error et une error_description :
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}/oauth2/validate répond 401 avec {"status":401,"message":"invalid access token"} pour un jeton invalide. La liste complète des codes d’erreur OAuth se trouve dans Authentification.
Codes de statut HTTP
| Code | Signification |
|---|---|
| 200 OK | Succès; le corps contient le résultat, habituellement { "data": [...] }. |
| 202 Accepted | La requête est acceptée et se terminera de façon asynchrone, par exemple un abonnement aux événements créé avec POST /v1/eventsub/subscriptions (les webhooks sont vérifiés ensuite). |
| 204 No Content | Succès sans corps, par exemple PATCH /v1/channels ou DELETE /v1/eventsub/subscriptions. |
| 400 Bad Request | Paramètre ou corps absent ou invalide. Corrigez la requête avant de réessayer. |
| 401 Unauthorized | Problème d’authentification : jeton absent, invalide, expiré ou révoqué; Client-Id absent ou erroné; ou jeton d’application utilisé là où un jeton d’utilisateur est requis. |
| 403 Forbidden | Authentifié, mais non autorisé : portée manquante, application suspendue, ou l’utilisateur du jeton n’est pas le diffuseur visé par l’action. |
| 404 Not Found | Point de terminaison ou ressource inconnus. |
| 409 Conflict | La ressource existe déjà, par exemple un abonnement aux événements en double. |
| 429 Too Many Requests | Limite de débit dépassée (ou trop de sessions WebSocket). Patientez avant de réessayer. |
| 500 / 503 | Erreur du serveur ou indisponibilité temporaire. Réessayez avec un délai croissant. |
Attention
Pendant une panne, les requêtes peuvent échouer avant d’atteindre l’API : une réponse 5xx peut alors contenir le corps {"error":"origin_unavailable"} au lieu de l’enveloppe habituelle. Traitez-la comme n’importe quelle autre réponse 5xx.
Réessayer sans risque
- 429 : attendez jusqu’à
Ratelimit-Reset(au moins une seconde), puis réessayez. SurveillezRatelimit-Remainingpour ralentir avant d’atteindre la limite. - 500 et 503 : réessayez avec un délai exponentiel et une petite variation aléatoire (par exemple 1 s, 2 s, 4 s... plafonné à 30 s), et abandonnez après quelques tentatives.
- 401 avec un jeton d’utilisateur : actualisez le jeton une fois (voir Authentification), puis réessayez. Si l’actualisation échoue, demandez à l’utilisateur d’autoriser de nouveau votre application.
- 400, 403, 404 et 409 : ne réessayez pas la même requête; corrigez-la d’abord.
- Réessayer un
PATCHou unDELETEne pose pas de problème. Avant de réessayer unPOSTqui crée quelque chose (un message de clavardage, un extrait, un abonnement), vérifiez si la première tentative a réussi.
async function woupiFetch(url, options = {}, attempt = 0) {
const res = await fetch(url, options);
if (attempt >= 5) return res;
if (res.status === 429) {
const resetMs = Number(res.headers.get('Ratelimit-Reset')) * 1000;
const waitMs = Math.max(resetMs - Date.now(), 1000);
await new Promise((resolve) => setTimeout(resolve, waitMs));
return woupiFetch(url, options, attempt + 1);
}
if (res.status >= 500) {
const waitMs = Math.min(30000, 1000 * 2 ** attempt) + Math.random() * 250;
await new Promise((resolve) => setTimeout(resolve, waitMs));
return woupiFetch(url, options, attempt + 1);
}
return res;
}