If you’ve pulled a current conditions or forecast response from WeatherAPI.com and looked at the wind fields, you’ve seen wind_degree and wind_dir sitting next to each other. Most developers ignore one or assume they’re interchangeable. They’re not — they encode different things — and how they diverge matters before you build display logic or any directional filtering on top of them.
What each field actually contains
wind_degree is a meteorological bearing, 0–360, measured clockwise from true north. 0 (and 360) is north, 90 is east, 180 is south, 270 is west. This is the raw numeric direction from the underlying model or METAR observation — no rounding to a compass label, no discretisation into sectors.
wind_dir is a 16-point compass label — N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW — derived by bucketing the degree value into 22.5° sectors. A wind_degree of 248 gives you WSW. So does 247. A value right at 247.5 sits on the boundary between W and WSW depending on how the bucketing rounds.
That boundary rounding is where things get messy. If you’re making directional decisions — filtering marine routes by onshore vs offshore wind, or flagging when wind crosses a compass bearing threshold — don’t key off wind_dir. Use wind_degree and do the math yourself. The label is useful for display; it’s not reliable for logic.
Where “from” vs “toward” bites people
Meteorological convention is that wind direction describes where the wind is coming from, not where it’s going. A wind_degree of 270 means the wind is blowing from the west — so it’s actually moving eastward. This is the convention used in METARs, GFS, HRRR, NAM, and every other standard output we ingest. It’s completely backwards from how most people instinctively visualise it, and backwards from how you’d normally draw an arrow on a map.
If you render a wind arrow pointing in the direction of wind_degree, your arrow points the wrong way. Rotate it 180°. In vector terms, if you decompose into u/v components for any physics calculation:
// wind_degree is the FROM direction
// u (east-west component) and v (north-south component)
// positive u = eastward flow, positive v = northward flow
const degRad = (wind_degree * Math.PI) / 180;
const u = -wind_speed_mps * Math.sin(degRad); // negative because FROM, not TO
const v = -wind_speed_mps * Math.cos(degRad);
Getting the sign wrong here produces results that look plausible until someone notices the arrows pointing into the wind instead of with it. The kind of bug that only surfaces once someone reports it from the field.
The case where wind_degree is genuinely ambiguous: calm conditions
When wind speed is zero or near-zero, the degree field has no meaningful value. METAR encodes calm wind as 00000KT — speed zero, direction indeterminate. Models do the same. In those conditions, wind_degree might return 0 (which looks like a north wind), or it might carry forward the last non-calm reading, depending on the source data. You can’t distinguish a genuine north wind from calm-direction-undefined purely from the degree field.
Check speed before doing anything with wind_degree:
if (wind_kph < 3) {
// treat as calm — direction is not meaningful
displayDirection = "Calm";
} else {
displayDirection = wind_dir; // or derive from wind_degree
}
Three km/h is roughly the threshold below which direction measurement becomes unreliable for most anemometers. WMO surface observation guidelines use 0.5 m/s (about 1.8 km/h) as the calm threshold for manual observations; automated stations often set it higher. Erring toward 3 km/h is defensible, not gospel.
Hourly forecasts vs current conditions: different source data, different precision
Current conditions are derived from METAR or nearby station data, sometimes with model blending. METAR wind directions are rounded to the nearest 10° per ICAO convention — you'll see 120, 130, 140, almost never 123 or 137. That's a reporting format choice, not a limitation of what we store.
Hourly and 3-hourly forecast wind degrees come from model grids — GFS at 0.25° resolution, HRRR at 3 km, NAM at 12 km or 3 km depending on the domain. Those are not rounded to 10° increments. So a forecast response might return wind_degree: 247 while a current conditions response for the same location and moment returns wind_degree: 250. That gap isn't disagreement between the model and observation — it's a formatting artefact of how METAR encodes direction versus how a model grid exports it.
If you're plotting current and forecast on the same chart and want visual consistency, round the forecast degree to the nearest 10° before comparing. Or use wind_dir for display and accept that adjacent 22.5° sectors are close enough for most purposes.
Detecting wind shift events
This is the genuinely hard thing to do with direction data from any API, ours included. If you're building something that needs to detect when wind rotates through a bearing — a sailing app flagging a tack opportunity, an air quality tool detecting when prevailing flow switches from a clean sector to an industrial one — naive degree subtraction breaks at the 0°/360° wrap.
The less obvious problem: hourly model output is a snapshot at each hour, not the exact timing of a shift. A cold front passage can produce a 90° direction change in under 30 minutes, but if it falls between the 14:00 and 15:00 model steps, the data shows one direction at one hour and another the next, with no indication of when within that window the shift happened. For most use cases that's fine. For anything safety-critical — offshore operations, aviation — treat forecast wind direction near frontal boundaries with appropriate caution.
Circular difference for detecting shifts:
function windShiftDeg(from_deg, to_deg) {
let diff = ((to_deg - from_deg) + 360) % 360;
if (diff > 180) diff -= 360; // normalise to -180..+180
return diff; // positive = clockwise rotation (veering), negative = anticlockwise (backing)
}
Meteorologists use "veering" for clockwise direction change and "backing" for anticlockwise. If that distinction matters to your use case, this function gives you the sign you need.
A note on gust direction
The API returns gust_kph and gust_mph but no separate gust direction. This matches how METAR and model output work — gusts are reported with the same direction as the sustained wind at that observation time. In practice, gusts don't always come from exactly the same direction as the mean flow, especially in convective conditions, but neither METAR nor model grids give you per-gust direction at the individual observation level.
The field worth sanity-checking is wind_degree against wind_kph together. If speed is non-trivial (above roughly 5 km/h) and the degree looks implausible for the location or season — east winds in a region where they almost never occur — it's worth pulling the underlying station data to verify. A station with a stuck wind vane will report a fixed direction at realistic speeds, and composite scoring doesn't always catch it cleanly.
Before shipping anything that makes real decisions from wind direction, test it against a calm-condition response and a direction sitting right on a compass sector boundary. Those two cases will expose edge cases in your own code faster than anything else.
