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 safety: the plaintext key is shown once at generation time. We only store its SHA-256 hash — we cannot recover a lost key, only revoke it and issue a new one.
Key lifecycle: a key is valid for 12 months from creation, after which it returns 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.

PlanDaily requestsEndpoints
free300METAR (single + batch), station, usage
pro — €7.99/mo50,000All endpoints
business — €15.99/mo250,000All + priority support
enterprise — €37.99/mo500,000All + ~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.

StatusCodeMeaning
401NO_KEYX-API-Key header missing
401BAD_KEYKey not recognized
401REVOKEDKey revoked by admin or self-service (also on any plan change)
401KEY_EXPIREDKey older than 12 months — generate a new one
400BAD_REQUESTMalformed parameters (e.g. bad date format)
400RANGE_TOO_LARGEHistory range exceeds 60 days
403FEATURE_NOT_AVAILABLEEndpoint requires a higher plan (returned by the edge tier gate; the origin may also return TIER_INSUFFICIENT)
404NOT_FOUNDNo current METAR published for that airport, or unknown ICAO
429QUOTA_EXCEEDEDDaily request cap reached — retry after reset_iso (00:00 UTC), or upgrade and regenerate your key for instant headroom
502UPSTREAM_ERRORUpstream source (NOAA/Iowa) failed; retry with backoff
5xxInternal 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}

GET /v1/metar/{icao} free+

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 (T0278024627.8/24.6), otherwise the whole degrees of the temperature group as a decimal number (17/0917.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=…

GET /v1/metar?icao=A,B,C free+

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}

GET /v1/station/{icao} free+

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}

GET /v1/taf/{icao} pro+

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

GET /v1/sigmet?near=LIRF&radius=100&hazard=TS pro+

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 (status set) are never returned by a proximity query. Requires radius.
radius
number Radius in nautical miles (1–1000) around near. Requires near.
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 (ICE also returns SEV 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

GET /v1/metar/{icao}/history?from=YYYY-MM-DD&to=YYYY-MM-DD pro+

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

GET /v1/usage all plans · free of quota

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