Docs · API Reference
AvioWeather API
REST API for aviation weather data. METAR, TAF, SIGMET, historic archive — sourced from NOAA AWC (current) and Iowa State ASOS (historic), fully decoded, served from a 5-minute global snapshot in eu-west-1 (Ireland).
Overview
The API is read-only, JSON over HTTPS, and authenticated with a single header. Base URL:
https://api.developer.avioweather.app
All endpoints live under /v1/. We never break v1 — additive changes only. When we do introduce breaking changes, they ship under /v2/ and v1 stays running for at least 12 months.
Authentication
Send your API key in the X-API-Key request header. Keys have the format ak_live_v2.<payload>.<signature> (signed; validated at the edge) and are generated from the developer portal after you create an account and choose a plan.
curl https://api.developer.avioweather.app/v1/metar/LIRF \ -H "X-API-Key: ak_live_v2.xxxxxxxx~pro~000.xxxxxxxx"
KEY_EXPIRED — generate a new one from the portal. The plan tier is signed into the key, so any plan change revokes every existing key immediately — upgrade, downgrade, cancellation or refund, whether you change it yourself or an admin does. After a plan change, generate a fresh key from the portal; it carries the new tier. Both revoked and expired keys are rejected at the edge with 401.
Plans & limits
Each plan has a daily request cap. When the cap is reached, calls return 429 until 00:00 UTC the next day. There is no automatic overage. Paid plans include every endpoint — they differ only by volume.
| Plan | Daily requests | Endpoints |
|---|---|---|
free | 300 | METAR (single + batch), station, usage |
pro — €7.99/mo | 50,000 | All endpoints |
business — €15.99/mo | 250,000 | All + priority support |
enterprise — €37.99/mo | 500,000 | All + ~99.9% uptime target |
Use GET /v1/usage to check remaining quota. That endpoint authenticates against your key but does not count toward the daily quota.
Hit your cap? Upgrading gives instant headroom. If you reach your daily cap and upgrade, you don't have to wait for the 00:00 UTC reset: the cap lifts as soon as you regenerate your key at the new tier (a plan change revokes your old keys, so you regenerate anyway — see Authentication). The limit then re-applies only if you exceed the new, higher cap. This holds at every step — free → pro → business → enterprise.
Errors
Errors come back as JSON with a stable code field — match on the code, not the human-readable error string.
| Status | Code | Meaning |
|---|---|---|
| 401 | NO_KEY | X-API-Key header missing |
| 401 | BAD_KEY | Key not recognized |
| 401 | REVOKED | Key revoked by admin or self-service (also on any plan change) |
| 401 | KEY_EXPIRED | Key older than 12 months — generate a new one |
| 400 | BAD_REQUEST | Malformed parameters (e.g. bad date format) |
| 400 | RANGE_TOO_LARGE | History range exceeds 60 days |
| 403 | FEATURE_NOT_AVAILABLE | Endpoint requires a higher plan (returned by the edge tier gate; the origin may also return TIER_INSUFFICIENT) |
| 404 | NOT_FOUND | No current METAR published for that airport, or unknown ICAO |
| 429 | QUOTA_EXCEEDED | Daily request cap reached — retry after reset_iso (00:00 UTC), or upgrade and regenerate your key for instant headroom |
| 502 | UPSTREAM_ERROR | Upstream source (NOAA/Iowa) failed; retry with backoff |
| 5xx | — | Internal failure; safe to retry with exponential backoff |
The 429 QUOTA_EXCEEDED response carries the reset time so you know when to retry — it's returned at the edge for every endpoint (including /v1/usage) while you're over the cap:
{
"code": "QUOTA_EXCEEDED",
"reset_iso": "2026-05-25T00:00:00Z",
"message": "Daily quota reached. Resets at 00:00 UTC; upgrade for instant headroom."
}
GET /v1/metar/{icao}
Returns the most recent METAR for the given ICAO airport, fully decoded. Served from the 5-minute pipeline snapshot (the same data the AvioWeather apps consume); falls back to a live NOAA fetch if the station is not in the current snapshot. source tells you which path answered.
Path parameters
- icao
- string Four-letter ICAO code, e.g.
LIRF. Case-insensitive.
Example response
{
"icao": "LIRF",
"name": "Roma Fiumicino",
"latitude": 41.8, "longitude": 12.24, "elevation_m": 5,
"raw": "LIRF 191420Z 22008G18KT 190V250 9999 FEW040 SCT100 17/09 Q1018 NOSIG",
"observed_at": "2026-05-19T14:20:00Z",
"report_type": "METAR", "auto": false, "corrected": false,
"flight_category": "VFR",
"wind": { "direction_deg": 220, "variable": false, "speed_kt": 8, "gust_kt": 18 },
"wind_variation": { "from_deg": 190, "to_deg": 250 },
"visibility": { "statute_miles": 6.21, "meters": 9999, "or_more": true, "ndv": false },
"min_visibility": null,
"rvr": [],
"clouds": [ { "cover": "FEW", "base_ft": 4000, "modifier": null }, { "cover": "SCT", "base_ft": 10000, "modifier": null } ],
"sky_condition": null,
"weather": [], "weather_unavailable": false, "recent_weather_unavailable": false,
"temperature_c": 17.0, "dewpoint_c": 9.0,
"temperature_c_reported": 17, "dewpoint_c_reported": 9,
"altimeter_hpa": 1018, "sea_level_pressure_hpa": null,
"trend": { "raw": "NOSIG", "nosig": true, "groups": [] },
"source": "pipeline", "age_seconds": 95
}
Each cloud layer carries a modifier: "CB" (cumulonimbus) or "TCU" (towering cumulus) when the report tags that layer convective, otherwise null — e.g. { "cover": "OVC", "base_ft": 400, "modifier": "CB" }. NOAA omits this from its structured METAR feed, so we recover it from the raw text: it is present on every METAR path (this endpoint, the batch endpoint, and history) and on TAF forecast layers.
Report identifiers. report_type is "METAR" or "SPECI" (a special report issued between routine hours because conditions changed). auto is true for a fully automatic observation (the AUTO identifier: expect sensor-limited codes such as NDV, NCD or unmeasured layers), corrected for a correction (COR) that replaces the previous report.
Visibility. The value is served in the report's native unit with the other derived: a metric report gives meters as coded (9999 is the ICAO code for "10 km or more", never a measurement), a US report gives statute_miles as coded. or_more marks the floors (9999, CAVOK, 10SM, P6SM). ndv is true when the report carries the NDV suffix (9999NDV, 0100NDV): the prevailing visibility is a single-sensor value with no directional variation available — the number is still the prevailing visibility and flight_category is computed from it. min_visibility is the minimum-visibility group a metric report adds when one sector of the aerodrome is markedly worse than the prevailing value: { "meters": 1000, "direction": "SW" } for 9999 1000SW, and { "meters": 500, "direction": null } for the direction-less form (1000 0500: the station reports the lowest visibility but cannot give its direction). It is null when the report has no such group; visibility always stays the prevailing value.
Present weather. weather lists the present-weather codes exactly as coded ("-RA", "VCSH", "+TSRA"), empty when none. An automatic station whose weather sensor has failed codes the group as //: weather is then empty and weather_unavailable is true — the two are not the same situation (no weather observed vs. no observation possible). recent_weather_unavailable is the same flag for the recent-weather group (RE//). Both are false otherwise.
Sky condition. When the report carries no cloud layer, sky_condition tells you why — the codes are not interchangeable: "SKC"/"CLR" sky clear, "NSC" no cloud of operational significance (layers above 1500 m / 5000 ft or the minimum sector altitude may exist), "NCD" no cloud detected by the automatic sensor, "CAVOK" ceiling and visibility OK. It is null whenever layers are listed.
Wind variation. wind_variation is the dddVddd group — the two extreme directions the wind varied between during the last 10 minutes (variation of 60° or more with a mean speed of 3 kt or more) — or null. wind.direction_deg stays the mean direction.
Runway visual range. rvr lists one entry per runway group, in report order (empty when none): { "runway": "25L", "meters": 50, "prefix": "M", "max_meters": null, "max_prefix": null, "trend": null, "feet": null, "max_feet": null, "raw": "R25L/M0050" }. prefix "M" means less than and "P" more than the value (the sensor's limits, e.g. M0050 = below 50 m, P2000 = above 2000 m). max_meters/max_prefix are set on the variation form (0400V0600: one-minute means ranging between the two). trend is "U" (increasing), "D" (decreasing) or "N" (no change) exactly as coded, and null when the report does not give a tendency — do not read null as "no change". North-American reports code RVR in feet: feet/max_feet then carry the native values and meters the conversion rounded down to the ICAO reporting step. A runway whose RVR could not be measured (R35/////) is still listed, with meters null and every other value null: the runway is named, the value is missing.
Trend forecast. trend is the landing forecast appended to the report (valid two hours from the observation), or null when the report has none. nosig is true for NOSIG (no significant change expected). groups lists the BECMG/TEMPO groups, each with its period ("FM1200", "TL1300", "AT1230", "FM1200 TL1300" or null for the whole two hours) and only the elements it changes — wind, visibility, weather, clouds in the same shapes as the observation, null when unchanged — plus the flags cavok, nsw (the forecast weather ends) and nsc. Example: { "type": "TEMPO", "period": null, "wind": { "direction_deg": 340, "variable": false, "speed_kt": 10, "gust_kt": 25 }, "visibility": { "statute_miles": 0.93, "meters": 1500, "or_more": false }, "weather": ["TSRA"], "clouds": null, "cavok": false, "nsw": false, "nsc": false }. A trend wind coded as a floor (140P99KT: more than 99 kt) keeps the coded number in speed_kt and adds speed_greater_than: true (gust_greater_than for a GP gust); the keys are present only when the report codes the prefix. The observation fields never include trend values.
Temperature and pressure. temperature_c and dewpoint_c are numbers with decimals: the tenths of a degree when the report carries them in its remarks (T02780246 → 27.8/24.6), otherwise the whole degrees of the temperature group as a decimal number (17/09 → 17.0/9.0). temperature_c_reported and dewpoint_c_reported are the whole degrees exactly as the report's own [M]nn/[M]nn group codes them (rounded to the nearest degree, .5 up), integers or null for a half the station did not measure (M69/, 30///). altimeter_hpa is the QNH group only (Q or A); it is null when the report has none, and never derived from the sea-level pressure of the remarks, which stays in sea_level_pressure_hpa.
Unmeasured layers. An automatic station that sees a layer but cannot range its base reports "///" in place of the height (FEW///CB), and one that cannot measure the amount either reports the all-slashes form (//////TCU). Those layers are included: base_ft is null when the base was not measured, and cover is "///" when the amount was not — e.g. { "cover": "///", "base_ft": null, "modifier": "TCU" }. Treat "///" as "layer present, extent unknown". flight_category is published only when it is certain: an unknown element (//// visibility, a BKN/// or ////// group, no cloud group at all) is evaluated at both its best and its worst case, and when the two disagree the value is "UNKNOWN" — 9999 BKN/// could be a ceiling anywhere, 0050 FG VV/// is LIFR whatever the ceilometer missed. A named convective group with no placement (//////CB) is a hazard reported in modifier, not a ceiling, and does not make the category unknown. Possible values: VFR, MVFR, IFR, LIFR, UNKNOWN. An obscured sky (VV002) is a layer with cover "VV" and base_ft the vertical visibility (200), null for VV/// (sky obscured, vertical visibility not measurable).
GET /v1/metar?icao=…
Batch METAR for up to 20 ICAOs in a single call, resolved from one snapshot read. A batch call costs 1 request against your daily quota, regardless of how many ICAOs you pass. Unknown ICAOs come back as { "icao": "XXXX", "error": "NOT_FOUND" } in the array rather than failing the whole request. The icao list is case-insensitive, order-independent, and deduplicated — LIRF,EGLL and egll, lirf, EGLL return the same result and hit the same cache entry.
Query parameters
- icao
- string Comma-separated ICAO codes. Maximum 20 per request — any codes beyond the first 20 (after dedup) are dropped, not rejected.
{ "count": 3, "metars": [ { "icao": "LIRF", … }, … ] }
GET /v1/station/{icao}
Station metadata (name, coordinates, elevation) plus the latest flight category and observation time.
{
"icao": "LIRF", "name": "Roma Fiumicino",
"latitude": 41.8, "longitude": 12.24, "elevation_m": 5,
"flight_category": "VFR", "last_observation_at": "2026-05-19T14:20:00Z",
"source": "pipeline"
}
GET /v1/taf/{icao}
Current TAF (terminal aerodrome forecast), raw plus decoded forecast periods (wind, visibility, clouds, weather, change indicator). Each period's visibility is read from that period's own group in the bulletin, in the bulletin's native unit: a metric TAF gives meters exactly as coded (9999 = 10 km or more, with or_more: true; 4800 stays 4800), a US TAF gives statute_miles as coded (P6SM = 6 or more, 3SM = 3). A period that does not restate visibility keeps the value in force for it; a TEMPO/PROB group that does not mention it has nulls. Cloud layers carry the modifier ("CB"/"TCU") forecast for that period; an obscured sky is { "cover": "VV", "base_ft": 200 } with the forecast vertical visibility as the base. weather carries "NSW" (the previously forecast weather ends) only in the change group whose own text codes it — BECMG, TEMPO, PROB or FM — never in the initial period, and never inherited into a later group that does not restate it.
{
"icao": "LIRF",
"raw": "TAF LIRF 201700Z 2018/2124 30013KT CAVOK BECMG 2022/2024 04006KT …",
"issued_at": "2026-05-20T17:00:00.000Z",
"valid_from": "2026-05-20T18:00:00Z",
"valid_to": "2026-05-22T00:00:00Z",
"forecast": [
{
"from": "2026-05-20T18:00:00Z",
"to": "2026-05-20T22:00:00Z",
"change_indicator": null,
"probability": null,
"wind": { "direction_deg": 300, "variable": false, "speed_kt": 13, "gust_kt": null },
"visibility": { "statute_miles": 6.21, "meters": 9999, "or_more": true },
"clouds": [ { "cover": "NSC", "base_ft": null, "modifier": null } ],
"weather": [ "NSW" ],
"wind_shear": null
}
]
}
GET /v1/sigmet
Active SIGMETs worldwide, decoded: the canonical hazard tag, every element of the SIGMET template (phenomenon code, observed/forecast indicator, levels, movement, intensity change, cancellation, volcano or cyclone, forecast position) and the affected area as [lat, lon] rings. With no query parameters it returns every active SIGMET (US domestic + international). All parameters below are optional and can be combined.
Query parameters
- near
- string ICAO of a reference airport. Combined with
radius, returns only SIGMETs whose area is within range of it (the point is inside one of the rings, or a boundary is within the radius). Test and exercise bulletins (statusset) are never returned by a proximity query. Requiresradius. - radius
- number Radius in nautical miles (1–1000) around
near. Requiresnear. - hazard
- string Filter by hazard tag (case-insensitive, matched on
hazard):TS,CONVECTIVE,TURB,SEV TURB,ICE,SEV ICE,MTW,TC,VA,IFR,MTN OBSCN. A partial value matches (ICEalso returnsSEV ICE).
{
"count": 1,
"near": "MMTG", "radius_nm": 100,
"sigmets": [
{
"id": "MMEX-A2", "unique_id": "MMEX-A2-20260902",
"type": "INTL_SIGMET", "fir_id": "MMEX",
"hazard": "TS", "hazard_code": "OCNL TS", "coverage": "OCNL", "intensity": null, "hail": false,
"raw_text": "WSMX31 MMMX 020624\nMMEX SIGMET A2 VALID 020623/021023 MMMX-\nMMFR MEXICO FIR OCNL TS OBS AT 0623Z WI N1459W09531 - … CB TOP ABV FL400 MOV SW 05KT NC.",
"valid_from": "2026-09-02T06:23:00Z", "valid_to": "2026-09-02T10:23:00Z",
"status": null, "operational": true,
"observed_or_forecast": "OBS", "observed_at": "0623Z", "forecast_at": null,
"flight_levels": "TOP ABV FL400",
"levels": { "base": null, "top": 40000, "base_open": false, "top_open": true, "below_only": false, "surface": false, "base_unit": null, "top_unit": "FL", "raw": "CB TOP ABV FL400" },
"movement": { "direction": "SW", "speed_kt": 5 }, "intensity_change": "NC",
"cancels": null, "volcano": null, "cyclone_name": null, "layers": null,
"coordinates": [ [14.983, -95.517], [15.95, -94.867], … ],
"polygons": [ [ [14.983, -95.517], … ] ], "polylines": null,
"centroid_lat": 15.4, "centroid_lon": -93.6, "pin_points": [ [15.4, -93.6] ],
"current_polygons": null, "forecast_polygons": null, "forecast_position": null, "forecast_position_at": null,
"content_sig": "b09a27cf", "geom_sig": "93e3ccc691cccb98"
}
]
}
Identity. id is the bulletin designator, FIR-sequence for an international SIGMET (MMEX-A2) and the issuer's id for a US one; it is stable across re-issues of the same sequence. Sequence numbers restart at 0001 UTC, so the same id can legitimately belong to two bulletins across midnight: unique_id adds the date of valid_from (MMEX-A2-20260902) and is the key to deduplicate on. content_sig is a fingerprint of the operational text (the WMO header line excluded) and geom_sig of the drawn geometry: a change in either means the bulletin was re-issued or its area changed — use them to decide whether to redraw. status is "TEST" or "EXER" for a test/exercise bulletin and null otherwise; operational is the derived boolean (false for those): never treat a non-operational bulletin as an active hazard.
Phenomenon. hazard is the short canonical tag (TS, SEV TURB, SEV ICE, VA, TC, MTW, DS, SS, GR, RDOACT CLD, and CONVECTIVE/TURB/ICE/IFR/MTN OBSCN for US bulletins); hazard_code is the full code as written (OCNL TS, FRQ TSGR, SEV ICE (FZRA), VA CLD), split into coverage (OBSC/EMBD/FRQ/SQL/ISOL/OCNL, thunderstorms only), intensity (SEV/HVY) and hail (true for TSGR/GR). observed_or_forecast is "OBS", "FCST" or "OBS AND FCST" with observed_at/forecast_at as coded ("0623Z", UTC). intensity_change is INTSF, WKN, NC or null. movement is { "direction": "SW", "speed_kt": 5 } (16-point compass; speed_kt null when the bulletin gives no speed; speed_kmh/speed_mps carry the native value when the bulletin used those units; US bulletins coded MOV FROM dddssKT add from_deg), { "stationary": true } for STNR, or null. fir_id, qualifier (the producer's qualifier field, verbatim) and qualifier_type (coverage, intensity, cyclone_name, volcano_name) are set on international bulletins.
Levels. flight_levels is the level group as written (TOP ABV FL400, SFC/FL170, FL270/420, 4000FT/FL180, BLW FL100, 2000/3000M). levels decodes it: base/top in feet (a flight level ×100, metres converted), base_unit/top_unit the unit each was coded in (FL, FT, M), surface for a base at SFC, and the open limits — top_open for TOP ABV, base_open for ABV, below_only for BLW: an open limit is a bound, not a value. null when the bulletin carries no level group.
Cancellation, volcanic ash, tropical cyclone. A cancellation message has hazard null and cancels { "sequence": "2", "valid_from": …, "valid_to": … } naming the SIGMET it withdraws. volcano (name), eruption, volcano_position { lat, lon } and va_moving_to_fir are set on volcanic-ash bulletins; no_va_expected is true when the bulletin forecasts the cloud dissolved by the end of validity. cyclone_name is set on tropical-cyclone bulletins; cyclone_unnamed is true for a cyclone coded TC NN (no name assigned).
Geometry. coordinates is the flat vertex list (historical field, kept). polygons is the list of rings actually to draw — one per zone, split at the antimeridian, densified at 1° so straight edges stay straight on a map — and polylines the open lines, each as [[lat, lon], …]; centroid_lat/centroid_lon is a pin inside the largest zone and pin_points one pin per zone. When the bulletin gives a forecast position at the end of validity, current_polygons and forecast_polygons separate the two parts (polygons holds both), forecast_position { lat, lon } is a forecast point (a cyclone centre) and forecast_position_at its time. layers is null except when one bulletin describes several clouds or areas joined by AND (several ash clouds): each entry then carries its own observed_or_forecast, observed_at/forecast_at, flight_levels, levels, movement and polygons, while the top-level fields describe the first one. A bulletin whose geometry could not be parsed keeps its text and has null geometry: it is still safety-relevant.
GET /v1/metar/{icao}/history
Every decoded METAR observed for the station across a UTC date range (max 60 days per request). The 60-day cap is per call, not the depth of the archive: history reaches back roughly 20 years — to go further back, request consecutive 60-day windows by shifting the from/to dates. Served from the durable archive; missing days are backfilled on demand from Iowa State ASOS (open data) and cached. Each observation has the same decoded shape as GET /v1/metar/{icao}, including the per-layer cloud modifier ("CB"/"TCU"/null), report_type/auto/corrected, visibility.ndv, min_visibility, sky_condition, wind_variation, rvr, weather_unavailable/recent_weather_unavailable, temperature_c_reported/dewpoint_c_reported and trend (the examples below are abbreviated).
Query parameters
- from
- string UTC start day
YYYY-MM-DD. - to
- string UTC end day
YYYY-MM-DD(inclusive).
{
"icao": "LIRF", "from": "2026-05-01", "to": "2026-05-07",
"count": 336, "sources": ["iowa"],
"observations": [
{
"icao": "LIRF", "name": "Rome/Fiumicino, RM, IT",
"latitude": 41.8, "longitude": 12.239, "elevation_m": 2,
"raw": "METAR LIRF 010020Z 27007KT CAVOK 19/16 Q1021",
"observed_at": "2026-05-01T00:20:00Z", "flight_category": "VFR",
"wind": { "direction_deg": 270, "variable": false, "speed_kt": 7, "gust_kt": null },
"visibility": { "statute_miles": 6.21, "meters": 9999, "or_more": true, "ndv": false },
"clouds": [], "weather": [],
"temperature_c": 19, "dewpoint_c": 16,
"altimeter_hpa": 1021, "sea_level_pressure_hpa": null
},
{
"icao": "LIRF", "name": "Rome/Fiumicino, RM, IT",
"latitude": 41.8, "longitude": 12.239, "elevation_m": 2,
"raw": "METAR LIRF 011220Z 20015G27KT 2000 +TSRA BKN015CB OVC025 22/20 Q1009",
"observed_at": "2026-05-01T12:20:00Z", "flight_category": "IFR",
"wind": { "direction_deg": 200, "variable": false, "speed_kt": 15, "gust_kt": 27 },
"visibility": { "statute_miles": 1.24, "meters": 2000, "or_more": false, "ndv": false },
"clouds": [ { "cover": "BKN", "base_ft": 1500, "modifier": "CB" }, { "cover": "OVC", "base_ft": 2500, "modifier": null } ],
"weather": ["+TSRA"],
"temperature_c": 22, "dewpoint_c": 20,
"altimeter_hpa": 1009, "sea_level_pressure_hpa": null
}
]
}
GET /v1/usage
Returns the remaining daily quota for the calling account (usage is metered per account across all its keys). Does not decrement the counter.
{
"tier": "pro",
"quota": 50000,
"used": 12483,
"remaining": 37517,
"reset_iso": "2026-05-21T00:00:00Z",
"period": "day"
}
Data sources & attribution
Current observations and forecasts are sourced from the NOAA Aviation Weather Center / National Weather Service (USA); historic METARs from the Iowa Environmental Mesonet (Iowa State University) ASOS archive. Both are public-domain / open-data sources. Applications redistributing this data must credit the original source — e.g. "Weather data via AvioWeather (NOAA AWC / Iowa State ASOS)". Use of the API is also subject to the Terms & Conditions and Acceptable Use Policy. Weather data is provided "as is" for situational awareness and is not a substitute for an official pre-flight briefing.
© 2026 Savaresi Aviation · billing@savaresi.aero · last updated 2026-06-06