Caching & Monitoring
METAR/TAF-Fallbacks bei 404: Fehlerbehandlung. HTTP-Fehler der Plattform und Wiederholungsversuche: Fehlerbehandlung.
Aktualität der Daten
| Datentyp | Aktualisierungsrhythmus | Hinweise |
|---|---|---|
| METAR | Alle 30 min (große Flughäfen), alle 60 min (kleinere Stationen) | SPECI-Meldungen werden bei signifikanten Änderungen der Bedingungen sofort ausgegeben |
| TAF | 4-mal täglich — 00Z, 06Z, 12Z, 18Z | Gültig für 24 oder 30 Stunden; ein TAF bleibt zwischen den Ausgaben aktuell |
| Höhenwinde (FB) | Fester Produktzeitplan der FAA | Nur USA; valid_time in der Antwort kennzeichnet die Gültigkeit der Vorhersage |
| PIREPs | Fortlaufende Meldung durch Piloten | Kurzlebig — verwenden Sie einen kurzen Rückblickzeitraum (Parameter hours) |
| AIRMET/SIGMET | Ausgabe und Aktualisierung nach FAA-Zeitplan | Clientseitig 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.
| Endpoint | Empfohlene TTL | Cache-Schlüssel |
|---|---|---|
GET /weather/metar/{icao} | 3–5 Minuten | metar:{icao} |
GET /weather/metar/{icao}?parsed=true | 3–5 Minuten | metar:{icao}:parsed |
GET /weather/taf/{icao} | 15–30 Minuten | taf:{icao} |
GET /weather/taf/{icao}?parsed=true | 15–30 Minuten | taf:{icao}:parsed |
GET /weather/winds-aloft?bbox=… | 30–60 Minuten | winds:{bbox}:{forecast}:{level} |
GET /weather/pireps?bbox=… | 5–15 Minuten | pirep:{bbox}:{hours} |
GET /weather/airsigmet?bbox=… | 10–15 Minuten | airsig:{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:
| Header | Wert |
|---|---|
X-RateLimit-Requests-Limit | Monatliches Anfragekontingent Ihres Tarifs |
X-RateLimit-Requests-Remaining | Verbleibende Anfragen in diesem Monat |
X-RateLimit-Requests-Reset | Sekunden bis zum Zurücksetzen des Kontingents |
Kontingente der Tarife:
| Tarif | Anfragen/Monat |
|---|---|
| Free | 1.000 |
| Basic | 5.000 |
| Pro | 50.000 |
| Ultra | 200.000 |
| Mega | 600.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ögertstatus = 429mehr 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)
Fehlerbehandlung
Fehlende METAR- und TAF-Daten im Produktivbetrieb behandeln – Bedeutung von 404, Fallbacks und Verweise auf die Fehlerbehandlung der Plattform.
Flughäfen
Detaillierte Flughafendaten per ICAO- oder IATA-Code – Start- und Landebahnen, Funkfrequenzen und Funknavigationsanlagen in der Nähe.