Day-1 forecasts from GFS or HRRR are pretty good. Day-7 forecasts are educated guesses dressed up as numbers. The API returns both with identical formatting and identical apparent precision — six decimal places on temperature, a percentage for precipitation probability — and nothing in the response tells your application that one of those figures is substantially more trustworthy than the other.
That asymmetry is a real problem if you’re building anything that makes decisions based on forecast data. Delivery routing, irrigation scheduling, construction planning, outdoor event risk scoring — all of these can be quietly undermined by treating day 7 the same as day 1.
Why Skill Degrades the Way It Does
Numerical weather prediction models are initial-condition problems. You start with the best available snapshot of the atmosphere — surface observations, radiosonde data, satellite retrievals, aircraft AMDAR reports — and integrate forward through time using physics equations discretized onto a grid. Every step compounds the error from the initial state. Small inaccuracies in where a pressure system sits, or how moist a column of air is, grow non-linearly.
HRRR (the High-Resolution Rapid Refresh, 3km grid) is exceptional inside 18 hours because the initial conditions are fresh — it assimilates radar data every 15 minutes and runs hourly. Beyond 48 hours it doesn’t run at all. GFS extends to 16 days at 0.25-degree resolution, but NOAA’s Environmental Modeling Center skill scores show that 500hPa height anomaly correlation — a standard measure of large-scale forecast skill — drops below 0.6 around day 8 to 10, varying by season and hemisphere. Below 0.6, you’re closer to climatology than to an actual forecast.
Precipitation probability skill degrades faster than temperature skill. Precipitation requires accurate placement of mesoscale features that temperature forecasts can partially average over. Knowing it’ll be 15°C on day 6 is plausible. Knowing it’ll rain between 14:00 and 17:00 on day 6 is close to fiction.
A Practical Decay Model You Can Actually Use
You don’t need full ensemble spread calculations to do something useful here. A simple horizon-based confidence weight is enough to change how your application behaves.
Here’s the decay curve I reach for first — sigmoid-ish, stays near 1.0 for the first two days, falls steadily through days 3–7, floors around 0.2 by day 10:
// JavaScript
function forecastConfidence(daysAhead) {
// Confidence decays from ~1.0 at day 0 to ~0.2 by day 10+
const floor = 0.20;
const decay = 0.28; // tune per use case
const raw = Math.exp(-decay * Math.max(0, daysAhead - 1));
return floor + (1.0 - floor) * raw;
}
// Example outputs:
// forecastConfidence(0) → 1.00
// forecastConfidence(1) → 1.00
// forecastConfidence(3) → 0.72
// forecastConfidence(5) → 0.48
// forecastConfidence(7) → 0.31
// forecastConfidence(10) → 0.22
The decay constant is the thing worth tuning. 0.28 is a reasonable starting point for temperature-based decisions. For precipitation probability, push it closer to 0.40. For wind, somewhere in between — synoptic wind direction 5 days out retains moderate skill, but gust magnitudes do not.
How to Apply This to WeatherAPI Responses
WeatherAPI’s forecast endpoint returns up to 14 days of daily and hourly data in a single response. The forecastday array is indexed 0 through N, index 0 being today. Converting that array index to a days-ahead value is straightforward:
// JavaScript — annotate each forecast day with a confidence weight
const response = await fetch(
`https://api.weatherapi.com/v1/forecast.json?key=${API_KEY}&q=${location}&days=10`
);
const data = await response.json();
const today = new Date();
today.setHours(0, 0, 0, 0);
const annotated = data.forecast.forecastday.map((day) => {
const forecastDate = new Date(day.date);
const daysAhead = Math.round((forecastDate - today) / 86400000);
const confidence = forecastConfidence(daysAhead);
return {
date: day.date,
daysAhead,
confidence,
// Raw fields
maxtemp_c: day.day.maxtemp_c,
mintemp_c: day.day.mintemp_c,
daily_chance_of_rain: day.day.daily_chance_of_rain,
// Weighted uncertainty bounds — simple symmetric version
maxtemp_uncertainty_c: (1.0 - confidence) * 6.0, // ±6°C at zero confidence
rain_chance_uncertainty_pct: (1.0 - confidence) * 40, // ±40pp at zero confidence
};
});
Those uncertainty bounds are illustrative, not official. NWS probabilistic forecasts and ECMWF ensemble spread data put temperature uncertainty roughly in the 1–2°C range at day 1, growing to 4–6°C by day 7 in mid-latitudes. A rough bound is more honest than implying the day-7 number is exact.
Where This Actually Changes Behavior
The point isn’t to display confidence numbers to users — most don’t want that. The point is to change what your application does based on horizon.
- Decision thresholds should widen with horizon. If you trigger an alert when precipitation probability exceeds 60%, that threshold makes sense at day 1. At day 6, the same 60% reading carries enough additional uncertainty that you might require 75% before triggering — or suppress the alert and flag it as “watch, not warning.”
- UI elements can reflect confidence visually without showing a number. Slightly desaturated colors, dashed instead of solid borders, a simple “forecast reliability: moderate” label — these communicate uncertainty without requiring users to understand ensemble spread.
- Caching TTLs should shorten as horizon shrinks. A day-7 forecast will shift substantially with each new model run; caching it for 3 hours is fine. Caching a day-1 hourly forecast for 3 hours when HRRR refreshes hourly wastes real accuracy. Freshness is most valuable at short range, least valuable at extended range.
Where the Model Breaks Down
Skill decay is consistent in aggregate, but not uniform across situations. A forecast for a stable high-pressure week might hold reasonable accuracy at day 8. A forecast during an active blocking-pattern breakdown might be wrong by day 3. The exponential decay curve is a statistical average over many forecasts, not a measurement of any specific one.
Actual ensemble spread data exists — ECMWF’s open-data program publishes ENS products in GRIB2, and some commercial providers expose it as a field — but integrating it is a real engineering investment. For most applications, a calibrated decay curve gets you most of the benefit with a few lines of code.
One instinct worth resisting: hiding day 7+ data entirely. That overcorrects. Extended-range data is genuinely useful for planning even when it’s not operationally reliable. Use it with appropriately widened uncertainty rather than discarding it.
If you’re building against WeatherAPI’s 14-day endpoint, run forecastConfidence(daysAhead) against a few weeks of real responses for a location you know well, then compare the confidence-weighted range to what actually happened. The decay constant you land on through that loop will fit your use case better than any default — including 0.28.
