> built 2026-10-14 05:03 UTC from dc3185a (main) · farepy 0.1.10. Details: build_info.json

# index.html.md

<!-- generated by epythet -->

# farepy

Multi-source flight search with normalized results, caching, and batch support.

Query Google Flights, Ryanair, and more from a single Python function. Results are
normalized into a common format so you can compare prices across sources without
worrying about each API’s quirks.

## Install

```bash
pip install farepy[google_flights]   # Google Flights (recommended, broadest coverage)
pip install farepy[ryanair]          # Ryanair direct fares
pip install farepy[all]              # Everything
```

## Usage

### Search flights

```python
from farepy import search_flights

# One-way, all available sources
result = search_flights("MRS-REK", "2026-07-01")

for offer in result["offers"][:5]:
    seg = offer["outbound"]["segments"][0]
    print(
        f"{offer['price']} {offer['currency']}  {offer['airlines']}  {seg['departure_time'][:16]}"
    )
```

### Pick a source

```python
# Google Flights only (covers virtually all European LCCs)
result = search_flights("DUB-STN", "2026-07-01", sources=["google_flights"])

# Ryanair direct API (accurate fare-only pricing, includes flight numbers)
result = search_flights("DUB-STN", "2026-07-01", sources=["ryanair"])

# Both in parallel
result = search_flights("DUB-STN", "2026-07-01", sources=["google_flights", "ryanair"])
```

### Round-trip

```python
result = search_flights("DUB-STN", "2026-07-01", return_date="2026-07-08")

for offer in result["offers"][:3]:
    out = offer["outbound"]["segments"][0]
    ret = offer["inbound"]["segments"][0] if offer["inbound"] else None
    print(
        f"{offer['price']} {offer['currency']}  out={out['departure_time'][:16]}",
        end="",
    )
    if ret:
        print(f"  ret={ret['departure_time'][:16]}", end="")
    print()
```

### Filters

```python
result = search_flights(
    "MRS-REK",
    "2026-07-01",
    currency="USD",
    adults=2,
    non_stop=True,
    outbound_departure_after="06:00",
    outbound_departure_before="14:00",
)
```

### Batch search

Search multiple routes and dates in one call. Combinations run sequentially to
respect API rate limits; caching avoids re-querying repeated combinations.

```python
from farepy import batch_search

results = batch_search(
    legs=["MRS-REK", "CDG-KEF"],
    departure_dates=["2026-07-01", "2026-07-02"],
    return_dates=["2026-07-08"],
)
# Returns one SearchResult dict per combination (4 in this case)
```

Or from a text file:

```python
from farepy import batch_from_file

results = batch_from_file("""
# route        depart       return
MRS-REK  2026-07-01  2026-07-08
CDG-KEF  2026-07-15  2026-07-22
DUB-STN  2026-08-01
""")
```

### Cache

Results are cached locally for 24 hours by default (in `~/.local/share/farepy/cache/`).

```python
from farepy import list_cached_searches, get_cached_result, clear_cache

# See what's cached
for entry in list_cached_searches():
    print(
        f"{entry['origin']}-{entry['destination']}  {entry['departure_date']}  ({entry['num_offers']} offers)"
    )

# Retrieve a cached result
result = get_cached_result(entry["cache_id"])

# Clear everything
clear_cache()
```

Disable caching per-search with `use_cache=False`, or adjust the TTL:

```python
result = search_flights("MRS-REK", "2026-07-01", use_cache=False)
result = search_flights("MRS-REK", "2026-07-01", cache_ttl_hours=1)
```

### Check available sources

```python
from farepy import available_sources

for s in available_sources():
    status = "ready" if s["available"] else "not installed"
    print(f"  {s['name']}: {status}")
```

## Sources

| Source           | Coverage                         | Install                              | Notes                                              |
|------------------|----------------------------------|--------------------------------------|----------------------------------------------------|
| `google_flights` | ~All airlines via Google Flights | `pip install farepy[google_flights]` | Protobuf API, no browser needed, broadest coverage |
| `ryanair`        | Ryanair only                     | `pip install farepy[ryanair]`        | Direct fare finder API, includes flight numbers    |
| `kayak`          | ~All airlines via Kayak          | `pip install farepy[kayak]`          | Experimental, requires Playwright + Chromium       |

## Data model

Every source returns results normalized into the same structure:

```default
SearchResult
  request: SearchRequest     # what was searched
  offers: [FlightOffer]      # sorted by price
  sources_queried: [str]     # which sources responded
  sources_failed: {str: str} # source -> error message
  searched_at: str            # ISO 8601 timestamp

FlightOffer
  source: str                # "google_flights" | "ryanair" | "kayak"
  outbound: Itinerary
  inbound: Itinerary | None  # None for one-way
  price: float
  currency: str
  airlines: [str]

Itinerary
  segments: [Segment]
  duration_minutes: int | None

Segment
  departure_airport: str     # IATA code
  arrival_airport: str       # IATA code
  departure_time: str        # ISO 8601
  arrival_time: str          # ISO 8601 (empty if unavailable)
  carrier: str
  carrier_name: str | None
  flight_number: str | None
  duration_minutes: int | None
```

Results are returned as plain dicts (via `dataclasses.asdict`), so they serialize
to JSON directly.

<p class="epythet-aggregates">This documentation as a single file: <a href="farepy.md">farepy.md</a> (Markdown, for agents).</p>


# _autosummary/farepy.base.html.md

# farepy.base

Normalized data model for multi-source flight search.

### Classes

| [`FlightOffer`](_autosummary/farepy.base.html.md#farepy.base.FlightOffer)(source, outbound, price, ...[, ...])   | A single bookable flight option, normalized across all sources.   |
|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
| [`FlightSource`](_autosummary/farepy.base.html.md#farepy.base.FlightSource)(\*args, \*\*kwargs)                   | Protocol that all source adapters implement.                      |
| [`Itinerary`](_autosummary/farepy.base.html.md#farepy.base.Itinerary)(segments[, duration_minutes])            | One direction of travel (outbound or return).                     |
| [`SearchRequest`](_autosummary/farepy.base.html.md#farepy.base.SearchRequest)(origin, destination, ...[, ...])     | Normalized search parameters.                                     |
| [`SearchResult`](_autosummary/farepy.base.html.md#farepy.base.SearchResult)(request, offers, ...[, cached])       | Container for search results with metadata.                       |
| [`Segment`](_autosummary/farepy.base.html.md#farepy.base.Segment)(departure_airport, arrival_airport, ...)   | A single flight leg (one takeoff-to-landing).                     |

### *class* farepy.base.FlightOffer(source, outbound, price, currency, airlines, inbound=None, booking_url=None, raw=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A single bookable flight option, normalized across all sources.

### *class* farepy.base.FlightSource(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Protocol that all source adapters implement.

#### is_available()

Check availability. Returns (available, message).

* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

#### search(request)

Search for flights. Returns empty list on no results.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FlightOffer`](_autosummary/farepy.base.html.md#farepy.base.FlightOffer)]

### *class* farepy.base.Itinerary(segments, duration_minutes=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One direction of travel (outbound or return).

### *class* farepy.base.SearchRequest(origin, destination, departure_date, return_date=None, adults=1, currency='EUR', max_results=50, non_stop=None, outbound_departure_after=None, outbound_departure_before=None, outbound_arrival_after=None, outbound_arrival_before=None, inbound_departure_after=None, inbound_departure_before=None, inbound_arrival_after=None, inbound_arrival_before=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Normalized search parameters.

### *class* farepy.base.SearchResult(request, offers, sources_queried, sources_failed, searched_at, cached=False)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Container for search results with metadata.

### *class* farepy.base.Segment(departure_airport, arrival_airport, departure_time, arrival_time, carrier, carrier_name=None, flight_number=None, duration_minutes=None, aircraft=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A single flight leg (one takeoff-to-landing).

\*\*Times are local wall-clock times at the airport they refer to, with no
zone\*\* (`departure_time` at `departure_airport`, `arrival_time` at
`arrival_airport`), which is how airlines and every source publish them.
They are naive on purpose: attaching a zone would need an airport-to-zone
table this package does not carry. So compare or sort them only against
times at the same airport (e.g. filtering departures from one origin); a
departure and an arrival in different zones cannot be subtracted to get a
flight time – use `duration_minutes` for that. `SearchResult.searched_at`
is the one timestamp that is UTC (see [`farepy.util.now_iso()`](_autosummary/farepy.util.html.md#farepy.util.now_iso)). A source
that does not report a time leaves it as `""`.


# _autosummary/farepy.batch.html.md

# farepy.batch

Batch flight search – cartesian combinations and file parsing.

### Functions

| [`batch_from_file`](_autosummary/farepy.batch.html.md#farepy.batch.batch_from_file)(file_content, \*[, sources, ...])   | Parse a batch file and run all searches.                          |
|------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
| [`batch_search`](_autosummary/farepy.batch.html.md#farepy.batch.batch_search)(legs, departure_dates, \*[, ...])      | Run a cartesian product of legs x departure_dates x return_dates. |
| [`parse_batch_file`](_autosummary/farepy.batch.html.md#farepy.batch.parse_batch_file)(file_content)                      | Parse a batch file into search parameter dicts.                   |

### farepy.batch.batch_from_file(file_content, , sources=None, currency='EUR', adults=1, non_stop=None, max_results=50, use_cache=True, cache_dir=None, cache_ttl_hours=24)

Parse a batch file and run all searches.

* **Parameters:**
  * **file_content** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The text content of the batch file.
  * **batch_search****)** ( *(**other args same as*)
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  List of SearchResult dicts.

### farepy.batch.batch_search(legs, departure_dates, , return_dates=None, sources=None, currency='EUR', adults=1, non_stop=None, max_results=50, use_cache=True, cache_dir=None, cache_ttl_hours=24)

Run a cartesian product of legs x departure_dates x return_dates.

Each combination is searched sequentially to respect API rate limits.
Caching ensures repeated combinations are not re-queried.

* **Parameters:**
  * **legs** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – List of IATA pairs, e.g. [“MRS-REK”, “CDG-KEF”]
  * **departure_dates** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – List of YYYY-MM-DD dates
  * **return_dates** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – List of YYYY-MM-DD dates (None for one-way)
  * **sources** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Which sources to query (None = all available)
  * **currency** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – ISO 4217 currency code
  * **adults** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of adult travelers
  * **non_stop** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Direct flights only
  * **max_results** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max results per source per search
  * **use_cache** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Use cached results
  * **cache_dir** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Override cache directory
  * **cache_ttl_hours** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Cache TTL
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  List of SearchResult dicts, one per combination.

### farepy.batch.parse_batch_file(file_content)

Parse a batch file into search parameter dicts.

Format: one search per line
: ORIGIN-DEST YYYY-MM-DD [YYYY-MM-DD]

### Examples

MRS-REK 2026-04-18
MRS-REK 2026-04-18 2026-04-30
CDG-KEF 2026-05-01 2026-05-15

* **Returns:**
  leg, departure_date, return_date
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]


# _autosummary/farepy.cache.html.md

# farepy.cache

JSON file-based caching for flight search results.

### Functions

| [`cache_key`](_autosummary/farepy.cache.html.md#farepy.cache.cache_key)(request, sources)                        | Generate a deterministic cache key from search parameters.   |
|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
| [`clear_cache`](_autosummary/farepy.cache.html.md#farepy.cache.clear_cache)(\*[, cache_dir])                       | Clear all cached results.                                    |
| [`get_cached`](_autosummary/farepy.cache.html.md#farepy.cache.get_cached)(request, sources, \*[, cache_dir, ...]) | Return cached result dict if fresh, else None.               |
| [`get_cached_result`](_autosummary/farepy.cache.html.md#farepy.cache.get_cached_result)(cache_id, \*[, cache_dir])       | Get a specific cached result by its ID (filename stem).      |
| [`list_cached_searches`](_autosummary/farepy.cache.html.md#farepy.cache.list_cached_searches)(\*[, cache_dir])              | Return summaries of all cached results.                      |
| [`put_cache`](_autosummary/farepy.cache.html.md#farepy.cache.put_cache)(result, sources, \*[, cache_dir])        | Cache a SearchResult.                                        |

### farepy.cache.cache_key(request, sources)

Generate a deterministic cache key from search parameters.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### farepy.cache.clear_cache(, cache_dir=None)

Clear all cached results. Returns count of files removed.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### farepy.cache.get_cached(request, sources, , cache_dir=None, ttl_hours=24)

Return cached result dict if fresh, else None.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) | [`None`](https://docs.python.org/3/builtins/constants.html#None)

### farepy.cache.get_cached_result(cache_id, , cache_dir=None)

Get a specific cached result by its ID (filename stem).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### farepy.cache.list_cached_searches(, cache_dir=None)

Return summaries of all cached results.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### farepy.cache.put_cache(result, sources, , cache_dir=None)

Cache a SearchResult. Returns the cache filename.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)


# _autosummary/farepy.core.html.md

# farepy.core

Search orchestration – the main entry point for flight searches.

### Functions

| [`search_flights`](_autosummary/farepy.core.html.md#farepy.core.search_flights)(leg, departure_date, \*[, ...])   | Search for flights across selected sources.   |
|---------------------------------------------------------------------------------------------------|-----------------------------------------------|

### farepy.core.search_flights(leg, departure_date, , return_date=None, sources=None, currency='EUR', adults=1, non_stop=None, max_results=50, outbound_departure_after=None, outbound_departure_before=None, outbound_arrival_after=None, outbound_arrival_before=None, inbound_departure_after=None, inbound_departure_before=None, inbound_arrival_after=None, inbound_arrival_before=None, use_cache=True, cache_dir=None, cache_ttl_hours=24)

Search for flights across selected sources.

* **Parameters:**
  * **leg** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – IATA pair like “MRS-REK”
  * **departure_date** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – YYYY-MM-DD
  * **return_date** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – YYYY-MM-DD or None for one-way
  * **sources** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – List of source names (e.g. [“google_flights”, “ryanair”]).
    None means all available.
  * **currency** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – ISO 4217 currency code
  * **adults** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of adult travelers
  * **non_stop** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – If True, direct flights only
  * **max_results** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max results per source
  * **outbound_departure_after** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – HH:MM filter
  * **outbound_departure_before** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – HH:MM filter
  * **outbound_arrival_after** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – HH:MM filter
  * **outbound_arrival_before** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – HH:MM filter
  * **inbound_departure_after** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – HH:MM filter
  * **inbound_departure_before** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – HH:MM filter
  * **inbound_arrival_after** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – HH:MM filter
  * **inbound_arrival_before** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – HH:MM filter
  * **use_cache** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether to use cached results
  * **cache_dir** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Override default cache directory
  * **cache_ttl_hours** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Cache time-to-live in hours
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  SearchResult as a dict.


# _autosummary/farepy.html.md

# farepy

farepy – Multi-source flight search with normalized results, caching, and batch support.

### Modules

| [`base`](_autosummary/farepy.base.html.md#module-farepy.base)       | Normalized data model for multi-source flight search.             |
|--------------------------------------------------------------------------------|-------------------------------------------------------------------|
| [`batch`](_autosummary/farepy.batch.html.md#module-farepy.batch)     | Batch flight search -- cartesian combinations and file parsing.   |
| [`cache`](_autosummary/farepy.cache.html.md#module-farepy.cache)     | JSON file-based caching for flight search results.                |
| [`core`](_autosummary/farepy.core.html.md#module-farepy.core)       | Search orchestration -- the main entry point for flight searches. |
| [`sources`](_autosummary/farepy.sources.html.md#module-farepy.sources) | Flight data source registry.                                      |
| [`util`](_autosummary/farepy.util.html.md#module-farepy.util)       | Internal utilities for farepy.                                    |


# _autosummary/farepy.sources.google_flights_source.html.md

# farepy.sources.google_flights_source

Google Flights source via the fast-flights library.

Uses reverse-engineered Protobuf encoding to query Google Flights directly
via HTTP – no browser automation needed. Covers virtually all European LCCs
including Ryanair, easyJet, Wizz Air, Vueling, Transavia, and more.

Install: pip install fast-flights

### Classes

| `GoogleFlightsSource`(\*\*_kwargs)   |    |
|--------------------------------------|----|


# _autosummary/farepy.sources.html.md

# farepy.sources

Flight data source registry.

### Functions

| [`available_sources`](_autosummary/farepy.sources.html.md#farepy.sources.available_sources)(\*\*kwargs)   | Return status of all sources.                            |
|----------------------------------------------------------------------------------|----------------------------------------------------------|
| [`make_source`](_autosummary/farepy.sources.html.md#farepy.sources.make_source)(name, \*\*kwargs)   | Create a source instance by name, passing config kwargs. |

### farepy.sources.available_sources(\*\*kwargs)

Return status of all sources.

Each dict has: name, available (bool), message (str).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### farepy.sources.make_source(name, \*\*kwargs)

Create a source instance by name, passing config kwargs.

### Modules

| [`google_flights_source`](_autosummary/farepy.sources.google_flights_source.html.md#module-farepy.sources.google_flights_source)   | Google Flights source via the fast-flights library.              |
|----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|
| [`kayak_source`](_autosummary/farepy.sources.kayak_source.html.md#module-farepy.sources.kayak_source)                     | Kayak flight search adapter using playwright browser automation. |
| [`ryanair_source`](_autosummary/farepy.sources.ryanair_source.html.md#module-farepy.sources.ryanair_source)                 | Ryanair source via the ryanair-py library.                       |


# _autosummary/farepy.sources.kayak_source.html.md

# farepy.sources.kayak_source

Kayak flight search adapter using playwright browser automation.

### Module Attributes

| [`MISSING_BROWSER`](_autosummary/farepy.sources.kayak_source.html.md#farepy.sources.kayak_source.MISSING_BROWSER)   | What playwright's launch error says when the browser was never installed.   |
|--------------------------------------------------------------------|-----------------------------------------------------------------------------|

### Classes

| `KayakSource`(\*[, timeout])   |    |
|--------------------------------|----|

### farepy.sources.kayak_source.MISSING_BROWSER *= "Executable doesn't exist"*

What playwright’s launch error says when the browser was never installed.


# _autosummary/farepy.sources.ryanair_source.html.md

# farepy.sources.ryanair_source

Ryanair source via the ryanair-py library.

Queries Ryanair’s fare finder API directly (no browser automation).
Returns cheapest fares per route with flight numbers and departure times.
Arrival times are not available from the fare finder endpoint.

Install: pip install ryanair-py

### Classes

| `RyanairSource`(\*\*_kwargs)   |    |
|--------------------------------|----|


# _autosummary/farepy.util.html.md

# farepy.util

Internal utilities for farepy.

### Functions

| [`check_api_key`](_autosummary/farepy.util.html.md#farepy.util.check_api_key)(env_var, \*, service_name, ...)   | Check for an API key, returning (value, message).             |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
| [`extract_time`](_autosummary/farepy.util.html.md#farepy.util.extract_time)(iso_datetime)                      | Extract HH:MM from an ISO 8601 datetime string.               |
| [`now_iso`](_autosummary/farepy.util.html.md#farepy.util.now_iso)()                                       | Return current UTC time as ISO 8601 string.                   |
| [`parse_iso_duration`](_autosummary/farepy.util.html.md#farepy.util.parse_iso_duration)(duration)                    | Parse ISO 8601 duration string to minutes.                    |
| [`parse_leg`](_autosummary/farepy.util.html.md#farepy.util.parse_leg)(leg)                                  | Parse a leg string like 'MRS-REK' into (origin, destination). |
| [`reformat_date`](_autosummary/farepy.util.html.md#farepy.util.reformat_date)(date_str, \*[, to_kiwi])          | Convert between date formats.                                 |
| [`time_in_range`](_autosummary/farepy.util.html.md#farepy.util.time_in_range)(time_str, \*[, after, before])    | Check if a HH:MM time falls within the given range.           |

### farepy.util.check_api_key(env_var, , service_name, signup_url, explicit_value=None)

Check for an API key, returning (value, message).

Checks explicit_value first, then the environment variable.

* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### farepy.util.extract_time(iso_datetime)

Extract HH:MM from an ISO 8601 datetime string.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

```pycon
>>> extract_time('2026-04-18T06:30:00')
'06:30'
```

### farepy.util.now_iso()

Return current UTC time as ISO 8601 string.

The format is fixed: second precision with a literal trailing `Z`, never
a numeric offset. Callers stamp it onto `SearchResult.searched_at` and
the cache parses it back, so the shape must not drift – and the clock call
behind it must not be a deprecated one.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

```pycon
>>> import re, warnings
>>> with warnings.catch_warnings():
...     warnings.simplefilter("error", DeprecationWarning)
...     s = now_iso()
>>> bool(re.fullmatch(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", s))
True
```

### farepy.util.parse_iso_duration(duration)

Parse ISO 8601 duration string to minutes.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int) | [`None`](https://docs.python.org/3/builtins/constants.html#None)

```pycon
>>> parse_iso_duration('PT2H30M')
150
>>> parse_iso_duration('PT45M')
45
>>> parse_iso_duration('PT1H')
60
```

### farepy.util.parse_leg(leg)

Parse a leg string like ‘MRS-REK’ into (origin, destination).

* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

```pycon
>>> parse_leg('MRS-REK')
('MRS', 'REK')
```

### farepy.util.reformat_date(date_str, , to_kiwi=False)

Convert between date formats.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

```pycon
>>> reformat_date('2026-04-18', to_kiwi=True)
'18/04/2026'
>>> reformat_date('18/04/2026')
'2026-04-18'
```

### farepy.util.time_in_range(time_str, , after=None, before=None)

Check if a HH:MM time falls within the given range.

An unknown time (`""`, as a source that does not report arrivals emits)
cannot be judged, so it is kept rather than filtered out: comparing `""`
with `"08:00"` used to drop every Ryanair offer under any arrival filter
(thorwhalen/farepy#6).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

```pycon
>>> time_in_range('14:30', after='08:00', before='18:00')
True
>>> time_in_range('06:00', after='08:00')
False
>>> time_in_range('', after='08:00', before='18:00')
True
```


# about-this-build.html.md

<!-- generated by epythet -->

# About this build

This documentation was built on **2026-10-14 05:03 UTC** from commit <a href="https://github.com/thorwhalen/farepy/commit/dc3185aea27a1e974c0939905cf758aa8fe5ff61"><code>dc3185a</code></a> on branch <code>main</code>, for **farepy 0.1.10** (from <code>pyproject.toml</code>).

#### WARNING
The documentation and the package may be misaligned:

- The documented version (0.1.10) is ahead of the latest release on PyPI (0.1.9): these docs describe unreleased code.

## Source

|                     |                                                                                                                                                          |
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| Commit              | <a href="https://github.com/thorwhalen/farepy/commit/dc3185aea27a1e974c0939905cf758aa8fe5ff61"><code>dc3185aea27a1e974c0939905cf758aa8fe5ff61</code></a> |
| Branch              | <code>main</code>                                                                                                                                        |
| Tags at this commit | <code>0.1.10</code>                                                                                                                                      |
| Working tree        | clean                                                                                                                                                    |
| Remote              | <code>https://github.com/thorwhalen/farepy</code>                                                                                                        |

## Continuous integration

|              |                                                                                            |
|--------------|--------------------------------------------------------------------------------------------|
| Repository   | <code>thorwhalen/farepy</code>                                                             |
| Run          | <a href="https://github.com/thorwhalen/farepy/actions/runs/38514482544">38514482544</a>    |
| Ref          | <code>refs/heads/main</code>                                                               |
| Event commit | <code>04d8d20e9b3232722bc622a009e679e14dbaf520</code> (in the history of the built commit) |

## Tools

|          |         |
|----------|---------|
| epythet  | 0.2.17  |
| Sphinx   | 9.1.0   |
| docutils | 0.22.4  |
| Python   | 3.12.15 |

## Configuration as resolved

|               |                                                                  |
|---------------|------------------------------------------------------------------|
| theme         | <code>auto</code> (Sphinx theme <code>shibuya</code>)            |
| accent        | <code>#006d64</code>                                             |
| api_generator | <code>autosummary</code>                                         |
| ignore        | <code>tests/</code>, <code>scrap/</code>, <code>examples/</code> |
| agent_outputs | <code>true</code>                                                |
| aggregates    | <code>md</code>                                                  |
| ai_artifacts  | <code>true</code>                                                |

## Package on PyPI

Latest release: <a href="https://pypi.org/project/farepy/0.1.9/">0.1.9</a>, older than the documented version (0.1.10).

## Reproduce

```bash
git clone https://github.com/thorwhalen/farepy && cd farepy
git checkout dc3185aea27a1e974c0939905cf758aa8fe5ff61
pip install "epythet==0.2.17"
epythet quickstart . --ignore tests/ scrap/ examples/
```

The same data, for machines: <a href="build_info.json"><code>build_info.json</code></a> (schema version 1).


# ai-agents.html.md

<!-- generated by epythet -->

# For AI agents

`farepy` ships artifacts for coding agents alongside its code. This page lists
them, says where each lives in the repository, and points at the
machine-readable copies of this documentation.

## Skills

Skills are folders holding a `SKILL.md` (the [Agent Skills](https://agentskills.io) format): a description that tells an agent when to use it and a body with the procedure. Install one into your agent with `gh skill` (any host: `--agent claude-code`, `copilot`, `cursor`, `codex`, `gemini`), or use the copy bundled in the wheel.

### `farepy-search`

Flight search tooling for the farepy package. Search and compare flight prices across Google Flights, Ryanair, and Kayak from Python. Use this skill when searching for flights, comparing prices across airlines, building flight price tools, batch-searching multiple routes or dates, managing cached flight results, or when the user asks about European flight prices or cheap flights.

Source: [`.claude/skills/farepy-search`](https://github.com/thorwhalen/farepy/tree/HEAD/.claude/skills/farepy-search).

### `farepy-source`

Source adapter tooling for the farepy package. Build new flight source adapters that wrap airline APIs or aggregator endpoints. Use this skill when adding a new airline or aggregator source to farepy, wrapping a flight API, extending farepy’s coverage to a new data provider, or when the user asks about how farepy sources work internally.

Source: [`.claude/skills/farepy-source`](https://github.com/thorwhalen/farepy/tree/HEAD/.claude/skills/farepy-source).

## Machine-readable documentation

This site publishes the same documentation in forms that fit an agent’s context window:

- [`llms.txt`](https://thorwhalen.github.io/farepy/llms.txt): an index of every page with a one-line description ([llms.txt](https://llmstxt.org) format)
- [`farepy.md`](https://thorwhalen.github.io/farepy/farepy.md): the whole documentation as one Markdown file
- `<page>.html.md`: a rendered Markdown twin of every page, advertised from each page’s `<head>` with `<link rel="alternate" type="text/markdown">`
- [`objects.inv`](https://thorwhalen.github.io/farepy/objects.inv): the Sphinx inventory: a symbol-to-URL index (`sphobjinv convert plain objects.inv -`)


# api.html.md

# API reference

| [`farepy`](_autosummary/farepy.html.md#module-farepy)   | farepy -- Multi-source flight search with normalized results, caching, and batch support.   |
|-------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|


