Save up to 17% with yearly billing

See pricing

NOTAM

Готовые к запуску примеры работы с NOTAM: сводки действующих извещений, фильтрация по аэродрому и предполётная проверка нескольких аэропортов. Описание полей см. в справочнике NOTAM.

#ВопросИспользуемые данные
1Получить все действующие NOTAM для KJFK и вывести сводкуGET /notams/{icao}
2Отфильтровать NOTAM, связанные с ВПП (область действия — аэродром), в EGLLGET /notams/{icao}
3Предполётная проверка NOTAM: аэропорты вылета и прилётаGET /notams/{icao}

Требования

Авторизация
x-api-key в каждом запросе (прямая подписка). Подробнее →

Установка

Приведённые ниже скрипты используют requests для вызовов API и rich для вывода в терминал. Установите их один раз:

pip install requests rich

1. Сводка действующих NOTAM для KJFK

Вопрос: какие NOTAM сейчас действуют в KJFK?

NOTAM обновляются часто — кэшируйте их не дольше 5 минут. Этот скрипт получает все действующие извещения и выводит сводную таблицу с сортировкой.

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()

Результат: таблица всех действующих NOTAM, отсортированных по времени вступления в силу, с идентификатором, типом, областью действия, периодом действия и первыми 60 символами текста.


2. NOTAM аэродрома EGLL

Вопрос: какие NOTAM в EGLL относятся к аэродрому (закрытие ВПП и рулёжных дорожек, отказы светосигнального оборудования)?

NOTAM аэродрома (scope == "AERODROME") напрямую влияют на наземные операции. Отфильтруйте их перед брифингом по рулению. Вместо фильтрации на стороне клиента можно также исключить записи с scope: "FIR" на стороне сервера с помощью ?exclude_scope=FIR.

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()

Результат: панель с количеством NOTAM аэродрома, за которой следует таблица с идентификатором, типом, Q-кодом и сокращённым текстом. Если совпадений нет, выводится «No aerodrome NOTAMs active».


3. Предполётная проверка NOTAM: вылет и прилёт

Вопрос: какие NOTAM действуют в моих аэропортах вылета (KJFK) и прилёта (KLAX)?

Запрашивайте данные по обоим аэропортам параллельно, чтобы минимизировать задержку. Просматривайте все NOTAM — не полагайтесь только на автоматический разбор.

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()

Результат: два раздела — по одному на каждый аэропорт, — в каждом из которых показано общее количество NOTAM и первые 5 извещений в виде компактного списка. В конце выводится напоминание просмотреть все NOTAM вручную.