Save up to 17% with yearly billing

See pricing

Caching & Monitoring

METAR/TAF-Fallbacks bei 404: Fehlerbehandlung. HTTP-Fehler der Plattform und Wiederholungsversuche: Fehlerbehandlung.

Aktualität der Daten

DatentypAktualisierungsrhythmusHinweise
METARAlle 30 min (große Flughäfen), alle 60 min (kleinere Stationen)SPECI-Meldungen werden bei signifikanten Änderungen der Bedingungen sofort ausgegeben
TAF4-mal täglich — 00Z, 06Z, 12Z, 18ZGültig für 24 oder 30 Stunden; ein TAF bleibt zwischen den Ausgaben aktuell
Höhenwinde (FB)Fester Produktzeitplan der FAANur USA; valid_time in der Antwort kennzeichnet die Gültigkeit der Vorhersage
PIREPsFortlaufende Meldung durch PilotenKurzlebig — verwenden Sie einen kurzen Rückblickzeitraum (Parameter hours)
AIRMET/SIGMETAusgabe und Aktualisierung nach FAA-ZeitplanClientseitig anhand von start_time / end_time ablaufen lassen

Das Feld timestamp in jeder Antwort gibt an, wann die API die Daten abgerufen hat — es ändert sich bei jeder Anfrage, auch wenn sich die zugrunde liegende Beobachtung nicht geändert hat. Für das Alter eines METAR verwenden Sie parsed.time (Beobachtungszeit aus dem Kopf der Meldung). Beim TAF sollten Sie parsed.start_time nicht als Maß für die Aktualität der Meldung verwenden — es markiert den Beginn des Gültigkeitszeitraums der Vorhersage. Zwischen den vier täglichen Ausgaben bleibt derselbe TAF aktuell; fragen Sie höchstens alle 15–30 Minuten oder nach den erwarteten Ausgabezeiten (00Z, 06Z, 12Z, 18Z) ab.

import requests
from datetime import datetime, timezone

HEADERS = {
    "X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"


def parse_observation_time(iso: str) -> datetime:
    return datetime.fromisoformat(iso.replace("Z", "+00:00"))


def observation_age_minutes(metar: dict) -> float:
    obs_time = parse_observation_time(metar["parsed"]["time"])
    now = datetime.now(timezone.utc)
    return (now - obs_time).total_seconds() / 60


def fetch_metar(icao: str) -> dict:
    r = requests.get(
        f"{BASE}/weather/metar/{icao}",
        headers=HEADERS,
        params={"parsed": "true"},
        timeout=(10, 15),
    )
    r.raise_for_status()
    return r.json()


# age = observation_age_minutes(data)
# if age > 90:
#     print(f"Warning: observation is {age:.0f} minutes old - station may be offline")

Liefert observation_age_minutes mehr als 90 Minuten, ist die Station wahrscheinlich offline oder meldet nur sporadisch. Sie können die Daten trotzdem anzeigen, sollten sie aber als möglicherweise veraltet kennzeichnen.

Caching

Antworten zu METAR und TAF ändern sich im Verhältnis zur Aktualisierungsrate der UI nur langsam. Speichern Sie METAR 3–5 Minuten und TAF 15–30 Minuten im Cache, statt die Daten bei jedem Rendern neu abzurufen.

EndpointEmpfohlene TTLCache-Schlüssel
GET /weather/metar/{icao}3–5 Minutenmetar:{icao}
GET /weather/metar/{icao}?parsed=true3–5 Minutenmetar:{icao}:parsed
GET /weather/taf/{icao}15–30 Minutentaf:{icao}
GET /weather/taf/{icao}?parsed=true15–30 Minutentaf:{icao}:parsed
GET /weather/winds-aloft?bbox=…30–60 Minutenwinds:{bbox}:{forecast}:{level}
GET /weather/pireps?bbox=…5–15 Minutenpirep:{bbox}:{hours}
GET /weather/airsigmet?bbox=…10–15 Minutenairsig:{bbox}:{type}
import time
import requests

HEADERS = {
    "X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"

_cache: dict[str, tuple[dict, float]] = {}


def cached_get(url: str, params: dict, ttl_seconds: int) -> dict | None:
    key = url + str(sorted(params.items()))
    cached = _cache.get(key)
    if cached and time.time() < cached[1]:
        return cached[0]

    r = requests.get(url, headers=HEADERS, params=params, timeout=(10, 15))
    if r.status_code == 404:
        return None
    r.raise_for_status()
    data = r.json()
    _cache[key] = (data, time.time() + ttl_seconds)
    return data


def get_metar(icao: str) -> dict | None:
    return cached_get(f"{BASE}/weather/metar/{icao}", {"parsed": "true"}, ttl_seconds=300)


def get_taf(icao: str) -> dict | None:
    return cached_get(f"{BASE}/weather/taf/{icao}", {"parsed": "true"}, ttl_seconds=1800)

Ersetzen Sie im Produktivbetrieb den Dict-basierten Cache durch Redis oder Memcached, damit der Cache Neustarts übersteht und von allen Instanzen gemeinsam genutzt wird.

Rate Limits

Jede Antwort enthält Rate-Limit-Header:

HeaderWert
X-RateLimit-Requests-LimitMonatliches Anfragekontingent Ihres Tarifs
X-RateLimit-Requests-RemainingVerbleibende Anfragen in diesem Monat
X-RateLimit-Requests-ResetSekunden bis zum Zurücksetzen des Kontingents

Kontingente der Tarife:

TarifAnfragen/Monat
Free1.000
Basic5.000
Pro50.000
Ultra200.000
Mega600.000
import requests

HEADERS = {
    "X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}


def check_quota() -> dict:
    r = requests.get(
        "https://skylink-api.p.rapidapi.com/weather/metar/KJFK",
        headers=HEADERS,
        timeout=(10, 15),
    )
    r.raise_for_status()

    remaining = int(r.headers.get("X-RateLimit-Requests-Remaining", 0))
    limit      = int(r.headers.get("X-RateLimit-Requests-Limit", 0))
    reset_in   = int(r.headers.get("X-RateLimit-Requests-Reset", 0))

    return {
        "remaining": remaining,
        "limit": limit,
        "reset_in": reset_in,
        "low_quota": remaining < 100,
    }


# quota = check_quota()
# print(f"Quota: {quota['remaining']}/{quota['limit']} remaining, resets in {quota['reset_in']}s")
#
# if quota["low_quota"]:
#     print("Warning: approaching quota limit - reduce polling frequency or upgrade plan")

Wenn Sie einen 429 erhalten, lautet der Antworttext {"message": "Too many requests"} (Struktur des RapidAPI-Gateways). Warten Sie per Backoff und versuchen Sie es erneut — siehe die Retry-Hilfsfunktion auf der Plattformseite zur Fehlerbehandlung.

Monitoring und Logging

Protokollieren Sie bei jeder Wetteranfrage Folgendes:

import logging
import time
import requests
from datetime import datetime, timezone

logger = logging.getLogger("weather")
HEADERS = {
    "X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"


def fetch_metar_logged(icao: str) -> dict | None:
    start = time.monotonic()
    r = requests.get(f"{BASE}/weather/metar/{icao}", headers=HEADERS, params={"parsed": "true"}, timeout=(10, 15))
    elapsed_ms = (time.monotonic() - start) * 1000

    log_data = {
        "endpoint": "metar",
        "icao": icao,
        "status": r.status_code,
        "elapsed_ms": round(elapsed_ms),
        "quota_remaining": r.headers.get("X-RateLimit-Requests-Remaining"),
    }

    if r.status_code == 200:
        data = r.json()
        if "parsed" in data:
            obs_time = datetime.fromisoformat(data["parsed"]["time"].replace("Z", "+00:00"))
            age_min = (datetime.now(timezone.utc) - obs_time).total_seconds() / 60
            log_data["observation_age_min"] = round(age_min, 1)
            log_data["flight_rules"] = data["parsed"]["flight_rules"]
        logger.info("metar_fetch", extra=log_data)
        return data
    elif r.status_code == 404:
        logger.info("metar_not_found", extra=log_data)
        return None
    else:
        logger.warning("metar_error", extra=log_data)
        r.raise_for_status()

Alarmieren bei:

  • observation_age_min > 90 — die Station ist wahrscheinlich offline oder die Aktualisierungen sind verzögert
  • status = 429 mehr als zweimal innerhalb von 5 Minuten — Ihre Abfragefrequenz ist für Ihren Tarif zu hoch
  • wiederholt elapsed_ms > 5000 — Latenzproblem auf der Upstream-Seite; erwägen Sie einen Timeout mit Fallback
  • quota_remaining < 200 — Sie haben Ihr monatliches Limit fast erreicht

Siehe auch: METAR · TAF · Höhenwinde · PIREPs · AIRMET/SIGMET · Fehlerbehandlung · Anwendungsfälle am Flughafen · Anwendungsfälle auf der Strecke (En-Route)