Two files: a cron val (refresh.ts) fetches an upstream JSON API every 15 minutes and writes the result to blob storage, and an HTTP val (main.ts) serves that cached blob instantly with an X-Cache-Age header. Your endpoint never hits the upstream on request, so it is fast and your traffic never counts against the upstream's rate limit.
On Val Town (or any serverless platform), outbound requests leave from shared egress IPs. Keyless APIs that rate-limit by IP -- Open-Meteo, for example -- count every other user's requests on that IP against "your" limit, so even one polite cron call can come back 429. An earlier version of this template used Open-Meteo and hit exactly that failure: the refresh cron itself got rate-limited by other users' traffic.
The fix is an upstream with a per-key quota. This template uses OpenWeatherMap: the quota belongs to your API key, so nobody else on the shared IP can drain it. The cron-to-blob cache then keeps your own usage to one upstream call per interval, no matter how much traffic your endpoint gets.
refresh.ts- cron, runs every 15 minutes, fetches OpenWeatherMap current weather and writes{ fetchedAt, data }to the blob keycachemain.ts- HTTP endpoint, reads the blob and returns the data withX-Cache-Age(seconds) andX-Cache-Fetched-Atheaders; returns 503 if the cache is empty
- Remix this val
- Get a free API key at https://openweathermap.org/api
- Set the
OPENWEATHER_API_KEYenvironment variable
š Add OPENWEATHER_API_KEY here: https://www.val.town/x/templates/cached-api-proxy/environment-variables?key=OPENWEATHER_API_KEY
- Run
refresh.tsonce (Run button) to populate the cache, or wait for the first cron run - Hit the
main.tsendpoint URL - check theX-Cache-Ageresponse header to see how fresh the data is
To adapt it to your own API: change the URL in refresh.ts and adjust the cron interval to taste. Keep reading the key with Deno.env.get() - it stays server-side and is never exposed to your endpoint's callers.