Flyan 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flyan/__init__.py +0 -0
- flyan/misc.py +132 -0
- flyan/ryanair.py +158 -0
- flyan-0.1.0.dist-info/METADATA +282 -0
- flyan-0.1.0.dist-info/RECORD +7 -0
- flyan-0.1.0.dist-info/WHEEL +5 -0
- flyan-0.1.0.dist-info/top_level.txt +1 -0
flyan/__init__.py
ADDED
|
File without changes
|
flyan/misc.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Optional, Dict
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, field_validator
|
|
6
|
+
|
|
7
|
+
currencies: Dict[str, str] = json.load(open("./currencies.json", encoding="utf-8"))
|
|
8
|
+
stations: Dict[str, str] = json.load(open("./stations.json", encoding="utf-8"))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Airport(BaseModel):
|
|
12
|
+
country_name: str
|
|
13
|
+
iata_code: str
|
|
14
|
+
name: str
|
|
15
|
+
seo_name: str
|
|
16
|
+
city_name: str
|
|
17
|
+
city_code: str
|
|
18
|
+
city_country_code: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Flight(BaseModel):
|
|
22
|
+
departure_airport: Airport
|
|
23
|
+
arrival_airport: Airport
|
|
24
|
+
departure_date: datetime
|
|
25
|
+
arrival_date: datetime
|
|
26
|
+
price: float
|
|
27
|
+
currency: str
|
|
28
|
+
flight_key: str
|
|
29
|
+
flight_number: str
|
|
30
|
+
previous_price: Optional[str | float]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ReturnFlight(BaseModel):
|
|
34
|
+
outbound: Flight
|
|
35
|
+
inbound: Flight
|
|
36
|
+
summary_price: float
|
|
37
|
+
summary_currency: str
|
|
38
|
+
previous_price: str | float
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class FlightSearchParams(BaseModel):
|
|
42
|
+
"""Parameters for flight searches"""
|
|
43
|
+
|
|
44
|
+
from_airport: str
|
|
45
|
+
from_date: datetime
|
|
46
|
+
to_date: datetime
|
|
47
|
+
destination_country: Optional[str] = None
|
|
48
|
+
max_price: Optional[int] = None
|
|
49
|
+
to_airport: Optional[str] = None
|
|
50
|
+
departure_time_from: Optional[str] = "00:00"
|
|
51
|
+
departure_time_to: Optional[str] = "23:59"
|
|
52
|
+
|
|
53
|
+
@field_validator("from_airport")
|
|
54
|
+
def validate_airport(cls, v: str):
|
|
55
|
+
|
|
56
|
+
if v not in stations.keys():
|
|
57
|
+
raise ValueError("Airport code must be a 3-letter IATA code")
|
|
58
|
+
|
|
59
|
+
return v.upper()
|
|
60
|
+
|
|
61
|
+
@field_validator("from_date", "to_date")
|
|
62
|
+
def validate_dates(cls, v: datetime):
|
|
63
|
+
if v < datetime.now():
|
|
64
|
+
raise ValueError("Date from or to cannot be in the past")
|
|
65
|
+
|
|
66
|
+
return v
|
|
67
|
+
|
|
68
|
+
@field_validator("max_price")
|
|
69
|
+
def validate_price(cls, v: int | None):
|
|
70
|
+
if v is None:
|
|
71
|
+
return v
|
|
72
|
+
|
|
73
|
+
if v <= 0:
|
|
74
|
+
raise ValueError("Price can't be negative")
|
|
75
|
+
|
|
76
|
+
return v
|
|
77
|
+
|
|
78
|
+
def to_api_params(self) -> Dict[str, str | int]:
|
|
79
|
+
"""Convert the parameters to the format expected by the Ryanair API"""
|
|
80
|
+
params: Dict[str, str | int] = {
|
|
81
|
+
"departureAirportIataCode": self.from_airport,
|
|
82
|
+
"outboundDepartureDateFrom": self.from_date.date().isoformat(),
|
|
83
|
+
"outboundDepartureDateTo": self.to_date.date().isoformat(),
|
|
84
|
+
"outboundDepartureTimeFrom": self.departure_time_from or "00:00",
|
|
85
|
+
"outboundDepartureTimeTo": self.departure_time_to or "23:59",
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if self.destination_country:
|
|
89
|
+
params["arrivalCountryCode"] = self.destination_country
|
|
90
|
+
|
|
91
|
+
if self.max_price:
|
|
92
|
+
params["priceValueTo"] = self.max_price
|
|
93
|
+
|
|
94
|
+
if self.to_airport:
|
|
95
|
+
params["arrivalAirportIataCode"] = self.to_airport
|
|
96
|
+
|
|
97
|
+
return params
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ReturnFlightSearchParams(FlightSearchParams):
|
|
101
|
+
"""Parameters for return flight searches"""
|
|
102
|
+
|
|
103
|
+
return_date_from: datetime
|
|
104
|
+
return_date_to: datetime
|
|
105
|
+
inbound_departure_time_from: Optional[str] = "00:00"
|
|
106
|
+
inbound_departure_time_to: Optional[str] = "23:59"
|
|
107
|
+
|
|
108
|
+
@field_validator("return_date_from", "return_date_to")
|
|
109
|
+
@classmethod
|
|
110
|
+
def validate_return_dates(cls, v: datetime) -> datetime:
|
|
111
|
+
# Note: In Pydantic v2, cross-field validation should be done with model_validator
|
|
112
|
+
# This is a simplified version - ideally use model_validator for cross-field validation
|
|
113
|
+
return v
|
|
114
|
+
|
|
115
|
+
def to_api_params(self) -> Dict[str, str | int]:
|
|
116
|
+
"""Convert the parameters to the format expected by the Ryanair API"""
|
|
117
|
+
params = super().to_api_params()
|
|
118
|
+
|
|
119
|
+
additional_params = {
|
|
120
|
+
"inboundDepartureDateFrom": self.return_date_from.date().isoformat(),
|
|
121
|
+
"inboundDepartureDateTo": self.return_date_to.date().isoformat(),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if self.inbound_departure_time_from:
|
|
125
|
+
additional_params["inboundDepartureTimeFrom"] = (
|
|
126
|
+
self.inbound_departure_time_from
|
|
127
|
+
)
|
|
128
|
+
if self.inbound_departure_time_to:
|
|
129
|
+
additional_params["inboundDepartureTimeTo"] = self.inbound_departure_time_to
|
|
130
|
+
|
|
131
|
+
params.update(additional_params)
|
|
132
|
+
return params
|
flyan/ryanair.py
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from fake_useragent.fake import UserAgent
|
|
7
|
+
from tenacity import (
|
|
8
|
+
retry,
|
|
9
|
+
retry_if_exception_type,
|
|
10
|
+
stop_after_attempt,
|
|
11
|
+
wait_exponential,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from flyan.misc import (
|
|
15
|
+
Airport,
|
|
16
|
+
Flight,
|
|
17
|
+
FlightSearchParams,
|
|
18
|
+
ReturnFlight,
|
|
19
|
+
currencies,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
ua = UserAgent()
|
|
23
|
+
logger = logging.getLogger("Flyan")
|
|
24
|
+
if not logger.handlers:
|
|
25
|
+
logger.setLevel(logging.INFO)
|
|
26
|
+
|
|
27
|
+
console_handler = logging.StreamHandler()
|
|
28
|
+
formatter = logging.Formatter(
|
|
29
|
+
"%(asctime)s.%(msecs)03d %(levelname)s:%(message)s", datefmt="%Y-%m-%d %I:%M:%S"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
console_handler.setFormatter(formatter)
|
|
33
|
+
logger.addHandler(console_handler)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RyanairException(Exception):
|
|
37
|
+
def __init__(self, message: str):
|
|
38
|
+
super().__init__(f"Ryanair API: {message}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class RyanAir:
|
|
42
|
+
"""
|
|
43
|
+
Create a RyanAir instance
|
|
44
|
+
|
|
45
|
+
:param str currency: Preferred currency
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
BASE_SERVICES_API_URL = "https://services-api.ryanair.com/farfnd/v4"
|
|
49
|
+
AGGREGATE_URL = "https://www.ryanair.com/api/views/locate/3/aggregate/all/en"
|
|
50
|
+
|
|
51
|
+
def __init__(self, currency: str = "EUR"):
|
|
52
|
+
self.client = httpx.Client(
|
|
53
|
+
headers={
|
|
54
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
55
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
56
|
+
"Accept-Language": "en-GB,en;q=0.9",
|
|
57
|
+
"Cache-Control": "no-cache",
|
|
58
|
+
"Pragma": "no-cache",
|
|
59
|
+
"Priority": "u=0, i",
|
|
60
|
+
"Upgrade-Insecure-Requests": "1",
|
|
61
|
+
"User-Agent": ua.random,
|
|
62
|
+
},
|
|
63
|
+
follow_redirects=True,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
self.__get("https://www.ryanair.com")
|
|
67
|
+
|
|
68
|
+
if currency in currencies.keys():
|
|
69
|
+
self.currency = currency
|
|
70
|
+
|
|
71
|
+
else:
|
|
72
|
+
self.currency = "EUR"
|
|
73
|
+
|
|
74
|
+
def __del__(self):
|
|
75
|
+
self.client.close()
|
|
76
|
+
|
|
77
|
+
@retry(
|
|
78
|
+
stop=stop_after_attempt(5), # Retry up to 5 times
|
|
79
|
+
wait=wait_exponential(),
|
|
80
|
+
retry=retry_if_exception_type(Exception),
|
|
81
|
+
reraise=True, # Raise the exception after retries are exhausted
|
|
82
|
+
)
|
|
83
|
+
def __get(self, url: str, params: dict[str, str] = {}) -> httpx.Response:
|
|
84
|
+
"""
|
|
85
|
+
Send a GET request to url
|
|
86
|
+
|
|
87
|
+
:param str url: The url to GET
|
|
88
|
+
:param dict params: URL Parameters for the query
|
|
89
|
+
:return httpx.Response: Response object
|
|
90
|
+
:raises httpx.HTTPStatusError: If one occurred
|
|
91
|
+
"""
|
|
92
|
+
response = self.client.get(url, params=params)
|
|
93
|
+
response.raise_for_status()
|
|
94
|
+
return response
|
|
95
|
+
|
|
96
|
+
def __parse_airport(self, airport: Dict[str, Any]) -> Airport:
|
|
97
|
+
return Airport(
|
|
98
|
+
country_name=airport["countryName"],
|
|
99
|
+
iata_code=airport["iataCode"],
|
|
100
|
+
name=airport["name"],
|
|
101
|
+
seo_name=airport["seoName"],
|
|
102
|
+
city_name=airport["city"]["name"],
|
|
103
|
+
city_code=airport["city"]["code"],
|
|
104
|
+
city_country_code=airport["city"]["countryCode"],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def __parse_fare(self, fare: Dict[str, Any], k: str = "outbound") -> Flight:
|
|
108
|
+
dep_date = datetime.fromisoformat(fare[k]["departureDate"])
|
|
109
|
+
arr_date = datetime.fromisoformat(fare[k]["arrivalDate"])
|
|
110
|
+
|
|
111
|
+
return Flight(
|
|
112
|
+
departure_airport=self.__parse_airport(fare[k]["departureAirport"]),
|
|
113
|
+
arrival_airport=self.__parse_airport(fare[k]["arrivalAirport"]),
|
|
114
|
+
departure_date=dep_date,
|
|
115
|
+
arrival_date=arr_date,
|
|
116
|
+
price=fare[k]["price"]["value"],
|
|
117
|
+
currency=fare[k]["price"]["currencyCode"],
|
|
118
|
+
flight_key=fare[k]["flightKey"],
|
|
119
|
+
flight_number=fare[k]["flightNumber"],
|
|
120
|
+
previous_price=fare[k].get("previousPrice", None),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def __parse_return_fare(self, fare: Dict[str, Any]) -> ReturnFlight:
|
|
124
|
+
outbound = self.__parse_fare(fare, "outbound")
|
|
125
|
+
inbound = self.__parse_fare(fare, "inbound")
|
|
126
|
+
|
|
127
|
+
return ReturnFlight(
|
|
128
|
+
outbound=outbound,
|
|
129
|
+
inbound=inbound,
|
|
130
|
+
summary_price=fare["summary"]["price"]["value"],
|
|
131
|
+
summary_currency=fare["summary"]["price"]["currencyCode"],
|
|
132
|
+
previous_price=fare.get("previousPrice") or 0,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def get_oneways(self, params: FlightSearchParams) -> list[Flight]:
|
|
136
|
+
"""
|
|
137
|
+
Get oneways
|
|
138
|
+
"""
|
|
139
|
+
print("Getting oneways")
|
|
140
|
+
url = f"{self.BASE_SERVICES_API_URL}/oneWayFares"
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
r = self.__get(url, params.to_api_params())
|
|
144
|
+
if not r.is_success:
|
|
145
|
+
return []
|
|
146
|
+
|
|
147
|
+
fares = r.json()["fares"]
|
|
148
|
+
flights = [self.__parse_fare(f) for f in fares]
|
|
149
|
+
|
|
150
|
+
return flights
|
|
151
|
+
|
|
152
|
+
except httpx.HTTPError:
|
|
153
|
+
logger.error(f"A HTTP Error occured trying to get {url}", exc_info=True)
|
|
154
|
+
return []
|
|
155
|
+
|
|
156
|
+
except KeyError as KE:
|
|
157
|
+
print(KE)
|
|
158
|
+
return []
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: Flyan
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Open source unofficial API wrappper to get flight data from RyanAir.
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: fake-useragent>=1.5.1
|
|
8
|
+
Requires-Dist: httpx>=0.27.2
|
|
9
|
+
Requires-Dist: pydantic>=2.9.2
|
|
10
|
+
Requires-Dist: tenacity>=9.0.0
|
|
11
|
+
|
|
12
|
+
# Flyan SDK
|
|
13
|
+
|
|
14
|
+
An open-source unofficial API wrapper to get flight data from Ryanair.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install flyan
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or using uv:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv add flyan
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from datetime import datetime
|
|
32
|
+
from flyan import RyanAir, FlightSearchParams
|
|
33
|
+
|
|
34
|
+
# Initialize the client
|
|
35
|
+
client = RyanAir(currency="EUR")
|
|
36
|
+
|
|
37
|
+
# Set up search parameters
|
|
38
|
+
search_params = FlightSearchParams(
|
|
39
|
+
from_airport="DUB", # Dublin
|
|
40
|
+
to_airport="BCN", # Barcelona
|
|
41
|
+
from_date=datetime(2025, 8, 15),
|
|
42
|
+
to_date=datetime(2025, 8, 20),
|
|
43
|
+
max_price=200
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Search for one-way flights
|
|
47
|
+
flights = client.get_oneways(search_params)
|
|
48
|
+
|
|
49
|
+
# Display results
|
|
50
|
+
for flight in flights:
|
|
51
|
+
print(f"Flight {flight.flight_number}: {flight.departure_airport.name} → {flight.arrival_airport.name}")
|
|
52
|
+
print(f"Departure: {flight.departure_date}")
|
|
53
|
+
print(f"Price: {flight.price} {flight.currency}")
|
|
54
|
+
print("---")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## API Reference
|
|
58
|
+
|
|
59
|
+
### RyanAir Class
|
|
60
|
+
|
|
61
|
+
#### Constructor
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
RyanAir(currency: str = "EUR")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Creates a new RyanAir client instance.
|
|
68
|
+
|
|
69
|
+
**Parameters:**
|
|
70
|
+
|
|
71
|
+
- `currency` (str, optional): Preferred currency for pricing. Defaults to "EUR". Must be a valid currency code from the supported currencies list.
|
|
72
|
+
|
|
73
|
+
**Example:**
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
# Default EUR currency
|
|
77
|
+
client = RyanAir()
|
|
78
|
+
|
|
79
|
+
# Specific currency
|
|
80
|
+
client = RyanAir(currency="USD")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Methods
|
|
84
|
+
|
|
85
|
+
##### `get_oneways(params: FlightSearchParams) -> list[Flight]`
|
|
86
|
+
|
|
87
|
+
Search for one-way flights.
|
|
88
|
+
|
|
89
|
+
**Parameters:**
|
|
90
|
+
|
|
91
|
+
- `params` (FlightSearchParams): Search parameters
|
|
92
|
+
|
|
93
|
+
**Returns:**
|
|
94
|
+
|
|
95
|
+
- `list[Flight]`: List of available flights
|
|
96
|
+
|
|
97
|
+
### FlightSearchParams Class
|
|
98
|
+
|
|
99
|
+
Parameters for searching flights.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
FlightSearchParams(
|
|
103
|
+
from_airport: str,
|
|
104
|
+
from_date: datetime,
|
|
105
|
+
to_date: datetime,
|
|
106
|
+
destination_country: Optional[str] = None,
|
|
107
|
+
max_price: Optional[int] = None,
|
|
108
|
+
to_airport: Optional[str] = None,
|
|
109
|
+
departure_time_from: Optional[str] = "00:00",
|
|
110
|
+
departure_time_to: Optional[str] = "23:59"
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Parameters:**
|
|
115
|
+
|
|
116
|
+
- `from_airport` (str): IATA code of departure airport (e.g., "DUB")
|
|
117
|
+
- `from_date` (datetime): Earliest departure date
|
|
118
|
+
- `to_date` (datetime): Latest departure date
|
|
119
|
+
- `destination_country` (str, optional): Country code for destination
|
|
120
|
+
- `max_price` (int, optional): Maximum price filter
|
|
121
|
+
- `to_airport` (str, optional): IATA code of arrival airport
|
|
122
|
+
- `departure_time_from` (str, optional): Earliest departure time (HH:MM format)
|
|
123
|
+
- `departure_time_to` (str, optional): Latest departure time (HH:MM format)
|
|
124
|
+
|
|
125
|
+
**Example:**
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from datetime import datetime
|
|
129
|
+
|
|
130
|
+
params = FlightSearchParams(
|
|
131
|
+
from_airport="DUB",
|
|
132
|
+
from_date=datetime(2025, 8, 15),
|
|
133
|
+
to_date=datetime(2025, 8, 20),
|
|
134
|
+
to_airport="BCN",
|
|
135
|
+
max_price=150,
|
|
136
|
+
departure_time_from="08:00",
|
|
137
|
+
departure_time_to="18:00"
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### ReturnFlightSearchParams Class
|
|
142
|
+
|
|
143
|
+
Extended parameters for return flight searches.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
ReturnFlightSearchParams(
|
|
147
|
+
# All FlightSearchParams fields plus:
|
|
148
|
+
return_date_from: datetime,
|
|
149
|
+
return_date_to: datetime,
|
|
150
|
+
inbound_departure_time_from: Optional[str] = "00:00",
|
|
151
|
+
inbound_departure_time_to: Optional[str] = "23:59"
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Data Models
|
|
156
|
+
|
|
157
|
+
### Flight
|
|
158
|
+
|
|
159
|
+
Represents a single flight.
|
|
160
|
+
|
|
161
|
+
**Attributes:**
|
|
162
|
+
|
|
163
|
+
- `departure_airport` (Airport): Departure airport information
|
|
164
|
+
- `arrival_airport` (Airport): Arrival airport information
|
|
165
|
+
- `departure_date` (datetime): Departure date and time
|
|
166
|
+
- `arrival_date` (datetime): Arrival date and time
|
|
167
|
+
- `price` (float): Flight price
|
|
168
|
+
- `currency` (str): Price currency
|
|
169
|
+
- `flight_key` (str): Unique flight identifier
|
|
170
|
+
- `flight_number` (str): Flight number
|
|
171
|
+
- `previous_price` (Optional[str | float]): Previous price if available
|
|
172
|
+
|
|
173
|
+
### Airport
|
|
174
|
+
|
|
175
|
+
Represents airport information.
|
|
176
|
+
|
|
177
|
+
**Attributes:**
|
|
178
|
+
|
|
179
|
+
- `country_name` (str): Country name
|
|
180
|
+
- `iata_code` (str): IATA airport code
|
|
181
|
+
- `name` (str): Airport name
|
|
182
|
+
- `seo_name` (str): SEO-friendly name
|
|
183
|
+
- `city_name` (str): City name
|
|
184
|
+
- `city_code` (str): City code
|
|
185
|
+
- `city_country_code` (str): Country code
|
|
186
|
+
|
|
187
|
+
### ReturnFlight
|
|
188
|
+
|
|
189
|
+
Represents a return flight booking.
|
|
190
|
+
|
|
191
|
+
**Attributes:**
|
|
192
|
+
|
|
193
|
+
- `outbound` (Flight): Outbound flight
|
|
194
|
+
- `inbound` (Flight): Return flight
|
|
195
|
+
- `summary_price` (float): Total price for both flights
|
|
196
|
+
- `summary_currency` (str): Currency for total price
|
|
197
|
+
- `previous_price` (str | float): Previous total price if available
|
|
198
|
+
|
|
199
|
+
## Examples
|
|
200
|
+
|
|
201
|
+
### Search by Country
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
# Search flights to any airport in Spain
|
|
205
|
+
params = FlightSearchParams(
|
|
206
|
+
from_airport="DUB",
|
|
207
|
+
destination_country="ES",
|
|
208
|
+
from_date=datetime(2025, 9, 1),
|
|
209
|
+
to_date=datetime(2025, 9, 7)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
flights = client.get_oneways(params)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Filter by Time and Price
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
# Morning flights under €100
|
|
219
|
+
params = FlightSearchParams(
|
|
220
|
+
from_airport="STN", # London Stansted
|
|
221
|
+
to_airport="DUB", # Dublin
|
|
222
|
+
from_date=datetime(2025, 8, 1),
|
|
223
|
+
to_date=datetime(2025, 8, 5),
|
|
224
|
+
max_price=100,
|
|
225
|
+
departure_time_from="06:00",
|
|
226
|
+
departure_time_to="12:00"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
flights = client.get_oneways(params)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Error Handling
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
from flyan import RyanairException
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
flights = client.get_oneways(params)
|
|
239
|
+
if not flights:
|
|
240
|
+
print("No flights found for the given criteria")
|
|
241
|
+
except RyanairException as e:
|
|
242
|
+
print(f"Ryanair API error: {e}")
|
|
243
|
+
except Exception as e:
|
|
244
|
+
print(f"Unexpected error: {e}")
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Supported Airports
|
|
248
|
+
|
|
249
|
+
The SDK supports all airports in Ryanair's network. Airport codes must be valid 3-letter IATA codes. You can find the complete list of supported airports in the `stations.json` file.
|
|
250
|
+
|
|
251
|
+
Popular airports include:
|
|
252
|
+
|
|
253
|
+
- **DUB** - Dublin
|
|
254
|
+
- **STN** - London Stansted
|
|
255
|
+
- **BCN** - Barcelona
|
|
256
|
+
- **MAD** - Madrid
|
|
257
|
+
- **FCO** - Rome Fiumicino
|
|
258
|
+
- **BRU** - Brussels
|
|
259
|
+
- **AMS** - Amsterdam
|
|
260
|
+
|
|
261
|
+
## Supported Currencies
|
|
262
|
+
|
|
263
|
+
The SDK supports multiple currencies. Some popular ones include:
|
|
264
|
+
|
|
265
|
+
- **EUR** - Euro
|
|
266
|
+
- **USD** - US Dollar
|
|
267
|
+
- **GBP** - British Pound
|
|
268
|
+
- **CHF** - Swiss Franc
|
|
269
|
+
|
|
270
|
+
See `currencies.json` for the complete list.
|
|
271
|
+
|
|
272
|
+
## Rate Limiting
|
|
273
|
+
|
|
274
|
+
The SDK includes automatic retry logic with exponential backoff to handle rate limiting and temporary API issues. It will retry failed requests up to 5 times before giving up.
|
|
275
|
+
|
|
276
|
+
## Contributing
|
|
277
|
+
|
|
278
|
+
This is an open-source project. Contributions are welcome!
|
|
279
|
+
|
|
280
|
+
## Disclaimer
|
|
281
|
+
|
|
282
|
+
This is an unofficial API wrapper and is not affiliated with Ryanair. Use at your own risk and ensure you comply with Ryanair's terms of service.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
flyan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
flyan/misc.py,sha256=dXeEyDyOC1SAndtug3lLo8grTwtx5Ds7soCiOrKINWo,4081
|
|
3
|
+
flyan/ryanair.py,sha256=O6kOZNhQU6By6erO3bvngmMCKBvsR6CcLMe7r8pHryU,5003
|
|
4
|
+
flyan-0.1.0.dist-info/METADATA,sha256=bPwWR2pF7AtJ8kglZQW1PDXx_CI4ieN_W7gjSVvifX8,6643
|
|
5
|
+
flyan-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
+
flyan-0.1.0.dist-info/top_level.txt,sha256=yd55u-oMq-vqxqRE63e7Twfk3LO4LAvBwQ44ir2Wxbs,6
|
|
7
|
+
flyan-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flyan
|