Перейти к основному содержимому

Ошибки и лимиты

Это центральная страница со всеми кодами и лимитами Public API.

Формат ответа

Все ошибки приходят с JSON-телом единого формата:

{
"error": {
"code": "domain_not_verified",
"message": "Домен ещё не подтверждён.",
"details": {
"code": "domain_not_verified",
"domain": "example.ru"
}
},
"request_id": "3d6f0dce5d1b47bf8b3f9f98c9a70fd4"
}

Передавайте request_id поддержке: по нему можно найти конкретный запрос, не раскрывая API-ключ или содержимое письма.

Ошибки схемы запроса (422) возвращают безопасный список проблем в details.fields:

{
"error": {
"code": "request_validation_failed",
"message": "Проверьте заполненные поля.",
"details": {
"fields": [
{
"field": "to.0",
"message": "укажите корректный email",
"code": "value_error"
}
]
}
},
"request_id": "3d6f0dce5d1b47bf8b3f9f98c9a70fd4"
}

HTTP-коды

КодЗначениеКогда
400Bad RequestНекорректное содержимое после разбора схемы: base64, шаблон, значение header и т.д.
401UnauthorizedAPI-ключ отсутствует, невалиден, отозван или истёк
403ForbiddenПрав ключа недостаточно (например, READ_ONLY пытается отправлять)
404Not FoundРесурс не найден или не принадлежит проекту/организации
409ConflictДублирующийся ресурс (slug шаблона, имя ключа, домен и т.д.)
422Unprocessable EntityОшибка схемы или бизнес-проверки: зарезервированный header, домен не готов и т.д.
429Too Many RequestsПревышен RPS или месячный объём тарифа
5xxServer ErrorВнутренняя ошибка — повторите запрос с backoff

Rate Limits

При 429 API возвращает заголовок Retry-After — минимальное число секунд до безопасной повторной попытки:

Retry-After: 1

Для секундного ограничения обычно возвращается Retry-After: 1. При исчерпании месячного объёма значение указывает время до начала следующего календарного месяца по UTC. Заголовки X-RateLimit-* API сейчас не предоставляет.

Лимиты по плану

ПараметрFREEPAIDPREMIUM
Объём отправки в месяц10010 000100 000
Доменов1550
Webhook endpoints3101 000

Объём считается по получателям: одно письмо с тремя адресами в to/cc/bcc расходует три единицы. Счётчик сбрасывается в начале календарного месяца по UTC.

Для live-отправки действует секундный лимит проекта, по умолчанию 10 запросов на API-ключ. TEST-ключи создают записи со статусом SIMULATED: реальной доставки нет, месячный объём и live RPS не расходуются.

Лимиты на письмо

ПараметрЗначение
Получателей (to + cc + bcc)50
reply_to адресов10
Тегов10
Длина тега32 символа
Длина subject998 символов (RFC 5321)
Размер вложения10 МиБ
Вложений в письме10
Размер письма целиком25 МиБ
Писем в одном POST /v1/emails/batch100

Retry с экспоненциальным backoff

Корректная стратегия для интеграции:

import time

import httpx


def send_with_retry(payload: dict, api_key: str, max_retries: int = 3) -> dict:
"""Отправляет письмо с retry на 429 и 5xx.

Args:
payload: Тело запроса POST /v1/emails.
api_key: Plain API-ключ (mi_live_... или mi_test_...).
max_retries: Максимум повторных попыток.

Returns:
Поле `data` успешного ответа.

Raises:
RuntimeError: Если все попытки исчерпаны.
"""
delay = 1.0
for _ in range(max_retries):
r = httpx.post(
"https://api.mailinfra.ru/v1/emails",
headers={"Authorization": f"Bearer {api_key}"},
json=payload,
)
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", delay))
time.sleep(wait)
delay *= 2
continue
if r.status_code >= 500:
time.sleep(delay)
delay *= 2
continue
r.raise_for_status()
return r.json()["data"]
raise RuntimeError("Max retries exceeded")

Для безопасного retry на сетевых таймаутах (ConnectError, чтение прервалось) добавляйте заголовок Idempotency-Key — повтор не приведёт к дубликату письма. См. Идемпотентность.