API: лимиты и идемпотентность
Rate limiting и идемпотентность API MeerPartners: лимиты на постбэк и вход, заголовок Retry-After, Idempotency-Key и дедупликация постбэков по external_order_id.
Чтобы интеграция была устойчивой, API ограничивает частоту запросов и гарантирует безопасные повторы. Эта страница — про оба механизма: что произойдёт при превышении лимита и как повторять запросы, не создавая дубликатов.
Rate limiting
Лимиты считаются в фиксированном окне (счётчик в Redis). Ключ лимита зависит от эндпоинта:
| Эндпоинт | Лимит | Ключ | Код при превышении |
|---|---|---|---|
POST /api/v1/postback | ~600 / мин | на tenant_api_key (key_id) | POSTBACK_RATE_LIMITED (429) |
POST /api/v1/auth/meerid/exchange | 20 / мин | на IP | RATE_LIMIT_EXCEEDED (429) |
GET /r/{code}, GET /p/{code}.gif | анти-флуд | на хэш IP | TRACKING_RATE_LIMITED (429) |
Точные значения — за оператором
Числа выше — значения по умолчанию (постбэк — 600 запросов в минуту на ключ, вход — 20 в минуту на IP). Оператор платформы может их изменить. Закладывайте экспоненциальный backoff, а не фиксированные паузы.
Заголовок Retry-After
При ответе 429 платформа сообщает, когда можно повторить, заголовком Retry-After (секунды), а в теле — лимит и окно:
{
"error": {
"code": "POSTBACK_RATE_LIMITED",
"message": "Слишком много postback",
"details": { "limit": 600, "window_seconds": 60 }
}
}HTTP/1.1 429 Too Many Requests
Retry-After: 23Как обрабатывать 429
Не «долбите» эндпоинт в цикле. Получив 429, подождите Retry-After секунд (или используйте экспоненциальный backoff с джиттером) и повторите. Для постбэка повтор безопасен — он идемпотентен по external_order_id (см. ниже).
Постбэк защищён независимо от лимитов
Даже при всплеске нагрузки приём постбэков защищён HMAC-подписью и проверкой timestamp, а идемпотентность по external_order_id исключает двойной учёт при ретраях.
Идемпотентность
Idempotency-Key
На мутирующих POST, которые создают ресурс или двигают деньги (/affiliate/links, /payouts, /merchant/offers, выпуск ключей и платёжные операции), передавайте заголовок:
Idempotency-Key: 3f8a1c2e-7b3d-4c1a-9f2e-1a2b3c4d5e6fIdempotency-KeystringoptionalПоведение при повторе:
- Тот же ключ + то же тело → возвращается сохранённый ответ (тот же
2xx) с заголовкомIdempotent-Replay: true. Побочный эффект (создание, списание) выполняется ровно один раз. - Используйте один и тот же ключ при ретраях из-за сетевого таймаута — так вы не создадите второй оффер/вторую заявку на вывод.
Дедупликация постбэка
У постбэка собственная, более сильная идемпотентность — по external_order_id, поэтому Idempotency-Key для него вторичен и не обязателен.
- Ключ дедупликации —
external_order_idбизнеса (в связке с tenant и видом события). - Повторный постбэк с тем же
external_order_idне создаёт вторую конверсию: ответ —200с"duplicate": trueи тем жеconversion_id. - Конкурентные одновременные постбэки сериализуются advisory-блокировкой — гонка не приводит к дублю.
- Дедуп-запись хранится постоянно; для практических ретраев ориентируйтесь на окно 24 часа — в его пределах повтор гарантированно вернёт исходный результат.
{ "accepted": true, "duplicate": true, "conversion_id": 778 }Уникальность external_order_id
external_order_id должен быть уникальным на стороне бизнеса для каждого заказа. Если переиспользовать его для разных конверсий, второе событие будет воспринято как дубликат и не засчитается.
Анти-replay подписи (S2S)
Для подписанных S2S-запросов (постбэк, token-exchange) дополнительно действует защита от воспроизведения перехваченного запроса:
X-Timestampдолжен быть в пределах ±300 секунд от серверного времени, иначеPOSTBACK_STALE_TIMESTAMP(400).- Пара
(key_id, signature)помечается использованной в Redis на 600 секунд; повтор той же подписи →POSTBACK_REPLAY(409).
Это значит: каждый S2S-запрос подписывайте заново с актуальным X-Timestamp, а ретраи делайте именно повторной отправкой того же external_order_id (логическая идемпотентность), а не переотправкой байт-в-байт той же подписи. Детали подписи — на странице Постбэк.