Two Fields, Two Different Things
If you’ve pulled forecast data from the WeatherAPI /forecast.json endpoint and noticed that summing hourly snow_cm across a day doesn’t match the daily totalsnow_cm value, you’re not looking at a bug. You’re running into a genuine representational difference between how the API surfaces hourly model output versus aggregated daily snowfall estimates. Conflating them gives you wrong numbers.
Get it wrong in a ski resort availability checker or a road-treatment dispatch tool and you’re either crying wolf or missing a real accumulation event.
What totalsnow_cm Actually Represents
totalsnow_cm appears at the forecast day level — inside forecast.forecastday[n].day. It’s a single aggregated figure for total snowfall accumulation across the 24-hour period, sourced from NWP model output (GFS or NAM for North American coverage, ECMWF for broader global reach) as a direct forecast accumulation field, not a sum of hourly intervals.
That distinction matters because of how NWP models actually work. GFS outputs snowfall as accumulated fields over specific time windows — typically 3-hour or 6-hour intervals in the raw GRIB2 files — not as instantaneous rates you can trivially bin into hours. When a pipeline ingests those files and maps to a daily total, it’s working with the model’s own accumulated field rather than re-summing smaller intervals. Re-summing intervals from different model runs introduces drift, particularly around model initialization boundaries. Using the model’s native accumulation field avoids that.
What the Hourly snow_cm Represents
Hourly snow_cm, inside forecast.forecastday[n].hour[n], is a rate-equivalent figure — how much snow is expected within that specific hour. These are derived values, downscaled from 3- or 6-hourly model accumulation intervals to hourly slots.
The downscaling is partly constructed. A 6-hour block of 4.2cm gets distributed across six hourly slots — often evenly, occasionally weighted by precipitation probability or model-inferred intensity — but the granularity you’re seeing isn’t directly observed at hourly resolution. GFS runs at 0.25° horizontal resolution with output steps that don’t natively produce true hourly snowfall. HRRR does — it covers CONUS at 3km resolution with genuine hourly output — but it only runs to 48 hours and only covers the contiguous US. Beyond that window or outside that domain, you’re back on GFS or similar, and hourly granularity is an interpolation.
Why They Diverge
Several mechanisms cause the two fields to disagree when you try to compare them:
- Rounding at different levels. Each hourly value rounded to two decimal places before summation accumulates error across 24 values. A daily field pulled directly from the model avoids this entirely.
- Boundary effects. The API defines a forecast day in local time. Snowfall in the local early morning (say 01:00–03:00) belongs to today’s daily total, but if the underlying model accumulation window started at 00:00 UTC, that straddles two calendar days in UTC. Hourly fields reflect local time slots; the daily total reflects the model’s accumulation window alignment, which isn’t always identical.
- Model run mixing. Later days in a 7-day forecast pull from a different model run than today. When daily aggregates and hourly slots are populated from different initialization cycles, small differences in forecast state between runs create visible divergence.
- Snow-to-liquid ratio assumptions. Converting liquid-equivalent precipitation to snow depth requires a density assumption. If hourly and daily fields use slightly different density lookup tables — which can vary by model and implementation — the numbers won’t reconcile.
Which One to Trust, and When
For total accumulation over a day — “how much snow will fall on Tuesday” — use totalsnow_cm at the day level. It’s the more internally consistent figure and reflects how the underlying model actually framed the forecast.
For timing — “when during the day will snow fall” — hourly snow_cm is the right field, but treat specific per-hour values as approximate. A reasonable approach: use hourly values for the distribution of snowfall across the day while anchoring total expected accumulation to totalsnow_cm. Normalize proportionally if you need the hours to sum correctly:
daily_total = forecast_day['day']['totalsnow_cm']
hourly_raw_sum = sum(h['snow_cm'] for h in forecast_day['hour'])
if hourly_raw_sum > 0:
normalized_hours = [
{
'time': h['time'],
'snow_cm': h['snow_cm'] * (daily_total / hourly_raw_sum)
}
for h in forecast_day['hour']
]
else:
# No hourly signal — distribute evenly across hours where precip > 0
precip_hours = [h for h in forecast_day['hour'] if h['precip_mm'] > 0]
per_hour = daily_total / len(precip_hours) if precip_hours else 0
normalized_hours = [
{'time': h['time'], 'snow_cm': per_hour if h['precip_mm'] > 0 else 0.0}
for h in forecast_day['hour']
]
The fallback branch handles the edge case where hourly snow values are all zero but the daily total isn’t — which happens with light accumulation events that fall below the hourly rounding threshold.
The Edge Case That Catches People Out
One scenario that produces especially confusing output: a forecast day where totalsnow_cm is 0.0 but hourly snow_cm shows a few non-zero values.
This usually means the daily aggregate rounded down — accumulation below roughly 0.5cm is common in transitional weather near the freezing line — while the hourly values, being smaller slices, were individually non-zero before rounding. Or the condition code at the day level got classified as rain rather than snow. The daily condition object reflects the dominant condition for the day, not a union of all hourly conditions. A day that’s mostly rain with one hour of sleet won’t carry snow in its day-level condition code, even if an hourly slot shows snow_cm: 0.2.
If you’re building a snow-detection trigger for logistics routing or similar, check both levels: totalsnow_cm > 0 OR any hourly snow_cm > threshold. The daily condition code alone will miss marginal events.
A Note on Forecast Range
Snowfall forecast accuracy degrades faster than temperature. NWS verification work consistently shows that day 1–2 QPF skill is reasonable; by day 5–7 you’re looking at climatological guidance dressed up as a specific number. We surface values out to 14 days because the endpoint supports it, but day 8+ snow totals shouldn’t be presented to users as actionable without some visual treatment that conveys the uncertainty. Implying precision the underlying model doesn’t have is a trust problem waiting to happen.
For use cases where that distinction is consequential — avalanche risk assessment, agricultural frost management, event planning — consider flagging days beyond the 72-hour mark differently in your UI rather than rendering them identically to near-term forecasts.
The most useful calibration exercise: grab a known snowfall event from your target region, pull the forecast from the day before, and compare totalsnow_cm against the sum of hourly snow_cm. That delta tells you exactly how much the two fields diverge for your specific location and season. No general guidance about which field to trust is more useful than that single concrete check.
