Flight briefing

Runnable examples for pre-flight and en-route briefings using the Flight Briefing endpoint. The API returns an IBM Granite-generated summary alongside structured weather and NOTAM sections.

#QuestionData used
1Generate a full briefing for KJFK → KLAX and display the summaryFlight Briefing
2Display NOTAM highlights from a briefing for EGLL → OMDBFlight Briefing

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. Full flight briefing summary

Question: Generate a JSON flight briefing for KJFK → KLAX and display the AI summary and weather conditions.

The response is structured JSON with summary, origin_briefing, and destination_briefing, or plain text with a briefing key — the script handles both. Briefing generation can take up to ~90 seconds; the script retries once on 503.

import os
import time

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_briefing(origin: str, destination: str) -> dict | None:
    params = {
        "origin": origin,
        "destination": destination,
        "include_weather": "true",
        "include_notams": "true",
        "format": "json",
    }
    for attempt in range(2):
        try:
            r = requests.get(
                f"{BASE}/briefing/flight",
                headers=HEADERS,
                params=params,
                timeout=(10, 100),
            )
        except requests.exceptions.Timeout:
            if attempt == 0:
                time.sleep(5)
                continue
            return None
        if r.status_code == 404:
            return None
        if r.status_code in (502, 503, 504):
            if attempt == 0:
                time.sleep(8)
                continue
            return None
        r.raise_for_status()
        return r.json()
    return None


def weather_snippet(airport_briefing: dict | None) -> str:
    if not airport_briefing:
        return "—"
    wx = airport_briefing.get("weather") or {}
    metar = wx.get("metar_raw")
    if metar:
        return metar[:72] + ("…" if len(metar) > 72 else "")
    taf = wx.get("taf_raw")
    if taf:
        return f"TAF only: {taf[:60]}…"
    return "No METAR/TAF in briefing"


def main() -> None:
    data = fetch_briefing("KJFK", "KLAX")
    if not data:
        console.print("[yellow]No briefing returned.[/]")
        return

    if "briefing" in data and "summary" not in data:
        console.print(Panel(data["briefing"], title="Flight Briefing (text)"))
        return

    summary = data.get("summary")
    if summary:
        console.print(Panel(summary, title="Briefing Summary (AI-generated — IBM Granite)"))

    table = Table(title="Weather (METAR excerpt)")
    table.add_column("Airport", style="cyan")
    table.add_column("Observation")

    origin = data.get("origin_briefing")
    destination = data.get("destination_briefing")
    table.add_row(f"Departure ({data.get('origin', 'KJFK')})", weather_snippet(origin))
    table.add_row(f"Arrival ({data.get('destination', 'KLAX')})", weather_snippet(destination))
    console.print(table)

    restrictions = data.get("critical_restrictions") or []
    if restrictions:
        console.print(Panel("\n".join(f"• {x}" for x in restrictions), title="Critical restrictions"))

    console.print(
        "\n[dim]Briefing summary is AI-generated. Always verify critical weather "
        "conditions directly against METAR and TAF before flight.[/dim]"
    )


if __name__ == "__main__":
    main()

Output: An AI-generated summary panel, a METAR excerpt table for departure and arrival, and optional critical-restriction bullets. Falls back to plain text when the API returns a briefing string instead of structured JSON.


2. NOTAM highlights from a briefing

Question: What are the active NOTAMs at departure and arrival for the EGLL → OMDB route?

import os
import time

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_briefing(origin: str, destination: str) -> dict | None:
    params = {
        "origin": origin,
        "destination": destination,
        "include_weather": "false",
        "include_notams": "true",
        "format": "json",
    }
    for attempt in range(2):
        try:
            r = requests.get(
                f"{BASE}/briefing/flight",
                headers=HEADERS,
                params=params,
                timeout=(10, 100),
            )
        except requests.exceptions.Timeout:
            if attempt == 0:
                time.sleep(5)
                continue
            return None
        if r.status_code == 404:
            return None
        if r.status_code in (502, 503, 504):
            if attempt == 0:
                time.sleep(8)
                continue
            return None
        r.raise_for_status()
        return r.json()
    return None


def print_notam_table(label: str, notams: list[dict]) -> None:
    top = notams[:3]
    table = Table(title=f"NOTAMs — {label} (top {len(top)})")
    table.add_column("NOTAM ID", style="cyan", min_width=12)
    table.add_column("Body (truncated)")

    for notam in top:
        notam_id = notam.get("notam_id") or notam.get("id") or "—"
        body = notam.get("body") or notam.get("raw") or "—"
        body_short = body[:80] + ("…" if len(body) > 80 else "")
        table.add_row(str(notam_id), body_short)

    console.print(table)


def main() -> None:
    data = fetch_briefing("EGLL", "OMDB")
    if not data:
        console.print("[yellow]No briefing returned.[/]")
        return

    if "briefing" in data and "origin_briefing" not in data:
        console.print(Panel(data["briefing"], title="Flight Briefing (text — no structured NOTAMs)"))
        return

    origin = data.get("origin_briefing") or {}
    destination = data.get("destination_briefing") or {}
    dep_notams = origin.get("notams") or []
    arr_notams = destination.get("notams") or []

    if not dep_notams and not arr_notams:
        console.print("[yellow]No NOTAMs embedded in briefing response.[/]")
        summary = data.get("summary") or data.get("briefing")
        if summary:
            console.print(Panel(str(summary), title="Briefing summary"))
        return

    if dep_notams:
        print_notam_table(f"Departure (EGLL) — {len(dep_notams)} total", dep_notams)
    else:
        console.print("[dim]No departure NOTAMs in briefing.[/]")

    if arr_notams:
        print_notam_table(f"Arrival (OMDB) — {len(arr_notams)} total", arr_notams)
    else:
        console.print("[dim]No arrival NOTAMs in briefing.[/]")


if __name__ == "__main__":
    main()

Output: Up to three NOTAMs per airport from origin_briefing.notams and destination_briefing.notams. Falls back to the plain-text briefing field when structured JSON is unavailable.