Save up to 17% with yearly billing

See pricing

Отслеживание в реальном времени

Готовые к запуску примеры запросов текущих позиций воздушных судов и статистики потока данных. Параметры фильтрации и структура ответа описаны в полном справочнике Отслеживание ВС по ADS-B.

#ВопросИспользуемые данные
1Сколько воздушных судов сейчас в воздухе и какова средняя высота?/adsb/aircraft/statistics
2Какие воздушные суда сейчас летят в воздушном пространстве вокруг KJFK?/adsb/aircraft
3Как распределены воздушные суда по диапазонам высот в ограничивающем прямоугольнике (bbox) над Европой?/adsb/aircraft

Требования

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

Установка

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

pip install requests rich

1. Статистика воздушного пространства в реальном времени

Вопрос: Сколько воздушных судов отслеживается прямо сейчас, сколько из них в воздухе и какова средняя высота?

Эндпоинт статистики агрегирует количество по всему потоку данных — параметры не требуются.

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_statistics() -> dict:
    r = requests.get(
        f"{BASE}/adsb/aircraft/statistics",
        headers=HEADERS,
        timeout=(10, 15),
    )
    r.raise_for_status()
    return r.json()


def main() -> None:
    stats = fetch_statistics()
    alt = stats.get("altitude_stats") or {}

    avg_ft = alt.get("avg_altitude")
    max_ft = alt.get("max_altitude")
    avg_fl = round(avg_ft / 100) if avg_ft is not None else None
    max_fl = round(max_ft / 100) if max_ft is not None else None

    lines = [
        f"[bold]Total tracked:[/bold]    {stats.get('total_aircraft', 0):,}",
        f"[bold]Airborne:[/bold]         {stats.get('airborne', 0):,}",
        f"[bold]On ground:[/bold]        {stats.get('on_ground', 0):,}",
    ]
    if avg_ft is not None:
        lines.append(f"[bold]Avg altitude:[/bold]     {avg_ft:,.0f} ft  (FL{avg_fl:03d})")
    if max_ft is not None:
        lines.append(f"[bold]Max altitude:[/bold]     {max_ft:,.0f} ft  (FL{max_fl:03d})")

    console.print(Panel("\n".join(lines), title="Live ADS-B Statistics", border_style="cyan"))
    console.print(
        "[dim]ADS-B data refreshes every few seconds — do not cache these results.[/dim]"
    )


if __name__ == "__main__":
    main()

Результат: Панель с общим числом отслеживаемых ВС, числом ВС в воздухе и на земле, средней высотой в ft и FL и максимальной высотой.

Примечание: данные ADS-B быстро устаревают — поток обновляется каждые несколько секунд. Не кэшируйте ответы; в сценариях реального времени всегда запрашивайте свежие данные.


2. Воздушные суда в воздушном пространстве вокруг KJFK

Вопрос: Какие воздушные суда сейчас находятся в воздухе в радиусе 80 km от международного аэропорта имени Джона Ф. Кеннеди?

Отфильтруйте по lat, lon и radius, затем исключите ВС на земле, чтобы видеть только находящиеся в воздухе. Результаты отсортированы по убыванию высоты.

import os

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"

# KJFK coordinates
LAT = 40.6413
LON = -73.7781
RADIUS_KM = 80
LIMIT = 20


def fetch_aircraft_near(lat: float, lon: float, radius: int, limit: int) -> list[dict]:
    r = requests.get(
        f"{BASE}/adsb/aircraft",
        headers=HEADERS,
        params={"lat": lat, "lon": lon, "radius": radius, "limit": limit},
        timeout=(10, 15),
    )
    r.raise_for_status()
    return r.json().get("aircraft", [])


def main() -> None:
    aircraft = fetch_aircraft_near(LAT, LON, RADIUS_KM, LIMIT)

    airborne = [
        a for a in aircraft
        if not a.get("is_on_ground")
    ]
    airborne.sort(key=lambda a: a.get("altitude") or 0, reverse=True)

    table = Table(
        title=f"Airborne Aircraft within {RADIUS_KM} km of KJFK  ({len(airborne)} shown)",
        border_style="cyan",
    )
    table.add_column("Callsign")
    table.add_column("ICAO24")
    table.add_column("Altitude (ft)", justify="right")
    table.add_column("Speed (kt)", justify="right")
    table.add_column("Track (°)", justify="right")
    table.add_column("Type")

    for a in airborne:
        alt = f"{int(a['altitude']):,}" if a.get("altitude") is not None else "—"
        spd = str(int(a["ground_speed"])) if a.get("ground_speed") is not None else "—"
        trk = f"{a['track']:.0f}" if a.get("track") is not None else "—"
        table.add_row(
            a.get("callsign") or "—",
            a.get("icao24") or "—",
            alt,
            spd,
            trk,
            a.get("aircraft_type") or "—",
        )

    console.print(table)
    console.print("[dim]On-ground aircraft excluded. ADS-B data refreshes every few seconds.[/dim]")


if __name__ == "__main__":
    main()

Результат: Таблица воздушных судов в воздухе, отсортированная по убыванию высоты: Позывной | ICAO24 | Высота (ft) | Скорость (kt) | Трек (°) | Тип.


3. Распределение по диапазонам высот в ограничивающем прямоугольнике над Европой

Вопрос: Как сейчас распределены воздушные суда по диапазонам высот в коридоре Париж — Франкфурт?

Запросите ограничивающий прямоугольник через bbox=lat1,lon1,lat2,lon2, затем отнесите каждое ВС к категории Ground / Low / Medium / High. Значения высоты None обрабатываются с помощью флага is_on_ground.

import os
from collections import defaultdict

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"

# Roughly Paris–Frankfurt bounding box
BBOX = "48,2,52,8"


def fetch_bbox(bbox: str) -> list[dict]:
    r = requests.get(
        f"{BASE}/adsb/aircraft",
        headers=HEADERS,
        params={"bbox": bbox, "limit": 200},
        timeout=(10, 15),
    )
    r.raise_for_status()
    return r.json().get("aircraft", [])


def classify(aircraft: dict) -> str | None:
    alt = aircraft.get("altitude")
    if alt is None:
        return "Ground" if aircraft.get("is_on_ground") else None
    if alt < 100:
        return "Ground"
    if alt < 10_000:
        return "Low  (<10,000 ft)"
    if alt <= 25_000:
        return "Medium  (10,000–25,000 ft)"
    return "High  (>25,000 ft)"


def main() -> None:
    aircraft = fetch_bbox(BBOX)

    bands: dict[str, list[str]] = defaultdict(list)
    for a in aircraft:
        band = classify(a)
        if band is None:
            continue
        callsign = a.get("callsign") or a.get("icao24") or "?"
        bands[band].append(callsign)

    order = ["Ground", "Low  (<10,000 ft)", "Medium  (10,000–25,000 ft)", "High  (>25,000 ft)"]

    table = Table(title=f"Altitude Distribution — bbox {BBOX}", border_style="cyan")
    table.add_column("Band", style="bold")
    table.add_column("Count", justify="right")
    table.add_column("Example callsigns")

    for band in order:
        entries = bands.get(band, [])
        examples = ", ".join(entries[:3]) if entries else "—"
        table.add_row(band, str(len(entries)), examples)

    console.print(table)
    console.print(
        f"[dim]Total aircraft returned: {len(aircraft)}. "
        "Aircraft with unknown altitude and not on_ground are excluded.[/dim]"
    )


if __name__ == "__main__":
    main()

Результат: Таблица с четырьмя диапазонами высот, количеством ВС в каждом и не более чем тремя примерами позывных для каждого диапазона.