FAA delays

Runnable examples for querying FAA ground delay programs, ground stops, closures, and airspace flow programs. See the FAA Delays reference for field definitions. Data covers US airports only (FAA ATCSCC programs).

#QuestionData used
1Are there any active FAA delays across the US right now?GET /delays/faa
2Is KJFK currently under a ground delay program?GET /delays/faa/{icao}
3Scan hub airports for any active programsGET /delays/faa/{icao}

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. Nationwide FAA delay snapshot

Question: Are there any active FAA delays across the US right now?

A single call to /delays/faa returns all current ground delay programs, ground stops, closures, and airspace flow programs. Cache for no more than 2 minutes.

import os

import requests
from rich.console import Console
from rich.panel import Panel
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"


def fetch_faa_delays() -> dict | None:
    r = requests.get(f"{BASE}/delays/faa", headers=HEADERS, timeout=(10, 15))
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()


def main() -> None:
    data = fetch_faa_delays()
    if not data:
        console.print("[red]Could not retrieve FAA delay data.[/red]")
        return

    total = data.get("total_alerts", 0)
    if total == 0:
        console.print("[green]No active FAA delays.[/green]")
        return

    console.print(Panel(f"[bold]{total}[/bold] active FAA alert(s)", style="yellow"))

    ground_delays = data.get("ground_delays", [])
    if ground_delays:
        t = Table(title="Ground Delay Programs (GDP)", show_lines=True)
        t.add_column("Airport", style="yellow")
        t.add_column("Name")
        t.add_column("Avg Delay")
        t.add_column("Max Delay")
        t.add_column("Reason")
        for row in ground_delays:
            t.add_row(
                row.get("airport", "—"),
                row.get("airport_name", "—"),
                row.get("avg_delay", "—"),
                row.get("max_delay", "—"),
                row.get("reason", "—"),
            )
        console.print(t)

    ground_stops = data.get("ground_stops", [])
    if ground_stops:
        t = Table(title="Ground Stops (GS)", show_lines=True)
        t.add_column("Airport", style="red")
        t.add_column("Name")
        t.add_column("End Time")
        t.add_column("Reason")
        for row in ground_stops:
            t.add_row(
                row.get("airport", "—"),
                row.get("airport_name", "—"),
                row.get("end_time", "—"),
                row.get("reason", "—"),
            )
        console.print(t)

    closures = data.get("closures", [])
    if closures:
        t = Table(title="Closures", show_lines=True)
        t.add_column("Airport", style="magenta")
        t.add_column("Name")
        t.add_column("Begin")
        t.add_column("Reopen")
        t.add_column("Reason")
        for row in closures:
            t.add_row(
                row.get("airport", "—"),
                row.get("airport_name", "—"),
                row.get("begin", "—"),
                row.get("reopen", "—"),
                row.get("reason", "—"),
            )
        console.print(t)


if __name__ == "__main__":
    main()

Output: Prints "No active FAA delays" when the system is clear. Otherwise shows a summary panel followed by colour-coded tables for each alert type — yellow for GDPs, red for ground stops, magenta for closures.


2. KJFK delay status

Question: Is KJFK currently under a ground delay program?

Use the airport-specific endpoint to check a single airport without downloading the full nationwide picture.

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"

AIRPORT = "KJFK"


def fetch_airport_delays(icao: str) -> dict | None:
    r = requests.get(
        f"{BASE}/delays/faa/{icao}",
        headers=HEADERS,
        timeout=(10, 15),
    )
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()


def main() -> None:
    data = fetch_airport_delays(AIRPORT)
    if not data:
        console.print(f"[red]No delay data returned for {AIRPORT}.[/red]")
        return

    total = data.get("total_alerts", 0)
    if total == 0:
        console.print(
            Panel(f"[green]No active delays at {AIRPORT}.[/green]", style="green")
        )
        return

    for gd in data.get("ground_delays", []):
        console.print(
            Panel(
                f"[bold yellow]Ground Delay Program (GDP)[/bold yellow]\n"
                f"Avg delay: {gd.get('avg_delay', '—')}  "
                f"Max delay: {gd.get('max_delay', '—')}\n"
                f"Reason: {gd.get('reason', '—')}",
                style="yellow",
                title=AIRPORT,
            )
        )

    for gs in data.get("ground_stops", []):
        console.print(
            Panel(
                f"[bold red]Ground Stop (GS)[/bold red]\n"
                f"End time: {gs.get('end_time', '—')}\n"
                f"Reason: {gs.get('reason', '—')}",
                style="red",
                title=AIRPORT,
            )
        )

    for cl in data.get("closures", []):
        console.print(
            Panel(
                f"[bold magenta]Closure[/bold magenta]\n"
                f"Begin: {cl.get('begin', '—')}  Reopen: {cl.get('reopen', '—')}\n"
                f"Reason: {cl.get('reason', '—')}",
                style="magenta",
                title=AIRPORT,
            )
        )


if __name__ == "__main__":
    main()

Output: A green "No active delays" panel when clear. Otherwise, a colour-coded panel per alert type (yellow GDP, red ground stop, magenta closure) with timing and reason details.


3. Hub airport delay dashboard

Question: Which of my hub airports have active FAA programs right now?

Fetch each hub individually and build a single dashboard table. Refresh no more often than every 2 minutes.

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"

HUB_AIRPORTS = ["KJFK", "KLAX", "KORD", "KATL"]


def fetch_airport_delays(icao: str) -> tuple[str, dict | None]:
    try:
        r = requests.get(
            f"{BASE}/delays/faa/{icao}",
            headers=HEADERS,
            timeout=(10, 15),
        )
        if r.status_code == 404:
            return icao, None
        r.raise_for_status()
        return icao, r.json()
    except Exception as exc:  # noqa: BLE001
        console.print(f"[red]Error fetching {icao}: {exc}[/red]")
        return icao, None


def summarise(data: dict | None) -> tuple[str, str, str, str]:
    """Return (active, alert_type, avg_delay, reason_snippet)."""
    if not data or data.get("total_alerts", 0) == 0:
        return "NO", "—", "—", "—"

    types = []
    avg_delay = "—"
    reason = "—"

    for gd in data.get("ground_delays", []):
        types.append("GDP")
        avg_delay = gd.get("avg_delay", "—")
        reason = (gd.get("reason") or "")[:40]

    for _ in data.get("ground_stops", []):
        types.append("GS")

    for _ in data.get("closures", []):
        types.append("Closure")

    for _ in data.get("airspace_flow_programs", []):
        types.append("AFP")

    return "YES", ", ".join(types) or "—", avg_delay, reason or "—"


def main() -> None:
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool:
        results = dict(pool.map(fetch_airport_delays, HUB_AIRPORTS))

    table = Table(title="Hub Airport FAA Delay Dashboard", show_lines=True)
    table.add_column("Airport", style="bold", no_wrap=True)
    table.add_column("Active", justify="center")
    table.add_column("Alert Type")
    table.add_column("Avg Delay", justify="center")
    table.add_column("Reason")

    for icao in HUB_AIRPORTS:
        active, alert_type, avg_delay, reason = summarise(results.get(icao))
        color = "red" if active == "YES" else "green"
        table.add_row(
            icao,
            f"[{color}]{active}[/{color}]",
            alert_type,
            avg_delay,
            reason,
        )

    console.print(table)
    console.print("[dim]Cache for 2 minutes max — FAA ATCSCC data, US only.[/dim]")


if __name__ == "__main__":
    main()

Output: A single dashboard table with one row per hub. Airports with active programs show in red; clear airports in green. Alert type, average delay, and reason snippet are included per row.