30 September 2026 · 640 words
The 40 minutes that weren’t: switching to MeteoSwiss
In late September a bug report came in: flicking the travel control made the Basel card show “40 min of sun” under a “Using cached forecast” banner, on a bright day. It took one weekend to trace and four releases to fix properly. By the end, FOMO Sun was running on MeteoSwiss’s own forecasts.
The “40 min”
The logs showed Open-Meteo, the free weather API the app used, answering HTTP 429 (too many requests) after about six requests in 25 seconds. Each flick of the control re-downloaded around 78 destination forecasts. Open-Meteo counts a request with more than 10 variables as several calls, and the app asked for 19. One flick cost about 156 calls.
When a batch failed, the whole answer fell back to sample data. The banner said “cached forecast”, which wasn’t true, and “40 min” was the sample city’s number, not Basel’s.
1.24 fixed it from both ends:
- a cache per destination (20 minutes fresh, older data only on errors, never from another day)
- half the variables per request, so one location costs one call
- a one-minute pause after a 429 instead of retrying harder
- partial answers instead of all-or-nothing
- if live data fails, the last good answer for the same question, labelled with its time; sample data only as a last resort, and labelled as such
While I was there: 114 places in the wrong spot
The same weekend I checked the catalog. 114 destinations had wrong coordinates from an early generated batch. Spiez sat near Grindelwald, which is why it showed 8°C on a 29°C day. Wisenberg sat next to Pratteln, “8 minutes from Basel”. Wrong coordinates mean wrong weather and wrong travel times. All 114 were fixed, then 127 navigation links that still pointed to the old spots.
Yesterday’s forecast, served today
The production check after 1.24 found something worse. Zurich tomorrow showed 0 minutes of sun, so every place “beat” Zurich.
The cause was older than any of this. The forecast requests went through Vercel’s data cache, which serves the last stored answer while it refreshes in the background, and survives deployments. For a city nobody had asked about since yesterday, that stored answer was yesterday’s forecast. Read today, its “tomorrow” was missing, so it read as zero.
1.24.2 turned that cache off for weather, keyed the app’s own caches by the Swiss date, and rejected any forecast that doesn’t cover both today and tomorrow.
The real fix: MeteoSwiss, directly
Rate limits on a free API will always come back on a busy morning. So 1.25 switched Swiss places to MeteoSwiss’s own local forecasts, published as Open Government Data: hourly sunshine, temperature, cloud and wind for about 5,600 points, every postcode and many summits, updated every hour. Static files, no key, no quota.
The app reads the files once per run, keeps them in memory and matches each place to the nearest point at the right altitude, so a summit gets the mountain point and not the valley below. Open-Meteo is still used for places abroad.
Sunrise and sunset are now calculated instead of fetched. That removed another call and fixed a quiet bug: the time to sunset had been 1 to 2 hours too high, because a local time was read as UTC.
What I learned
A fallback that looks like data is worse than an error. “40 min” looked plausible. A clear “forecast unavailable” would have been reported a month earlier.
Caches need a calendar. Anything weather-related must know which day it belongs to.
Use the source. The best forecast for Switzerland was free all along.