Error handling
Caching and quotas: Caching & monitoring.
Platform errors (401, 403, 422, 429, 5xx, retry/backoff, rate-limit headers): Error Handling.
Endpoint 404 samples: METAR · TAF · Winds Aloft.
When weather data is missing
Two distinct 404 cases - same HTTP status and JSON shape ({ "detail": "..." }), different meaning:
METAR 404. No weather observation station at this ICAO. Common for private strips, many military airfields, and some international airports that don't report to the ASOS/AWOS network. The airport may still exist - it just doesn't report METARs. Show Weather not available and let users proceed if appropriate.
TAF 404. The airport may have a METAR, but no TAF is issued. Smaller airports without significant instrument traffic often have METAR only. Fetch METAR normally, skip the TAF, and display current conditions only.
import requests
HEADERS = {
"X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
"X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"
def get_weather(icao: str) -> dict:
"""
Returns a dict with 'metar' and 'taf' keys.
Either can be None if data is unavailable for this airport.
"""
result = {"metar": None, "taf": None}
r = requests.get(f"{BASE}/weather/metar/{icao}", headers=HEADERS, params={"parsed": "true"}, timeout=(10, 15))
if r.status_code == 200:
result["metar"] = r.json()
elif r.status_code != 404:
r.raise_for_status()
r = requests.get(f"{BASE}/weather/taf/{icao}", headers=HEADERS, params={"parsed": "true"}, timeout=(10, 15))
if r.status_code == 200:
result["taf"] = r.json()
elif r.status_code != 404:
r.raise_for_status()
return result
# weather = get_weather("KOXB") # small airport, no TAF
# print("METAR:", "available" if weather["metar"] else "not available")
# print("TAF:", "available" if weather["taf"] else "not available")Render available METAR/TAF data and label gaps explicitly (for example, "No forecast for this airport"). A weather 404 means the product is not published for that ICAO, not a client bug.
For transient failures (429, 5xx), use the retry helper on Error Handling.
En-route endpoints (bbox)
Winds Aloft, PIREPs, and AIRMET/SIGMET use a bbox query - not a single ICAO. Error semantics differ from METAR/TAF:
| Endpoint | Empty result | 404 | Notes |
|---|---|---|---|
| Winds Aloft | - | No FB stations in bbox | US coverage only - widen bbox or check coordinates |
| PIREPs | reports: [], total: 0 | - (use 400 for bad bbox) | No recent pilot reports - not an error |
| AIRMET/SIGMET | reports: [], total: 0 | - (use 400 for bad bbox) | No active advisories - common in VFR weather |
import requests
HEADERS = {
"X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
"X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"
BBOX = "39,-80,42,-73" # example corridor
def enroute_weather(bbox: str) -> dict:
"""Fetch en-route layers. Empty lists are valid - do not treat as failures."""
out = {"winds": None, "pireps": [], "airsigmets": []}
r = requests.get(
f"{BASE}/weather/winds-aloft",
headers=HEADERS,
params={"bbox": bbox, "forecast": 12, "level": "low"},
timeout=(10, 20),
)
if r.status_code == 200:
out["winds"] = r.json()
elif r.status_code != 404:
r.raise_for_status()
r = requests.get(
f"{BASE}/weather/pireps",
headers=HEADERS,
params={"bbox": bbox, "hours": 3},
timeout=(10, 20),
)
r.raise_for_status()
out["pireps"] = r.json().get("reports") or []
r = requests.get(
f"{BASE}/weather/airsigmet",
headers=HEADERS,
params={"bbox": bbox, "type": "airmet"},
timeout=(10, 25),
)
r.raise_for_status()
out["airsigmets"] = r.json().get("reports") or []
return out
# wx = enroute_weather(BBOX)
# print("Winds stations:", wx["winds"]["total"] if wx["winds"] else "none in bbox")
# print("PIREPs:", len(wx["pireps"]))
# print("AIRMETs:", len(wx["airsigmets"]))Show explicit empty states in the UI ("No pilot reports in the last 3 h", "No active AIRMETs") instead of error banners.
Related: METAR · TAF · Winds Aloft · PIREPs · AIRMET/SIGMET · Production patterns · Airport use cases · En-route use cases