Cache a rate-limited API behind your own endpoint
If an upstream API is rate-limited, the fix is to stop hitting it per request: a cron val fetches the upstream every 15 minutes and writes the result to blob storage, and an HTTP val serves that cached blob instantly. Your endpoint never talks to the upstream on request, so it's fast and your traffic never counts against the upstream's rate limit.
Why a keyed upstream matters
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.
The two files
The cron half fetches the upstream and writes { fetchedAt, data } to a
blob:
The HTTP half serves the cache with an X-Cache-Age header (and a 503 if
the cache is empty):
Set it up
- Remix this template — click the Remix button in the top right corner.
- Get a free API key at openweathermap.org/api.
- Add it to your val's
environment variables as
OPENWEATHER_API_KEY. - Click Run on
refresh.tsonce to populate the cache, or wait for the first cron run. - Hit the
main.tsendpoint URL and 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 schedule to taste. Keep reading the key with Deno.env.get() — it
stays server-side and is never exposed to your endpoint's callers.
Notes
- The 15-minute schedule (
*/15 * * * *) is the fastest a cron can run on the free plan; Pro allows up to once a minute. - Cached data survives between runs in blob storage, so a failed refresh just means slightly staler data, not an outage.
Next steps
- Blob storage reference — what the cache is built on
- Cron reference — schedule types and limits
- Environment variables — how secrets stay private
Last updated