Hourly Forecast Interpolation: Why Grabbing Hour 0 and Hour 1 Isn’t Enough for Sub-Hourly Apps

WeatherAPI’s forecast endpoint returns one data point per hour. If you’re building something that needs a weather value at 14:23 — a delivery ETA, a construction scheduling tool, a drone flight window calculator — you have two choices: snap to the nearest hour and accept the error, or interpolate between the hour blocks and try to do better. Most apps do the former without thinking about it. Some try the latter but do it wrong in ways that produce numbers worse than just snapping.

Here’s what actually matters when interpolating hourly forecast data, where linear interpolation holds up, and where it silently breaks.

What the Hourly Payload Actually Contains

Each hourly block in a WeatherAPI forecast response represents a snapshot at the top of that hour, not an average over the hour. So hour[14] is the modeled state of the atmosphere at 14:00, not the average condition from 14:00 to 14:59. That distinction matters because it changes what interpolation is doing conceptually — you’re estimating a value on a curve between two sampled points, not filling in a gap between two measured averages.

The underlying model data (HRRR at 3km resolution, NAM at 12km, GFS at roughly 25km depending on version) produces output on its own native timestep, which our ingestion pipeline aggregates and maps to the top-of-hour structure you get back from the API. You’re already one step removed from the raw model output before you start interpolating further.

Where Linear Interpolation Is Fine

Temperature: almost always fine. Atmospheric temperature changes slowly enough between consecutive hours that a linear estimate at minute 37 is going to be within rounding noise of the truth on most days. Same goes for pressure, dew point over short windows, and wind speed in steady-state conditions.

The math is just:

value_at_t = h0_value + (h1_value - h0_value) * (minutes_past_hour / 60.0)

Nothing exotic. If temperature at 14:00 is 18.4°C and at 15:00 is 20.1°C, then at 14:23 you’d estimate 18.4 + (1.7 * 0.383) ≈ 19.05°C. Reasonable. The error on a typical day is small enough not to matter for most applications.

Where It Breaks Quietly

Wind direction. This one bites people. wind_degree is a circular quantity — 359° and 1° are two degrees apart, not 358°. Linearly interpolating between 350° and 10° gives you 180° at the midpoint, which is the exact opposite direction from correct. You need to interpolate over the shorter arc.

The fix is to compute the angular difference correctly before interpolating:

delta = ((h1_deg - h0_deg) + 540) % 360 - 180
interpolated_deg = (h0_deg + delta * fraction + 360) % 360

That’s not optional. Skipping it produces wrong results any time wind is shifting through north, which happens constantly in real weather systems.

Cloud cover is a softer problem but still real. cloud in the response is an integer percentage (0–100). Linear interpolation between 20% at hour 0 and 80% at hour 1 gives you a smooth ramp, which is physically plausible for some situations and completely wrong for others — a convective afternoon where a cell develops and collapses within 30 minutes doesn’t look like a ramp at all. For cloud cover, linear interpolation gives you a working approximation, not something you should present to users as a measurement.

Precipitation is the worst case. precip_mm in an hourly block is the total for that hour — an accumulation bucket, not a point on a smooth curve. Rain can fall in bursts, stop, and resume. Interpolating between two hourly totals and using the result to imply a rain rate at minute 23 isn’t meaningful. If you need sub-hourly precipitation intensity, the hourly forecast is the wrong tool. Poll the current conditions endpoint more frequently, or acknowledge in your UI that you’re working with an hourly average spread across time.

Condition Codes Don’t Interpolate at All

The condition.code field is categorical. There’s no sensible interpolation between code 1063 (patchy rain possible) and code 1183 (light drizzle). Don’t try. If you need a condition for a sub-hourly timestamp, use the nearest hour’s code — or, for safety-critical applications, use the more conservative of the two surrounding hours.

A Practical Pattern for Sub-Hourly Lookups

For most numeric fields, this function structure works as a reasonable starting point in any language:

function interpolateHourlyField(hours, targetEpoch, fieldName) {
  // hours: array of hourly objects with 'time_epoch' and numeric fields
  // find surrounding pair
  const h0 = hours.findLast(h => h.time_epoch <= targetEpoch);
  const h1 = hours.find(h => h.time_epoch > targetEpoch);

  if (!h0) return hours[0][fieldName];
  if (!h1) return hours[hours.length - 1][fieldName];

  const fraction = (targetEpoch - h0.time_epoch) / (h1.time_epoch - h0.time_epoch);
  return h0[fieldName] + (h1[fieldName] - h0[fieldName]) * fraction;
}

Apply circular correction separately when the field is wind_degree. Skip interpolation entirely for condition.code, will_it_rain, and will_it_snow.

One thing that catches people: time_epoch in WeatherAPI responses is always UTC. If your target timestamp is in local time, convert before comparing. Getting this wrong puts your fraction calculation between the wrong two hours entirely — the kind of bug that only shows up in certain time zones and only at certain hours, which makes it annoying to track down.

Caching Interacts With This

If you’re caching forecast responses — and you should be, since fetching a fresh 3-day forecast every minute burns quota for no meaningful accuracy gain — cache the full hourly array and do interpolation client-side on the cached data. The forecast isn’t going to shift meaningfully in the four minutes between a cache hit and a re-fetch. A 10–15 minute TTL for forecast data keeps things reasonably current without the waste.

The Honest Ceiling

Interpolation between hourly model output is an approximation. It reduces error compared to snapping, for smooth fields under stable atmospheric conditions. It does not give you real sub-hourly observations — those don’t exist in a forecast product. For temperature, pressure, and humidity it’s a sensible approach. For wind, get the circular math right and it’s usable. For precipitation and condition codes, either skip interpolation or tell your users explicitly that the value is derived from adjacent hourly buckets, not a modeled or observed quantity at that exact minute.

If your use case genuinely requires high-frequency data — real-time construction site safety, precision agricultural spray windows — the right architecture is to poll /current.json on a short interval, build your own time series from real observations, and use the hourly forecast only for lookahead beyond the current hour. More work, but more honest about what the data actually is.

The question worth sitting with before you ship sub-hourly estimates: are you doing this because your application needs that precision, or because it feels more precise to the user? Those are different justifications and they lead to different decisions about how prominently to surface the uncertainty.

Scroll to Top