Почему API отвечает 429?
Почти любой лимит запросов внутри устроен как ведро с жетонами — token bucket. Мы собрали его в железе: магазин монет, часы пополнения и лента запросов. Трогайте машину, смотрите ролик — и через пять минут правило b + r·T будет очевидным.
- Что значит 429 Too Many Requests?
- Сколько запросов можно отправить разом?
- Почему в среднем пропускают ровно r в секунду?
- Что делать, получив 429?
Token bucket в железе
Магазин на 5 монет — ведро (ёмкость b). Часы бункера роняют монету раз в 2 секунды — пополнение (скорость r). Каждый запрос на ленте забирает одну монету; нет монеты — толкатель сбрасывает его в лоток 429.
Вера объясняет, Макс спрашивает
Диалог над той же машиной: ведро, пополнение, запрос, всплеск, среднее и две ручки лимита. Есть вертикальная версия для Reels/Shorts.
Четыре факта о token bucket
Всплеск — не больше b
Полное ведро пропускает b запросов подряд, сколько бы их ни пришло разом.
В среднем — не больше r
Когда запас кончился, проходит ровно столько, сколько капает: r запросов в секунду.
Копить нельзя
Ведро не растёт: лишние жетоны сгорают. Долгое молчание не даёт права на огромный всплеск.
Отказ — это подсказка
Получили 429 — подождите Retry-After секунд: к этому времени капнет новый жетон.
Тот же алгоритм — двенадцать строк
В настоящих серверах жетоны не падают по одному: их число пересчитывают при каждом запросе по прошедшему времени. Результат тот же, что у машины.
class TokenBucket { constructor(b, r) { this.b = b; this.r = r; this.tokens = b; this.last = Date.now() / 1000; } take() { const now = Date.now() / 1000; // пополнение: r жетонов в секунду, но не больше ёмкости b this.tokens = Math.min(this.b, this.tokens + (now - this.last) * this.r); this.last = now; if (this.tokens >= 1) { this.tokens -= 1; return { ok: true }; } // 200 return { ok: false, retryAfter: Math.ceil((1 - this.tokens) / this.r) }; // 429 } }
Сделано на anatomy (изометрическая фигура) и video-kit (ролик с озвучкой). Машина — figure.js, модель — model.js, сценарий — video/SCENARIO.md.
Explainer · github.com/nemilya/explainer