Найти в Дзене
Vibecode Wiki

Rate limit и retry: базовая схема для надёжных интеграций

В любой интеграции рано или поздно появляется одна и та же проблема. Вы отправляете запрос к API — и он не проходит. Иногда сервер отвечает ошибкой, иногда сеть даёт сбой, иногда API просто говорит: «слишком много запросов». В логах это выглядит примерно так: 429 Too Many Requests или 500 Internal Server Error Если система не умеет правильно реагировать на такие ситуации, интеграция становится нестабильной: Чтобы этого не происходило, в любой серьёзной интеграции используют два базовых механизма: rate limit и retry. Первый отвечает за контроль скорости запросов, второй — за повторные попытки при ошибках. Если их правильно реализовать, интеграция становится устойчивой даже при сетевых сбоях и высокой нагрузке. Rate limit — это ограничение на количество запросов, которые можно отправить к API за определённый промежуток времени. Почти все публичные API используют такие ограничения. Примеры: Когда лимит превышен, сервер возвращает ответ: 429 Too Many Requests Иногда вместе с дополнительным
Оглавление

В любой интеграции рано или поздно появляется одна и та же проблема. Вы отправляете запрос к API — и он не проходит. Иногда сервер отвечает ошибкой, иногда сеть даёт сбой, иногда API просто говорит: «слишком много запросов».

В логах это выглядит примерно так:

429 Too Many Requests

или

500 Internal Server Error

Если система не умеет правильно реагировать на такие ситуации, интеграция становится нестабильной:

  • часть запросов падает
  • данные не синхронизируются
  • события теряются
  • система начинает бесконечно повторять запросы

Чтобы этого не происходило, в любой серьёзной интеграции используют два базовых механизма:

rate limit и retry.

Первый отвечает за контроль скорости запросов, второй — за повторные попытки при ошибках.

Если их правильно реализовать, интеграция становится устойчивой даже при сетевых сбоях и высокой нагрузке.

Что такое rate limit

Rate limit — это ограничение на количество запросов, которые можно отправить к API за определённый промежуток времени.

Почти все публичные API используют такие ограничения.

Примеры:

  • GitHub API — 5000 запросов в час
  • Stripe — примерно 100 запросов в секунду
  • многие SaaS — 60 запросов в минуту

Когда лимит превышен, сервер возвращает ответ:

429 Too Many Requests

Иногда вместе с дополнительным заголовком:

Retry-After: 10

Это означает, что новый запрос можно отправить только через 10 секунд.

Такие ограничения нужны для защиты инфраструктуры и справедливого распределения ресурсов между клиентами.

Почему API ограничивают запросы

На первый взгляд может показаться, что rate limit — это просто неудобство для разработчиков. Но у этого механизма есть важные причины.

Первая причина — защита сервера. Если один клиент отправит тысячи запросов в секунду, это может перегрузить систему.

Вторая причина — равномерное использование ресурсов. Ограничения гарантируют, что один пользователь не займёт всю мощность сервиса.

Третья причина — безопасность. Rate limit помогает защищаться от brute force и других атак.

Что происходит без контроля rate limit

Представим простую интеграцию, которая отправляет данные в API.

Код может выглядеть так:

for (const event of events) {

await api.send(event)

}

Если событий немного — всё работает нормально. Но если их становится тысячи, система начинает отправлять огромное количество запросов.

В какой-то момент API начинает отвечать ошибкой:

429 Too Many Requests

Если код не умеет корректно обрабатывать такие ответы, интеграция начинает работать всё хуже:

  • запросы падают
  • система перегружает API
  • часть данных теряется

Поэтому важно контролировать скорость отправки запросов.

Что такое retry

Retry — это повторная попытка выполнить запрос после ошибки.

Смысл retry в том, что многие ошибки являются временными.

Например:

  • сервер был перегружен
  • балансировщик вернул ошибку
  • сеть на секунду оборвалась

В таких случаях повторный запрос часто проходит успешно.

Типичные ошибки, при которых используется retry:

  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • timeout

Во всех этих ситуациях повторная попытка имеет смысл.

Когда retry делать не нужно

Некоторые ошибки означают, что запрос никогда не станет успешным, пока не изменятся данные.

Например:

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found

Если повторять такие запросы, система будет просто создавать лишнюю нагрузку.

Поэтому retry должен применяться только к временным ошибкам.

Проблема наивного retry

Самая простая реализация retry выглядит так:

try {

await api.request()

} catch (e) {

await api.request()

}

Но такой подход может привести к серьёзной проблеме.

Если сервер уже перегружен, мгновенные повторные запросы только увеличат нагрузку. В результате система может попасть в состояние, когда тысячи клиентов одновременно начинают повторять запросы.

Это называют retry storm — шторм повторных запросов.

Чтобы этого избежать, используют более аккуратный алгоритм.

Exponential backoff

Один из самых популярных алгоритмов retry — exponential backoff.

Его идея очень проста: каждая следующая попытка выполняется с увеличивающейся задержкой.

Например:

1 попытка — сразу 2 попытка — через 1 секунду 3 попытка — через 2 секунды 4 попытка — через 4 секунды 5 попытка — через 8 секунд

Это даёт серверу время восстановиться и резко снижает нагрузку.

Пример retry на JavaScript

Простейшая реализация retry с exponential backoff:

async function requestWithRetry(fn, retries = 5) {

for (let attempt = 0; attempt < retries; attempt++) {

try {

return await fn()

} catch (error) {

if (attempt === retries - 1) {

throw error

}

const delay = 2 ** attempt * 1000

await new Promise(resolve => setTimeout(resolve, delay))

}

}

}

Использование:

await requestWithRetry(() => fetch("https://api.example.com"))

Если запрос завершится ошибкой, функция автоматически попробует снова.

Ограничение скорости запросов

Помимо retry, часто нужно контролировать скорость отправки запросов.

Самая простая схема — использовать очередь.

Сначала задачи попадают в очередь, затем worker отправляет их в API с контролируемой скоростью.

Простейший limiter может выглядеть так:

class RateLimiter {

constructor(limit, interval) {

this.limit = limit

this.interval = interval

this.queue = []

this.active = 0

}

async schedule(task) {

return new Promise(resolve => {

this.queue.push({ task, resolve })

this.run()

})

}

run() {

if (this.active >= this.limit || this.queue.length === 0) return

const { task, resolve } = this.queue.shift()

this.active++

task().then(result => {

resolve(result)

setTimeout(() => {

this.active--

this.run()

}, this.interval)

})

}

}

Такой limiter позволяет отправлять, например, не больше 5 запросов в секунду.

Как это выглядит в реальной архитектуре

В production системах схема обычно выглядит так:

  1. события попадают в очередь
  2. worker берёт задачу
  3. rate limiter контролирует скорость
  4. отправляется API-запрос
  5. при ошибке включается retry

Такая архитектура позволяет системе:

  • не превышать лимиты API
  • корректно обрабатывать временные ошибки
  • не терять данные при сетевых сбоях.

Итог

Rate limit и retry — это фундамент любой надёжной интеграции.

Rate limit контролирует скорость запросов и защищает API от перегрузки. Retry помогает системе автоматически восстанавливаться после временных ошибок.

Даже простая реализация этих механизмов значительно повышает устойчивость интеграций и предотвращает потерю данных при работе с внешними сервисами.