Weather API responses typically carry two separate precipitation fields: a type (rain, snow, sleet, freezing rain) and an amount (millimeters in the period). Most developers grab the amount, ignore the type, and move on. That works fine — until a user in Edinburgh sees 4mm of precipitation labeled as rain in January and wonders why their app didn’t warn them about ice on the roads.
These two fields are computed very differently, and the difference matters if you’re building anything that makes decisions on precip data — logistics routing, outdoor scheduling, construction holds, anything temperature-sensitive.
Amount Comes from Models. Type Comes from a Different Inference Chain.
Precipitation amount is a direct model output. GFS, NAM, and HRRR all produce accumulated liquid-equivalent precipitation as a primary forecast variable — well-constrained, with decades of verification behind it, degrading predictably with forecast lead time. The number in precip_mm traces directly back to that model grid value.
Precipitation type is derived, not directly modeled. The standard method — used by NWS and baked into most model post-processing — is a temperature-profile algorithm. It looks at surface temperature, the 850 hPa level (roughly 1,500 meters), and sometimes the full atmospheric column to decide whether precipitation falling through that profile arrives as rain, snow, ice pellets, or freezing rain. The two most common approaches are the Bourgouin algorithm and simpler surface/850 hPa threshold methods. Neither actually observes what’s falling; they infer it from temperature structure.
That distinction matters most in the 0–2°C band. At 4°C, it’s almost certainly rain. At -5°C, it’s almost certainly snow. In between, the same model run can produce different answers depending on which algorithm is applied, because the temperature profile is genuinely ambiguous.
Where Type Goes Wrong
The worst-case scenario is a shallow warm layer — say, a 200-meter band of above-freezing air in an otherwise sub-zero column. Snow falls through it, partially melts, then refreezes before hitting the surface: ice pellets or freezing rain. A simple surface-temperature check (surface temp = 1°C, call it rain) misses this entirely. And it’s not a rare edge case — it’s the normal vertical structure of a significant ice event across the UK, the northern US, and Canada.
HRRR handles this better than GFS because its 3km horizontal grid can resolve the mesoscale dynamics that produce those thin warm layers. Orographic lifting and frontal structures that smear out at GFS’s 13km grid spacing show up clearly at HRRR resolution. If the rain/snow/ice distinction is safety-relevant for your use case and your users are in complex terrain or a region prone to ice storms, the model behind the type field matters.
In our GRIB2 pipeline we ingest HRRR, NAM, and GFS, and type classification draws on whichever model covers that location and time window. HRRR cuts off at 48 hours, so beyond that we fall back to GFS resolution. Worth knowing if you’re building 72-hour or 96-hour alerts on precip type.
METAR Makes Type More Reliable — When It’s Available
For current conditions, METAR beats model inference on precip type. A human observer or automated ASOS sensor reporting FZRA (freezing rain) or PL (ice pellets) is actually observing what’s falling, not inferring it from a temperature column. The constraint is that METAR coverage is airport-centric. A city center 20km from the nearest ICAO station can be in a completely different precipitation phase — especially in hilly terrain where the freezing level shifts quickly over short distances.
Our station selection logic tries to account for this. Where there’s significant elevation difference between the reporting station and the requested coordinate, we apply a lapse-rate correction — but temperature correction doesn’t automatically translate to a different precipitation type flag. If a station at 50m is reporting rain and the requested point is at 300m, the correction might push the adjusted temperature below zero, but whether we flip the type flag from rain to snow depends on how that threshold is tuned. No implementation here is fully clean, and we’d rather say that plainly.
What to Actually Build On
If your application needs to distinguish rain from snow for display purposes, precip_type is the right field. It’s better than inferring type from surface temperature yourself, because the underlying algorithm has access to multi-level model data that doesn’t appear in the API response.
If your application is making a risk decision — road condition alerting, flight dispatching, construction hold logic — treat precip_type as a signal, not ground truth. The more useful combination is type plus surface temperature. If type is rain but temp is below 2°C, that’s an ambiguous situation worth flagging. If type is snow and temp is above 4°C, something is off — either the model is wrong or there’s a data lag — and you probably shouldn’t be rendering a snowflake icon.
A simple guard in Python:
def resolved_precip_type(api_type: str, temp_c: float) -> str:
"""
Apply a sanity check on model-derived precip type using surface temp.
Returns the original type, a warning flag, or an adjusted type.
"""
if api_type == "snow" and temp_c > 4.0:
return "rain" # Almost certainly model artifact or data lag
if api_type == "rain" and temp_c < 1.0:
return "freezing_rain_risk" # Ambiguous — flag rather than silently pass
return api_type
That's not a replacement for better model data — it's a cheap filter that catches the most common mismatch cases. The 1.0°C threshold is conservative; you could tighten it to 0°C but you'd miss legitimate freezing rain events where surface temp reads marginally positive while the road surface is already below freezing.
Amount Is Liquid-Equivalent. Always.
One thing that trips people up: precip_mm is liquid-equivalent regardless of type. 10mm of snow is not 10mm of snow depth — it's 10mm of water that fell as snow. Actual depth depends on snow density, which varies from roughly 50–100 kg/m³ for dry powder to 300–500 kg/m³ for wet, heavy snow. The common rule of thumb is 10:1 (10mm liquid = 100mm depth) for average continental snowfall, but coastal snow — the kind you get in Glasgow or Seattle — often runs 5:1 or wetter because it's denser.
If you're building a snowpack or depth display, liquid-equivalent plus type isn't enough. You'd need a snow density model on top, which most API responses don't include. Snowfall depth from a weather API is genuinely hard to get right — the physics varies too much by location and air mass to paper over with a single conversion factor.
The Real Caveat
All of this is most relevant at the margins. At -10°C and snowing, precip type is not your problem. At 0.5°C with 3mm of precipitation forecast, it might be rain, snow, sleet, or freezing rain depending on how the storm evolves — and no API can tell you with certainty, because the atmosphere hasn't committed yet either.
A hard binary on precip type in that temperature band is the wrong architecture. A risk-level or ambiguity flag is more honest and more useful than a confident wrong answer.
The fastest way to calibrate your own thresholds: pull a week of historical responses for a northern-latitude location during a known mixed-precipitation period and compare the condition code, precip_type, and surface temperature. You'll see exactly where the model-based inference breaks down — and that'll tell you more about where to draw your lines than any documentation will.
