Error handling

Caching and monitoring: Caching & monitoring.

Platform errors (401, 403, 422, 429, 5xx, retry/backoff, rate-limit headers): Error Handling.

Empty results are not errors

count: 0 with flights: [] is a valid response. It means no seats were found for the requested itinerary on that date — not a client bug or server failure. Show a no-availability state in the UI rather than an error banner.

{
  "origin": "JFK",
  "destination": "LAX",
  "date": "2026-08-14",
  "passengers": 1,
  "count": 0,
  "flights": []
}
StatusMeaningUI response
200 with count > 0Results availableShow flight list
200 with count: 0No availabilityShow "No flights found for this route and date."
422Invalid parametersFix the request — do not surface to passengers
429Rate limit exceededBack off and retry — see retry helper

IATA codes only

This endpoint accepts IATA 3-letter codes (JFK, LAX, LHR) — not ICAO. Passing a 4-letter ICAO code (KJFK) returns a 422. Resolve codes first with Airport Search.

422 — validation errors

Returned when origin or destination are missing, blank, or fail the IATA format check.

FieldCommon cause
originMissing, blank, or a 4-letter ICAO code passed instead of IATA
destinationSame as above
passengersValue outside the 1–9 range

Prices are indicative

Quotes reflect a market snapshot at the time of search, not live airline inventory. Seats shown may sell out or change price between search and booking. Disclose this to passengers in any UI that shows prices (for example: "Prices may change at booking").

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}")

For transient failures (429, 5xx), use the retry helper on Error Handling.


Related: Flight Tickets · Airport Search · Caching & monitoring · Error Handling