Tile URLs
https://nimboweather.com/tiles/{layer}/{issue}/{valid}/{z}/{x}/{y}.webp?key=nimbo_...
| Part | Meaning |
|---|---|
layer | rain: rain rate in the map’s colours. p1: chance of 1 mm/h or more, as blue of varying opacity. |
issue | The forecast’s issue time, YYYYMMDDTHHMM in UTC. |
valid | The time the frame shows, same format, in 10-minute steps. |
z/x/y | Web Mercator tile address, zoom 0 to 8, 256 × 256 pixels. |
key | Your key, required. Tiles take it in the URL only, so that cached copies stay tied to it: a key sent in a header gets 401 key_in_query_only. |
Tiles are lossless WebP with transparency: dry and uncovered areas are transparent, so they sit on any base map. Above zoom 8, let your map library stretch zoom 8 tiles (maxNativeZoom in Leaflet, maxzoom on a MapLibre source). Since the issue time is in the URL, a tile never changes and can be cached for good.
The tile index
GET /v1/tiles/index lists the forecast frames you can draw right now, each with a ready URL template per layer. Past radar frames are not served as tiles yet: past is an empty list for now. Read it once per refresh (every 10 minutes is enough) and build your layers from it.
curl "https://nimboweather.com/v1/tiles/index" -H "X-API-Key: $NIMBO_KEY"
Each frame carries its time, its offset_min (minutes after the forecast’s issue time) and, under tiles, one URL per layer (tiles.rain, tiles.p1) with {z}, {x} and {y} left for your map library to fill. Add ?key= to it. All frames of one index belong to the same forecast, which is what keeps an animation smooth.
Leaflet
The forecast for one hour ahead, over OpenStreetMap, in a page with <div id="map"> and Leaflet loaded:
const KEY = "nimbo_...";
const map = L.map("map").setView([48.5, 8], 5);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 12, attribution: "© OpenStreetMap contributors",
}).addTo(map);
const index = await (await fetch(`https://nimboweather.com/v1/tiles/index?key=${KEY}`)).json();
const frame = index.forecast.find((f) => f.offset_min === 60); // one hour ahead
L.tileLayer(`${frame.tiles.rain}?key=${KEY}`, {
maxNativeZoom: 8, opacity: 0.85, attribution: ["Rain: Nimbo Weather", ...index.attribution].join(", "),
}).addTo(map);MapLibre
const KEY = "nimbo_...";
const map = new maplibregl.Map({ container: "map", style: "https://tiles.openfreemap.org/styles/positron", center: [8, 48.5], zoom: 4 });
map.on("load", async () => {
const index = await (await fetch(`https://nimboweather.com/v1/tiles/index?key=${KEY}`)).json();
const frame = index.forecast.find((f) => f.offset_min === 60);
map.addSource("rain", { type: "raster", tiles: [`${frame.tiles.rain}?key=${KEY}`], tileSize: 256, maxzoom: 8,
attribution: ["Rain: Nimbo Weather", ...index.attribution].join(", ") });
map.addLayer({ id: "rain", type: "raster", source: "rain", paint: { "raster-opacity": 0.85 } });
});Animating
Add one layer per frame of the index with opacity 0, wait for their tiles to load, then step through them by setting one layer’s opacity at a time. Building every frame from the same index matters: the forecast is replaced every 10 minutes, and frames from two different forecasts make the rain jump.
A full animated view of a map (about 12 to 20 tiles on screen, 20 frames) loads 250 to 400 tiles. The pricing page turns that into views per month.
The latest alias
For a single still image, such as a widget, there is a shortcut that needs no index:
https://nimboweather.com/tiles/rain/latest/{offset_min}/{z}/{x}/{y}.webp?key=nimbo_...
offset_min is how far ahead of the latest forecast’s issue time, in minutes: one of the offset_min values of the index (every 5 minutes to one hour, then every 15 to three hours). The answer is a 302 redirect to the tile’s permanent URL, with your key carried over. The redirect checks the key but is not counted. Do not use it for animations: the latest forecast can change between two frames of your loop. Use the index instead.
How tiles are counted
- A tile our server draws for you counts as one call against your monthly allowance.
- A tile answered from our cache does not reach the server and is not counted.
- The
latestredirect is not counted. - Each key may load bursts of up to 40 tiles, refilled at 25 tiles a second, enough for a full map view. Past that you get a
429with aRetry-Afterheader in seconds; a tile refused this way is not counted.
Attribution follows the same rule as the point API; the index carries the credit to show in its attribution field, as the examples above do.