Обработка ошибок
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
}
]
}
Как отличать ошибки валидации
- Код
422— всегда ошибка параметра.source.parameterназывает параметр; если его нет, смотритеdetail. 429бывает двух видов — различайте поdetail:Daily rate limit of …(ждатьRetry-After) иYou can only report the same IP address … once in 15 minutes(ждать 15 минут). Второй содержитsource.parameter=ip.401vs403—401означает «ключ не принят»,403— «ключ принят, но прав/тарифа недостаточно». Повторять401бессмысленно: сначала исправьте ключ.- Ошибки строк внутри
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. |
check | ipAddress | The ipAddress field is required. |
check | ipAddress | The ip address must be a valid IPv4 or IPv6 address. |
check, reports, check-block | maxAgeInDays | The max age in days must be between 1 and 365. |
reports | page | The page must be an integer greater than or equal to 1. |
reports | perPage | The per page must be between 1 and 100. |
report | ip | The ip field is required. |
report | ip | The ip must be a valid IPv4 or IPv6 address. |
report | categories | The categories field is required. |
report | categories | The categories must be a comma separated list of category ids between 1 and 23. |
report | timestamp | The timestamp must be a valid ISO 8601 datetime. |
blacklist | confidenceMinimum | The confidence minimum must be between 25 and 100. |
blacklist | limit | The limit must be between 1 and 500000. |
blacklist | ipVersion | The ip version must be either 4 or 6. |
blacklist | onlyCountries, exceptCountries | The … must be a comma separated list of ISO 3166 alpha-2 country codes. |
check-block | network | The network field is required. |
check-block | network | The network must be a valid IPv4 or IPv6 CIDR network (IPv4 /16 to /24, IPv6 /48 to /64). |
bulk-report | csv | The csv field is required. |
bulk-report | csv | The csv must not contain more than 10000 rows. |
clear-address | ipAddress | The ip address field is required. |
clear-address | ipAddress | The 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