Тема
Войти Регистрация

Обработка ошибок

API v2 возвращает ошибки в конверте JSON:API-подобного формата. HTTP-код — самый надёжный признак: сначала ветвитесь по нему, и только потом читайте detail.

Раздел «API v2»: Обзор · Проверка IP (CHECK) · Жалобы по IP (REPORTS) · Отправить жалобу (REPORT) · Чёрный список (BLACKLIST) · Проверка подсети (CHECK-BLOCK) · Массовые жалобы (BULK-REPORT) · Удаление жалоб (CLEAR-ADDRESS) · Лимиты · Ошибки · Fail2Ban, CSF, Suricata · FAQ

Конверт ошибки

{
  "errors": [
    {
      "detail": "The max age in days must be between 1 and 365.",
      "status": 422,
      "source": { "parameter": "maxAgeInDays" }
    }
  ]
}
ПолеОбязательноЧто означает
errorsдамассив объектов; в v2 всегда один элемент, но клиент должен быть готов к нескольким
detailдачеловекочитаемое описание на английском, в стиле AbuseIPDB. Текст стабилен, но не является машиночитаемым кодом: ветвитесь по status и source.parameter
statusдатот же HTTP-код, продублированный в теле
source.parameterнетимя параметра запроса, который не прошёл валидацию; есть только у ошибок валидации

Не разбирайте detail парсером строк как единственный признак ошибки — используйте HTTP-код и source.parameter.

Коды ответа

КодКогда появляетсяТелоДействие клиента
400не используется в v2——
401ключ не передан, неверен, отозван или истёк{"errors":[{"detail":"…","status":401}]}исправить ключ, не повторять
402не используется в AbuseIPDB.RU (у abuseipdb.com — превышение тарифа check-block)—маска вне диапазона даёт 422
403эндпоинт требует более высокого тарифа (clear-address — admin){"errors":[{"detail":"This endpoint (clear-address) requires an active subscription.","status":403}]}не повторять, сменить ключ
404такого эндпоинта нет; известное отличие: неизвестный путь под /api/v2/* обрабатывается платформенным роутером и отдаёт конверт платформы {"ok":false,"error":{…}}, а не v2-конвертсм. нижепроверить путь
422ошибка валидации параметра{"errors":[{"detail":"…","status":422,"source":{"parameter":"…"}}]}исправить параметр
429дневная квота или повторная жалоба в течение 15 минут{"errors":[{"detail":"…","status":429}]}ждать Retry-After / 15 минут
500внутренняя ошибка; известное отличие: неперехваченное исключение отдаёт платформенный конверт {"ok":false,"error":{…}}см. нижеповторить с backoff, сообщить в поддержку

Важно про 404/500: все семь документированных эндпоинтов (check, reports, report, blacklist, check-block, bulk-report, clear-address) и /status всегда отвечают v2-конвертом. Платформенный конверт {"ok":false,…} возможен только на путях вне v2 или при необработанном сбое — это ограничение совместимости, о котором нужно знать, если вы пишете универсальный разбор ошибок.

Явные примеры ошибок (для приёмки)

# 401 — без ключа
curl -i -G https://api.abuseipdb.ru/api/v2/check \
  --data-urlencode "ipAddress=8.8.8.8" \
  -H "Accept: application/json"

Ожидаемый ответ:

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
{
  "errors": [
    {
      "detail": "Authentication failed. You have not provided a valid API key.",
      "status": 401
    }
  ]
}
# 401 — неверный ключ
curl -i -G https://api.abuseipdb.ru/api/v2/check \
  --data-urlencode "ipAddress=8.8.8.8" \
  -H "Key: definitely-not-a-valid-key" \
  -H "Accept: application/json"
{
  "errors": [
    {
      "detail": "Authentication failed. The API key you provided is invalid, expired or revoked.",
      "status": 401
    }
  ]
}
# 422 — невалидный maxAgeInDays
curl -i -G https://api.abuseipdb.ru/api/v2/check \
  --data-urlencode "ipAddress=8.8.8.8" \
  -d maxAgeInDays=0 \
  -H "Key: $API_KEY" \
  -H "Accept: application/json"
{
  "errors": [
    {
      "detail": "The max age in days must be between 1 and 365.",
      "status": 422,
      "source": { "parameter": "maxAgeInDays" }
    }
  ]
}
# 422 — невалидный адрес
curl -i -G https://api.abuseipdb.ru/api/v2/check \
  --data-urlencode "ipAddress=not-an-ip" \
  -H "Key: $API_KEY" \
  -H "Accept: application/json"
{
  "errors": [
    {
      "detail": "The ip address must be a valid IPv4 or IPv6 address.",
      "status": 422,
      "source": { "parameter": "ipAddress" }
    }
  ]
}
# 429 — дневная квота
curl -i -G https://api.abuseipdb.ru/api/v2/check \
  --data-urlencode "ipAddress=8.8.8.8" \
  -H "Key: $API_KEY" \
  -H "Accept: application/json"
HTTP/1.1 429 Too Many Requests
Retry-After: 29241
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1545973200
{
  "errors": [
    {
      "detail": "Daily rate limit of 1000 requests exceeded for this endpoint. See headers for additional details.",
      "status": 429
    }
  ]
}
# 429 — повторная жалоба за 15 минут (нужен любой ключ)
curl -i https://api.abuseipdb.ru/api/v2/report \
  --data-urlencode "ip=89.205.125.160" \
  -d categories=18 \
  --data-urlencode "comment=test duplicate" \
  -H "Key: $API_KEY" \
  -H "Accept: application/json"
{
  "errors": [
    {
      "detail": "You can only report the same IP address once in 15 minutes.",
      "status": 429,
      "source": { "parameter": "ip" }
    }
  ]
}
# 403 — clear-address обычным ключом
curl -i -X DELETE "https://api.abuseipdb.ru/api/v2/clear-address?ipAddress=89.205.125.160" \
  -H "Key: $API_KEY" \
  -H "Accept: application/json"
{
  "errors": [
    {
      "detail": "This endpoint (clear-address) requires an active subscription.",
      "status": 403
    }
  ]
}

Как отличать ошибки валидации

  1. Код 422 — всегда ошибка параметра. source.parameter называет параметр; если его нет, смотрите detail.
  2. 429 бывает двух видов — различайте по detail: Daily rate limit of … (ждать Retry-After) и You can only report the same IP address … once in 15 minutes (ждать 15 минут). Второй содержит source.parameter=ip.
  3. 401 vs 403 — 401 означает «ключ не принят», 403 — «ключ принят, но прав/тарифа недостаточно». Повторять 401 бессмысленно: сначала исправьте ключ.
  4. Ошибки строк внутри bulk-report приходят не в HTTP-коде, а в теле успешного 200 (invalidReports) — см. BULK-REPORT.

Справочник detail по эндпоинтам

ЭндпоинтПараметрdetail
все—Authentication failed. You have not provided a valid API key.
все—Authentication failed. The API key you provided is invalid, expired or revoked.
checkipAddressThe ipAddress field is required.
checkipAddressThe ip address must be a valid IPv4 or IPv6 address.
check, reports, check-blockmaxAgeInDaysThe max age in days must be between 1 and 365.
reportspageThe page must be an integer greater than or equal to 1.
reportsperPageThe per page must be between 1 and 100.
reportipThe ip field is required.
reportipThe ip must be a valid IPv4 or IPv6 address.
reportcategoriesThe categories field is required.
reportcategoriesThe categories must be a comma separated list of category ids between 1 and 23.
reporttimestampThe timestamp must be a valid ISO 8601 datetime.
blacklistconfidenceMinimumThe confidence minimum must be between 25 and 100.
blacklistlimitThe limit must be between 1 and 500000.
blacklistipVersionThe ip version must be either 4 or 6.
blacklistonlyCountries, exceptCountriesThe … must be a comma separated list of ISO 3166 alpha-2 country codes.
check-blocknetworkThe network field is required.
check-blocknetworkThe network must be a valid IPv4 or IPv6 CIDR network (IPv4 /16 to /24, IPv6 /48 to /64).
bulk-reportcsvThe csv field is required.
bulk-reportcsvThe csv must not contain more than 10000 rows.
clear-addressipAddressThe ip address field is required.
clear-addressipAddressThe ip address must be a valid IPv4 or IPv6 address.

Тексты detail совпадают с abuseipdb.com настолько, насколько это возможно: клиенты (в том числе Fail2Ban) иногда печатают их как есть.

Правильный клиент

import time
import requests

API_KEY = "YOUR_OWN_API_KEY"
BASE = "https://api.abuseipdb.ru/api/v2"

def api_get(path: str, params: dict) -> dict:
    for attempt in range(1, 4):
        response = requests.get(
            f"{BASE}/{path}",
            params=params,
            headers={"Key": API_KEY, "Accept": "application/json"},
            timeout=15,
        )
        if response.status_code == 200:
            return response.json()["data"]

        # 1) сначала код
        if response.status_code == 401:
            raise RuntimeError("ключ не принят: " + response.text)
        if response.status_code == 403:
            raise RuntimeError("нет прав/тарифа: " + response.text)
        if response.status_code == 422:
            error = response.json()["errors"][0]
            parameter = error.get("source", {}).get("parameter", "?")
            raise ValueError(f"параметр {parameter}: {error['detail']}")
        if response.status_code == 429:
            wait = int(response.headers.get("Retry-After", "60"))
            if attempt == 3:
                raise RuntimeError("квота исчерпана: " + response.text)
            time.sleep(wait)
            continue
        if response.status_code >= 500:
            time.sleep(2 ** attempt)
            continue

        # 2) платформенный конверт (404/500 вне v2) — известное ограничение
        raise RuntimeError(f"HTTP {response.status_code}: {response.text}")

    raise RuntimeError("unreachable")

print(api_get("check", {"ipAddress": "8.8.8.8", "maxAgeInDays": 90}))

Практические рекомендации

  • Всегда передавайте Accept: application/json: так вы гарантированно получите конверт, а не HTML-страницу ошибки.
  • Логируйте status, detail и source.parameter, но не логируйте ключ API.
  • При 429 уважайте Retry-After и добавляйте джиттер, чтобы все клиенты не пошли одновременно.
  • Не повторяйте 401/403/422: это ошибки конфигурации или запроса, повтор их не исправит.
  • 5xx повторяйте с экспоненциальной задержкой (максимум 2–3 попытки).
  • Для bulk-report проверяйте invalidReports даже при 200.

Версия 2.2.0 · AbuseIPDB.RU · webmaster@abuseipdb.ru · source: docs/ABUSE-API-errors.md