API documentation
Four raster layers and two JSON endpoints, behind one key. Tiles are standard XYZ — anything that speaks slippy maps can draw them.
Quickstart
Two requests. Ask what frames exist, then fetch a tile from one.
curl -H "x-api-key: $KEY" \
https://splitshotweather.com/manifest/composite.json
That returns a frames array of timestamps. Put one into the tile template:
https://splitshotweather.com/tiles/composite/{ts}/{z}/{x}/{y}.png?key=$KEY
In MapLibre or Mapbox GL:
map.addSource('radar', {
type: 'raster',
tiles: ['https://splitshotweather.com/tiles/composite/20261024-0610/{z}/{x}/{y}.png?key=' + KEY],
tileSize: 256,
maxzoom: 8
});
map.addLayer({ id: 'radar', type: 'raster', source: 'radar',
paint: { 'raster-opacity': 0.85 } });
maxzoom 8 matters. Tiles are generated to zoom 8 and the source data is about 1 km, so one screen pixel is one data cell around zoom 7. Past that every zoom is interpolation — letting the GPU stretch from 8 looks the same as asking us for deeper tiles and costs you nothing.
Authentication
Every request needs your key, either way round:
| x-api-key: <key> | Header. Use this where you can. |
| ?key=<key> | Query string. For map libraries that cannot attach headers to tile requests. |
The query-string form exists because several map SDKs give you no way to set a header on a raster tile request. It is stripped at the edge before the request is cached or logged. Treat the key as a secret either way — anything in a URL ends up in browser history and referrer headers.
Manifests
A manifest says which frames exist right now. Poll it, do not guess — publish cadence varies by layer, and a missed render leaves a gap that counting backwards from "now" would land in.
GET /manifest/{product}.json
{
"product": "composite",
"source": "NOAA MRMS MergedReflectivityQCComposite_00.50",
"updated": "2026-10-24T06:10:33Z",
"cadenceMinutes": 5,
"maxZoom": 8,
"units": "dBZ",
"legend": [ { "value": 5, "unit": "dBZ", "rgb": [126,211,126] }, … ],
"tileUrlTemplate": "/tiles/composite/{ts}/{z}/{x}/{y}.png",
"frames": ["20261024-0540", … , "20261024-0610"],
"latest": "20261024-0610",
"forecastTileUrlTemplate": "/tiles/composite-fcst/{ts}/{z}/{x}/{y}.png",
"forecast": [ { "ts": "20261024-0615", "leadMinutes": 5 }, … ]
}
| frames | Observed frames, oldest first. |
| forecast | Extrapolated frames, with lead time. Empty on layers without a forecast. Served from a separate template so a projection is never mistaken for a measurement. |
| legend | The colour ramp, so you can draw a matching scale. Every stop carries value and unit. |
| cadenceMinutes | How often a new frame appears — step your timeline by this, not by a guess. |
Manifests are cached 30 seconds at the edge. Polling faster just burns requests against your quota.
Tiles
GET /tiles/{product}/{ts}/{z}/{x}/{y}.png
256 px PNG with transparency, Web Mercator, standard XYZ (y increasing
southward). {ts} comes from a manifest.
A tile with nothing in it is never written, and returns 404. That is the correct answer — draw nothing. It is also most of why this is cheap, so do not treat a 404 as an error. 404s are not billable.
Radar
| product | composite |
| source | NOAA MRMS composite reflectivity |
| cadence | 5 minutes |
| forecast | 30 minutes, 5-minute steps |
| units | dBZ |
Rain totals
| products | qpe-1h, qpe-24h |
| source | NOAA MRMS multi-sensor QPE — radar corrected against rain gauges |
| cadence | hourly, published about an hour behind the hour it covers |
| units | millimetres (legend also carries inches) |
Lightning
| product | lightning |
| source | GOES satellite lightning mapper, both satellites |
| cadence | 5 minutes, each frame covering a rolling 15-minute window |
| units | flashes per cell |
Total lightning — in-cloud as well as cloud-to-ground. In-cloud activity usually precedes the first ground strike, so this tends to light up before a ground network would. Located to roughly 8–14 km: it answers "is that storm electrically active", never "the strike was here".
The window is longer than the cadence on purpose, so consecutive frames
overlap and an animation moves rather than strobing. The manifest reports
windowMinutes.
Wind
| product | wind |
| source | NOAA real-time mesoscale analysis, 2.5 km |
| cadence | hourly |
| units | mph |
Latest frame only — no timeline.
Point forecast
GET /forecast/{lat},{lon}
Current conditions, hourly for the next twelve hours, and seven days. Temperature, feels-like, wind speed and direction, humidity, pressure, visibility, precipitation probability, conditions and an icon.
Coordinates are rounded to two decimal places before anything upstream sees them — about a kilometre, which is finer than the underlying grid and means an exact location never leaves this service.
Historical conditions
GET /history/{lat},{lon}/{iso8601}
What the weather actually was at a point and a moment: temperature, wind, pressure, humidity, cloud cover and precipitation. Back to 2013.
Available on Growth. Unknown values are returned as null rather
than filled in — an imputed number cannot be told from a measurement later,
and for a historical record that distinction is the whole point.
Coverage
| Region | Radar | Rain | Lightning | Wind |
|---|---|---|---|---|
| CONUS | ✓ | ✓ | ✓ | ✓ |
| Alaska | ✓ | ✓ | — | ✓ |
| Hawaii | ✓ | — | ✓ | ✓ |
| Puerto Rico | ✓ | ✓ | ✓ | ✓ |
| Guam | ✓ | ✓ | — | ✓ |
Lightning stops near 54°N — that is the satellite's field of view, measured, not a gap we intend to fill. Guam is outside both satellites. Hawaii has no published gauge-corrected rainfall. Nothing outside the United States.
Errors & limits
| 200 | Fine. |
| 401 | Missing, malformed or revoked key. Never cached — a key activated a moment ago works immediately. |
| 404 | No tile there. Expected, not an error. Draw nothing. |
| 429 | Monthly request limit reached. Body carries an upgrade link. |
| 502 | Upstream unavailable. Retry. |
Only 2xx and 3xx count against your plan. Your 401s and 404s are not billed — a misconfigured client should cost you a bug report, not quota.
A newly issued key can return 401 from edges that have not yet converged. Allow a minute, and treat five consecutive successes as proof rather than one.
Availability
There is no uptime guarantee on Starter or Growth. What there is instead: frames are immutable once published and served from cache, so a failure upstream degrades the service to stale frames rather than blank ones, and frame age is alarmed on. Ask if you need something contractual.