Обработка ошибок
UnoAPI использует стандартные HTTP-коды ответов и возвращает детальную информацию об ошибках в JSON-формате.
Формат ошибки
Заголовок раздела «Формат ошибки»Все ошибки возвращаются в едином формате:
{ "error": "error_code", "message": "Human-readable description of the error"}| Поле | Тип | Описание |
|---|---|---|
error | string | Машиночитаемый код ошибки (snake_case) |
message | string | Описание ошибки для разработчика |
HTTP-коды ответов
Заголовок раздела «HTTP-коды ответов»2xx — Успех
Заголовок раздела «2xx — Успех»| Код | Описание |
|---|---|
200 | Запрос выполнен успешно |
201 | Ресурс создан |
204 | Запрос выполнен, тело ответа пустое |
4xx — Ошибки клиента
Заголовок раздела «4xx — Ошибки клиента»| Код | Ошибка | Описание |
|---|---|---|
400 | bad_request | Неверные параметры запроса |
401 | unauthorized | Отсутствует или неверный API-ключ |
403 | forbidden | Доступ запрещён (план, IP, ресурс) |
404 | not_found | Ресурс не найден |
408 | timeout | Таймаут выполнения запроса |
422 | validation_error | Ошибка валидации данных |
429 | rate_limit | Превышен лимит запросов |
5xx — Ошибки сервера
Заголовок раздела «5xx — Ошибки сервера»| Код | Ошибка | Описание |
|---|---|---|
500 | internal_error | Внутренняя ошибка сервера |
502 | bad_gateway | Ошибка upstream-сервиса |
503 | service_unavailable | Сервис временно недоступен |
504 | gateway_timeout | Таймаут upstream-сервиса |
Rate Limiting (429)
Заголовок раздела «Rate Limiting (429)»При превышении лимита запросов возвращается код 429 с дополнительными заголовками:
HTTP/1.1 429 Too Many RequestsX-RateLimit-Limit: 100X-RateLimit-Remaining: 0X-RateLimit-Reset: 1706745600Retry-After: 60| Заголовок | Описание |
|---|---|
X-RateLimit-Limit | Максимум запросов в текущем окне |
X-RateLimit-Remaining | Оставшиеся запросы |
X-RateLimit-Reset | Unix timestamp сброса лимита |
Retry-After | Секунды до возможности повтора |
Обработка ошибок в коде
Заголовок раздела «Обработка ошибок в коде»interface UnoAPIError { error: string; message: string;}
async function callAPI(path: string) { const response = await fetch(`https://proxy.unoapi.ru${path}`, { headers: { 'Authorization': `Bearer ${process.env.UNOAPI_KEY}` } });
if (!response.ok) { const error: UnoAPIError = await response.json();
switch (response.status) { case 401: throw new Error('Invalid API key'); case 429: const retryAfter = response.headers.get('Retry-After'); throw new Error(`Rate limited. Retry after ${retryAfter}s`); case 500: case 502: case 503: throw new Error(`Server error: ${error.message}`); default: throw new Error(`API error: ${error.message}`); } }
return response.json();}
// Использование с retryasync function callAPIWithRetry(path: string, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await callAPI(path); } catch (err) { if (i === maxRetries - 1) throw err; await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000)); } }}import requestsimport timeimport osfrom typing import Any
class UnoAPIError(Exception): def __init__(self, status_code: int, error: str, message: str): self.status_code = status_code self.error = error self.message = message super().__init__(f"{error}: {message}")
def call_api(path: str) -> Any: response = requests.get( f'https://proxy.unoapi.ru{path}', headers={'Authorization': f'Bearer {os.environ["UNOAPI_KEY"]}'} )
if not response.ok: data = response.json()
if response.status_code == 429: retry_after = response.headers.get('Retry-After', 60) raise UnoAPIError(429, 'rate_limit', f'Rate limited. Retry after {retry_after}s')
raise UnoAPIError( response.status_code, data.get('error', 'unknown'), data.get('message', 'Unknown error') )
return response.json()
# Использование с retrydef call_api_with_retry(path: str, max_retries: int = 3) -> Any: for i in range(max_retries): try: return call_api(path) except UnoAPIError as e: if e.status_code < 500 and e.status_code != 429: raise # Не retry на клиентские ошибки if i == max_retries - 1: raise time.sleep(2 ** i) # Exponential backoffpackage main
import ( "encoding/json" "fmt" "net/http" "time")
type APIError struct { StatusCode int Error string `json:"error"` Message string `json:"message"`}
func (e *APIError) Error() string { return fmt.Sprintf("%s: %s", e.Error, e.Message)}
func callAPI(path string) (map[string]interface{}, error) { req, _ := http.NewRequest("GET", "https://proxy.unoapi.ru"+path, nil) req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close()
if resp.StatusCode >= 400 { var apiErr APIError json.NewDecoder(resp.Body).Decode(&apiErr) apiErr.StatusCode = resp.StatusCode return nil, &apiErr }
var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) return result, nil}
// Использование с retryfunc callAPIWithRetry(path string, maxRetries int) (map[string]interface{}, error) { var lastErr error for i := 0; i < maxRetries; i++ { result, err := callAPI(path) if err == nil { return result, nil }
if apiErr, ok := err.(*APIError); ok { if apiErr.StatusCode < 500 && apiErr.StatusCode != 429 { return nil, err // Не retry на клиентские ошибки } }
lastErr = err time.Sleep(time.Duration(1<<i) * time.Second) // Exponential backoff } return nil, lastErr}Retry-стратегия
Заголовок раздела «Retry-стратегия»Рекомендуемая стратегия повторных запросов:
| Код | Retry? | Стратегия |
|---|---|---|
400, 401, 403, 404 | ❌ Нет | Исправьте запрос |
408 | ✅ Да | Немедленный retry (1-2 раза) |
429 | ✅ Да | Подождите Retry-After секунд |
500, 502, 503 | ✅ Да | Exponential backoff (1s, 2s, 4s…) |
504 | ✅ Да | Exponential backoff + увеличьте timeout |
Exponential Backoff
Заголовок раздела «Exponential Backoff»Формула задержки между retry:
delay = min(base_delay * 2^attempt + random_jitter, max_delay)Типичные значения:
base_delay: 1 секундаmax_delay: 30 секундrandom_jitter: 0-1 секундаmax_attempts: 3-5
Типичные ошибки
Заголовок раздела «Типичные ошибки»invalid_ip
Заголовок раздела «invalid_ip»{ "error": "invalid_ip", "message": "Invalid IP address format"}Решение: Проверьте формат IP-адреса (IPv4 или IPv6)
private_ip
Заголовок раздела «private_ip»{ "error": "private_ip", "message": "Private IP addresses are not supported"}Решение: Используйте публичный IP-адрес
invalid_url
Заголовок раздела «invalid_url»{ "error": "invalid_url", "message": "URL must be a valid HTTP or HTTPS URL"}Решение: URL должен начинаться с http:// или https://
batch_too_large
Заголовок раздела «batch_too_large»{ "error": "batch_too_large", "message": "Batch size exceeds maximum of 100"}Решение: Разбейте запрос на части по 100 элементов
Мониторинг ошибок
Заголовок раздела «Мониторинг ошибок»Рекомендуем логировать все ошибки API для анализа. Обращайте внимание на:
- Частые
400— проблемы с валидацией на клиенте - Рост
429— пора увеличить тарифный план - Всплески
5xx— проблемы на стороне API
При систематических ошибках 5xx проверьте статус-страницу или напишите в поддержку.