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

Обработка ошибок поиска авиабилетов в продакшне — пустые результаты, недопустимые маршруты и ошибки валидации.



Кэширование и мониторинг: [Кэширование и мониторинг](/docs/v31/tickets-integration-production).

**Ошибки платформы** (401, 403, 422, 429, 5xx, повторные попытки и экспоненциальная задержка (backoff), заголовки rate limit): [Обработка ошибок](/docs/errors).

### Пустые результаты — не ошибка [#empty-results-are-not-errors]

`count: 0` с `flights: []` — это **корректный** ответ. Он означает, что на эту дату для запрошенного маршрута мест не найдено, — это не ошибка клиента и не сбой сервера. Показывайте в интерфейсе состояние «нет мест», а не баннер с ошибкой.

```json
{
  "origin": "JFK",
  "destination": "LAX",
  "date": "2026-08-14",
  "passengers": 1,
  "count": 0,
  "flights": []
}
```

| Статус              | Значение                                            | Реакция интерфейса                                                                                        |
| ------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `200` с `count > 0` | Результаты есть                                     | Показать список рейсов                                                                                    |
| `200` с `count: 0`  | Нет мест                                            | Показать «Рейсы по этому маршруту на эту дату не найдены.»                                                |
| `422`               | Недопустимые параметры                              | Исправить запрос — не показывать пассажирам                                                               |
| `429`               | Превышено ограничение частоты запросов (rate limit) | Подождать и повторить попытку — см. [вспомогательную функцию повторных попыток](/docs/errors#retry-rules) |

### Только коды IATA [#iata-codes-only]

Этот эндпоинт принимает **трёхбуквенные коды IATA** (`JFK`, `LAX`, `LHR`), а не ICAO. Передача четырёхбуквенного кода ICAO (`KJFK`) возвращает `422`. Сначала определите коды через [поиск аэропортов](/docs/v31/airport-search).

### 422 — ошибки валидации [#422--validation-errors]

Возвращается, если `origin` или `destination` отсутствуют, пусты или не проходят проверку формата IATA.

| Поле          | Частая причина                                                       |
| ------------- | -------------------------------------------------------------------- |
| `origin`      | Отсутствует, пусто или вместо IATA передан четырёхбуквенный код ICAO |
| `destination` | То же, что выше                                                      |
| `passengers`  | Значение вне диапазона 1–9                                           |

### Цены носят ориентировочный характер [#prices-are-indicative]

Котировки отражают снимок рынка на момент поиска, а не актуальное наличие мест у авиакомпании. Показанные места могут быть распроданы или подорожать между поиском и бронированием. **Сообщайте об этом пассажирам** в любом интерфейсе, где показываются цены (например: «Цены могут измениться при бронировании»).

```python
import os
import requests

HEADERS = {
    "X-RapidAPI-Key": os.getenv("RAPIDAPI_KEY", "YOUR_RAPIDAPI_KEY"),
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"


def search_tickets(
    origin: str,
    destination: str,
    date: str | None = None,
    passengers: int = 1,
) -> dict:
    """
    Search tickets. Returns the full response dict including count and flights.
    Raises ValueError on 422. Returns count:0/flights:[] for no availability.
    """
    params: dict[str, str | int] = {
        "origin": origin,
        "destination": destination,
        "passengers": passengers,
    }
    if date:
        params["date"] = date

    r = requests.get(
        f"{BASE}/tickets/search",
        headers=HEADERS,
        params=params,
        timeout=(10, 15),
    )
    if r.status_code == 422:
        detail = r.json().get("detail", "validation error")
        raise ValueError(f"422 Unprocessable: {detail}")
    r.raise_for_status()
    return r.json()


def display_results(data: dict) -> None:
    flights = data.get("flights", [])
    origin = data.get("origin", "?")
    dest = data.get("destination", "?")

    if not flights:
        # count:0 is valid — show a user-friendly empty state
        print(f"No flights found for {origin} → {dest} on {data.get('date', '?')}.")
        print("Try a different date or check nearby airports.")
        return

    cheapest = flights[0]
    price = cheapest.get("price_usd", 0.0)
    duration = cheapest.get("total_duration_min", 0)
    stops = cheapest.get("stops", 0)

    print(
        f"{origin} → {dest}  ·  {len(flights)} option(s)  ·  "
        f"from ${price:.2f}  ·  {duration // 60}h {duration % 60:02d}m  ·  "
        f"{'Nonstop' if stops == 0 else f'{stops} stop(s)'}"
    )
    print("Prices may change at booking.")


if __name__ == "__main__":
    data = search_tickets("JFK", "LAX")
    display_results(data)

    try:
        search_tickets("KJFK", "KLAX")
    except ValueError as exc:
        print(f"Caught expected error: {exc}")
```

При временных сбоях (`429`, `5xx`) используйте вспомогательную функцию повторных попыток из раздела [Обработка ошибок](/docs/errors#retry-rules).

***

См. также: [Авиабилеты](/docs/v31/tickets) · [Поиск аэропортов](/docs/v31/airport-search) · [Кэширование и мониторинг](/docs/v31/tickets-integration-production) · [Обработка ошибок](/docs/errors)
