NOTAMs

Runnable examples for working with NOTAMs: active notice summaries, aerodrome filtering, and pre-flight multi-airport checks. See the NOTAMs reference for field definitions.

#QuestionData used
1Fetch all active NOTAMs for KJFK and display a summaryGET /notams/{icao}
2Filter runway-related NOTAMs (aerodrome scope) at EGLLGET /notams/{icao}
3Pre-flight NOTAM check: departure and arrival airportsGET /notams/{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. Active NOTAM summary for KJFK

Question: What NOTAMs are currently active at KJFK?

NOTAMs update frequently — cache for no more than 5 minutes. This script fetches all active notices and displays a sortable summary table.

import os
from datetime import datetime

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"


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


def format_dt(val: str | None) -> str:
    if not val:
        return "—"
    try:
        return datetime.fromisoformat(val).strftime("%m/%d %H:%Mz")
    except ValueError:
        return val[:16]


def main() -> None:
    data = fetch_notams("KJFK")
    if not data:
        console.print("[red]No NOTAM data returned for KJFK.[/red]")
        return

    notams = data.get("notams", [])
    total = data.get("total", len(notams))

    sorted_notams = sorted(
        notams,
        key=lambda n: n.get("effective") or "",
    )

    table = Table(
        title=f"KJFK NOTAMs — {total} active",
        show_lines=True,
    )
    table.add_column("NOTAM ID", style="cyan", no_wrap=True)
    table.add_column("Type", justify="center")
    table.add_column("Scope", justify="center")
    table.add_column("Effective", no_wrap=True)
    table.add_column("Expiration", no_wrap=True)
    table.add_column("Body")

    for n in sorted_notams:
        body = n.get("body") or n.get("raw", "")
        snippet = (body[:60] + "…") if len(body) > 60 else body
        table.add_row(
            n.get("notam_id") or n.get("notam_id_domestic") or "—",
            n.get("type") or "—",
            n.get("scope") or "—",
            format_dt(n.get("effective")),
            format_dt(n.get("expiration")),
            snippet,
        )

    console.print(table)


if __name__ == "__main__":
    main()

Output: A table of all active NOTAMs sorted by effective time, showing ID, type, scope, validity window, and a 60-character body preview.


2. Aerodrome NOTAMs at EGLL

Question: Which NOTAMs at EGLL are aerodrome-scoped (runway/taxiway closures, lighting outages)?

Aerodrome NOTAMs (scope == "AERODROME") directly affect ground operations. Filter to these before taxi briefings. You can also drop scope: "FIR" entries server-side with ?exclude_scope=FIR instead of filtering client-side.

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_notams(icao: str) -> dict | None:
    r = requests.get(
        f"{BASE}/notams/{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_notams("EGLL")
    if not data:
        console.print("[red]No NOTAM data returned for EGLL.[/red]")
        return

    all_notams = data.get("notams", [])
    aerodrome = [n for n in all_notams if n.get("scope") == "AERODROME"]

    if not aerodrome:
        console.print("[yellow]No aerodrome NOTAMs active at EGLL.[/yellow]")
        return

    console.print(
        Panel(
            f"[bold]{len(aerodrome)}[/bold] aerodrome-scoped NOTAM(s) at EGLL "
            f"(scope=AERODROME: runway/taxiway closures, lighting outages)",
            style="yellow",
        )
    )

    table = Table(show_lines=True)
    table.add_column("NOTAM ID", style="cyan", no_wrap=True)
    table.add_column("Type", justify="center")
    table.add_column("Q-Code")
    table.add_column("Body")

    for n in aerodrome:
        body = n.get("body") or n.get("raw", "")
        snippet = (body[:80] + "…") if len(body) > 80 else body
        table.add_row(
            n.get("notam_id") or n.get("notam_id_domestic") or "—",
            n.get("type") or "—",
            n.get("q_code") or "—",
            snippet,
        )

    console.print(table)


if __name__ == "__main__":
    main()

Output: A panel showing the aerodrome NOTAM count followed by a table with ID, type, Q-code, and truncated body. Prints "No aerodrome NOTAMs active" if none match.


3. Pre-flight NOTAM check: departure and arrival

Question: What NOTAMs are active at both my departure (KJFK) and arrival (KLAX) airports?

Fetch both airports concurrently to minimize latency. Review all NOTAMs — do not rely on automated parsing alone.

import os
import concurrent.futures

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"

DEPARTURE = "KJFK"
ARRIVAL = "KLAX"


def fetch_notams(icao: str) -> tuple[str, dict | None]:
    try:
        r = requests.get(
            f"{BASE}/notams/{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 print_airport_notams(icao: str, data: dict | None) -> None:
    if not data:
        console.print(Panel(f"[red]No data for {icao}[/red]"))
        return

    notams = data.get("notams", [])
    total = data.get("total", len(notams))

    console.print(
        Panel(
            f"[bold cyan]{icao}[/bold cyan] — {total} active NOTAM(s)",
            style="blue",
        )
    )

    for n in notams[:5]:
        body = n.get("body") or n.get("raw", "")
        notam_id = n.get("notam_id") or n.get("notam_id_domestic") or "—"
        scope = n.get("scope") or "—"
        snippet = (body[:80] + "…") if len(body) > 80 else body
        console.print(f"  [cyan]{notam_id}[/cyan] [{scope}] {snippet}")

    if total > 5:
        console.print(f"  [dim]… and {total - 5} more NOTAM(s)[/dim]")

    console.print()


def main() -> None:
    airports = [DEPARTURE, ARRIVAL]

    with concurrent.futures.ThreadPoolExecutor(max_workers=2) as pool:
        results = dict(pool.map(lambda a: fetch_notams(a), airports))

    for icao in airports:
        print_airport_notams(icao, results.get(icao))

    console.print(
        "[bold yellow]⚠ Review all NOTAMs — do not rely on automated "
        "parsing alone.[/bold yellow]"
    )


if __name__ == "__main__":
    main()

Output: Two sections — one per airport — each showing the total NOTAM count and the first 5 notices as a compact list. A reminder to review all NOTAMs manually is printed at the end.