Block time

Runnable examples for estimating gate-to-gate block time using SkyLink's ML model. See ML Flight Time Estimation for model details, accuracy metrics, and supported aircraft types.

#QuestionData used
1Estimate block time for KJFK → KLAX on a B738GET /ml/flight-time
2Compare flight times across 4 routes from EGLLGET /ml/flight-time

Requirements

Auth
x-api-key on every request (direct subscription). Learn more →

Install

The scripts below use requests for API calls and rich for terminal output. Install once:

pip install requests rich

1. Block time estimate: KJFK → KLAX on B738

Question: What is the estimated gate-to-gate block time for a 737-800 on the KJFK–KLAX route?

The model returns an estimated time plus a min/max range derived from historical data (GradientBoosting, R²≈0.98). Wind is not modelled — actual times will vary with routing and conditions.

import os

import requests
from rich.console import Console
from rich.panel import Panel

console = Console()

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 fetch_flight_time(
    origin: str, destination: str, aircraft: str | None = None
) -> dict | None:
    params: dict[str, str] = {"from": origin, "to": destination}
    if aircraft:
        params["aircraft"] = aircraft
    r = requests.get(
        f"{BASE}/ml/flight-time",
        headers=HEADERS,
        params=params,
        timeout=(10, 15),
    )
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()


def fmt_mins(minutes: int | None) -> str:
    if minutes is None:
        return "—"
    return f"{minutes // 60}h {minutes % 60:02d}m"


def main() -> None:
    data = fetch_flight_time("KJFK", "KLAX", aircraft="B738")
    if not data:
        console.print("[red]No estimate returned.[/red]")
        return

    origin = data.get("origin", "KJFK")
    dest = data.get("destination", "KLAX")
    aircraft = data.get("aircraft_type", "B738")
    est_display = data.get("estimated_hours_display") or fmt_mins(
        data.get("estimated_minutes")
    )
    min_m = data.get("min_minutes")
    max_m = data.get("max_minutes")
    distance = data.get("distance_nm")

    range_str = f"{fmt_mins(min_m)} – {fmt_mins(max_m)}"

    body = (
        f"Route:      [bold]{origin} → {dest}[/bold]\n"
        f"Aircraft:   {aircraft}\n"
        f"Estimated:  [bold green]{est_display}[/bold green]\n"
        f"Range:      {range_str}\n"
        f"Distance:   {distance:,.1f} nm" if distance else ""
    )

    console.print(Panel(body, title="Block Time Estimate", style="blue"))
    console.print(
        "[dim]Estimate is statistical average for this route/type — "
        "actual time varies with winds and routing.[/dim]"
    )


if __name__ == "__main__":
    main()

Output: A panel showing route, aircraft type, estimated block time (display format), min–max range, and great-circle distance in nautical miles.


2. Route comparison from EGLL

Question: How do flight times from London Heathrow compare across four long-haul destinations?

Fetch all four routes concurrently and sort by estimated time ascending. Useful for schedulers identifying tight connection windows.

import os
import concurrent.futures

import requests
from rich.console import Console
from rich.table import Table

console = Console()

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"

ROUTES = [
    ("EGLL", "KJFK"),
    ("EGLL", "OMDB"),
    ("EGLL", "YSSY"),
    ("EGLL", "RJTT"),
]


def fetch_flight_time(origin: str, destination: str) -> tuple[str, str, dict | None]:
    try:
        r = requests.get(
            f"{BASE}/ml/flight-time",
            headers=HEADERS,
            params={"from": origin, "to": destination},
            timeout=(10, 15),
        )
        if r.status_code == 404:
            return origin, destination, None
        r.raise_for_status()
        return origin, destination, r.json()
    except Exception as exc:  # noqa: BLE001
        console.print(f"[red]{origin}→{destination}: {exc}[/red]")
        return origin, destination, None


def fmt_mins(minutes: int | None) -> str:
    if minutes is None:
        return "—"
    return f"{minutes // 60}h {minutes % 60:02d}m"


def main() -> None:
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool:
        results = list(pool.map(lambda r: fetch_flight_time(*r), ROUTES))

    valid = [
        (orig, dest, d)
        for orig, dest, d in results
        if d and d.get("estimated_minutes") is not None
    ]
    valid.sort(key=lambda x: x[2].get("estimated_minutes", 0))

    table = Table(
        title="EGLL Long-haul Route Comparison (model default aircraft)",
        show_lines=True,
    )
    table.add_column("Route", style="cyan", no_wrap=True)
    table.add_column("Distance (nm)", justify="right")
    table.add_column("Est. Time", justify="center", style="bold")
    table.add_column("Min", justify="center")
    table.add_column("Max", justify="center")

    for orig, dest, d in valid:
        distance = d.get("distance_nm")
        dist_str = f"{distance:,.0f}" if distance else "—"
        table.add_row(
            f"{orig} → {dest}",
            dist_str,
            fmt_mins(d.get("estimated_minutes")),
            fmt_mins(d.get("min_minutes")),
            fmt_mins(d.get("max_minutes")),
        )

    console.print(table)
    console.print(
        "[dim]Statistical estimates — wind not modelled. "
        "Routes sorted by estimated time ascending.[/dim]"
    )


if __name__ == "__main__":
    main()

Output: A table sorted shortest-to-longest showing route, distance in nautical miles, estimated time, and the historical min/max range for each city pair.