Snow readiness / Setup guide
Setup guide for municipalities
The snow-readiness API tells a municipality how likely snow is to disrupt it in the next 48 hours, 2 to 7 days and 7 to 15 days, as a level from 0 to 3 with a suggested action. It is read-only and free during the pilot winter 2026/27. Every API call needs a key, which you request here.
1. Getting access
Request a key with the request form (Norwegian and English). It asks for your country and municipality number, the municipality name, who is asking, your preferred national forecast provider, what you will use the levels for, how you will connect (JSON API, CAP feed or the page) and, if you have them, the points to watch.
- Request. Send the form. We read every request ourselves.
- Key. If approved, we email you a key of the form
sr_<id>_<secret>. It is shown once when issued and only a hash is stored, so keep it in your secret store. Lost keys are revoked and replaced, never recovered. - Points and thresholds. We agree the points with you (centre, nearest snow-measuring station, steep bus or road sections) and any thresholds that differ from the defaults.
- Shadow run. We add your municipality; your id is the country code plus the municipality number, for example
NO-4601. Your page and API data appear after the first forecast run. - Connect. JSON API, CAP feed, or the page on a wall screen. One key per system is easiest to manage; ask for more if you need them.
Your key works for every municipality in the service, not only your own. A preferred provider other than MET Norway is noted for later: today the 0 to 48 h horizon uses MET Norway's MEPS where it covers you, and ECMWF elsewhere.
What we log. Each API call is stored with your key id, the endpoint, the municipality, the time, the response status and the response time, so we can see how the service is used and plan capacity. We do not store IP addresses. Usage records are deleted after 90 days. Contact details from the form are kept while your key is active; ask us to delete them at any time.
2. Quick start
Base URL: https://snow.benefelds.lv. Send your key as Authorization: Bearer <key> or X-API-Key: <key>. Your municipality id is the ISO country code plus the national municipality number, for example NO-0301 for Oslo.
Find your municipality, its points and thresholds:
export SNOWREADY_KEY=sr_xxxxxxxx_your-secret
curl -H "Authorization: Bearer $SNOWREADY_KEY" https://snow.benefelds.lv/v1/municipalities
Get today's readiness level for each horizon:
curl -H "Authorization: Bearer $SNOWREADY_KEY" https://snow.benefelds.lv/v1/readiness/NO-0301
Check that the data behind it is fresh:
curl -H "Authorization: Bearer $SNOWREADY_KEY" https://snow.benefelds.lv/v1/status
To see the same data without code, open the city page: /m/NO-0301 (add ?lang=nb or ?lang=en). The page, the public freshness summary at /status and the interactive API reference at /docs need no key. Without a key, /v1 answers 401 with a pointer to /join.
3. Readiness levels
Each horizon gets one level from 0 to 3 and one machine-readable action code. The API sends codes, not prose; English and Norwegian Bokmål texts are published at /strings/en.json and /strings/nb.json.
| Level | Name | 0 to 48 h action code | 2 to 7 days action code | 7 to 15 days action code |
|---|---|---|---|---|
| 0 | Normal | none | none | none |
| 1 | Watch | brief_contractors | check_stock_contracts | prepare_season_start |
| 2 | Standby | standby_prewet_transit_plan | contractors_standby_tyre_reminder | (not used) |
| 3 | Act | dispatch_reroute_inform | (not used) | (not used) |
| Action code | Suggested action |
|---|---|
brief_contractors | Brief contractors and check salt stock. |
standby_prewet_transit_plan | Contractors on call, pre-wet salt, bus diversion plan ready. |
dispatch_reroute_inform | Dispatch ploughs and salt, reroute buses off steep streets, inform the public. |
check_stock_contracts | Check salt stock and contractor availability. |
contractors_standby_tyre_reminder | Put contractors on standby, send the winter-tyre reminder. |
prepare_season_start | Prepare to declare the season start and switch the fleet to winter tyres. |
no_data | No fresh forecast for this horizon; check /v1/status. |
The 7 to 15 day horizon never goes above Watch: at that range ensembles show the weather regime, not a specific event. An official snow or ice warning from the met service in force for the area sets the 0 to 48 h level to 3, whatever the probabilities say.
4. Endpoints
All endpoints are GET, return JSON unless noted, and need your API key. The machine-readable OpenAPI description is at /openapi.json.
| Endpoint | Returns | Call it |
|---|---|---|
/v1/municipalities | Every municipality: id, name, country, time zone, points with height in metres, thresholds in force, season state | Once a day |
/v1/readiness/{id} | Level, action code, driving event, probability and threshold per horizon; rules that fired; official warnings; season state | Every 15 min |
/v1/forecast/{id} | Probability time series per event and source, raw and calibrated side by side. ?by_point=true gives every point instead of the highest | Every 30 to 60 min, for charts |
/v1/status | Freshness per upstream source, runs in the last 24 h, degraded flag | Before acting on a level, or every 15 min for monitoring |
/v1/cap/{id}.xml | 0 to 48 h readiness as a CAP 1.2 alert in the national language and English. ?lang=en gives one language | Every 15 min, or from your CAP aggregator |
/m/{id} | The city page (HTML, no key), prints to one A4 page | For people |
Errors use plain HTTP status codes: 401 for a missing, unknown or revoked key, 404 for an unknown municipality id, 503 when no readiness has been computed yet (a new municipality before its first run). Coming in Phase 1: /v1/readiness/{id}/history (past levels with what happened), /v1/verification/{id} (hit and false-alarm rates) and webhooks that call your system when a level changes.
5. The readiness response
GET /v1/readiness/{id} is the call most integrations need. Times are UTC ISO 8601, probabilities are 0 to 1, heights are metres.
| Field | Meaning |
|---|---|
computed_at | When the levels were last computed |
season_state.state | pre_first_snow (thresholds × 2/3) or in_season; source says whether it was observed, set for the municipality (override) or the default |
first_snow_within | Probability of the season's first snow (≥ 2 cm in 24 h) within 3, 7 and 14 days |
official_warnings[] | Snow and ice warnings from the met service in force for the area: title, level, link |
horizons[].horizon | h0_48h, h2_7d or h7_15d |
horizons[].level | 0 to 3 |
horizons[].action | Action code (see Readiness levels) |
horizons[].driving_event | The event behind the level, or official_warning |
horizons[].driving_point | The point where that event is most likely, for example NO-0301:grefsenkollveien |
horizons[].probability / threshold | The driving event's probability and the threshold it crossed |
horizons[].source | Forecast used: meps (0 to 48 h, 2.5 km) or ecmwf_ens (longer range, or fallback) |
horizons[].model_run_time / data_age_h | The forecast's start time and its age in hours when computed |
horizons[].official_warning_active | Whether an official snow or ice warning is in force |
horizons[].rules_fired[] | Every rule that held: event, window, probability, threshold, point |
horizons[].events[] | All event probabilities for the horizon: calibrated p, raw member fraction raw, members, reliability |
standby_thresholds | Thresholds in force for Standby, for colouring charts |
attribution, notice | Licences of the data used, and the decision-support notice with a link to official warnings |
Live example for Oslo on 12 November 2026, shortened to the 0 to 48 h horizon and one entry per list:
{
"municipality_id": "NO-0301",
"computed_at": "2026-11-12T12:00:23Z",
"season_state": {"state": "pre_first_snow", "since": "2026-09-01", "source": "default"},
"first_snow_within": {"3d": 1.0, "7d": 1.0, "14d": 1.0},
"official_warnings": [{"title": "Snow, yellow level, Large parts of Akershus, Oslo, ...", "awareness_level": "2; yellow; Moderate", "url": "https://www.met.no/..."}],
"horizons": [{
"horizon": "h0_48h",
"level": 3,
"action": "dispatch_reroute_inform",
"driving_event": "snow_6h",
"driving_point": "NO-0301:grefsenkollveien",
"probability": 0.4333,
"threshold": 0.4,
"source": "meps",
"model_run_time": "2026-11-12T09:00:00Z",
"data_age_h": 3.0,
"official_warning_active": true,
"rules_fired": [{"event": "snow_6h", "level": 3, "window": "0-24h", "p": 0.4333, "threshold": 0.4, "point": "NO-0301:grefsenkollveien"}],
"events": [{"event": "phase_risk", "window": "0-48h", "p": 0.633, "raw": 0.633, "members": 30, "reliability": "uncalibrated", "point": "NO-0301:grefsenkollveien"}]
}],
"standby_thresholds": {"snow_6h": 0.2, "snow_24h_day": 0.2667},
"notice": {"code": "decision_support_uncalibrated", "official_warnings_url": "https://www.met.no/vaer-og-klima/Ekstremvaervarsler-og-andre-farevarsler"}
}
In plain words: 13 of 30 forecasts give at least 3 cm in 6 hours at Grefsenkollveien within a day, which crosses the lowered pre-season threshold of 0.40, and MET Norway's yellow snow warning is in force, so the level is 3.
6. Events and thresholds
A probability is the share of ensemble forecasts ("members") in which the event happens. For a horizon it is the share of members in which the event happens at least once in that window, at the municipality point where it is highest. Thresholds are per municipality; the values below are Oslo's.
| Event code | Definition (Oslo) | Used for |
|---|---|---|
snow_6h | At least 3 cm of new snow in 6 hours | 0 to 48 h |
snow_24h | At least 5 cm of new snow in 24 hours | All horizons |
first_snow | At least 2 cm of new snow in 24 hours, counted only before the season's first snow | 2 to 15 days |
phase_risk | At least 0.5 mm/h of precipitation while the temperature is between −1 and +1.5 °C | 0 to 48 h |
freeze_after_wet | Rain, then the temperature falls below 0 °C within 6 hours | 0 to 48 h |
Snow depth is computed the same way for every forecast source: temperature is moved to each point's real height (6.5 °C per km), precipitation is split into rain and snow between 0 and +2 °C, and water becomes snow at 10:1 below −2 °C and 7:1 above.
Default rules (a level is reached when any of its rules holds):
| Horizon | Level | Rule |
|---|---|---|
| 0 to 48 h | 3 Act | P(snow_6h in the next 24 h) ≥ 0.60, or an official snow/ice warning is in force |
| 0 to 48 h | 2 Standby | P(snow_6h in the next 24 h) ≥ 0.30, or P(snow_24h) ≥ 0.40 |
| 0 to 48 h | 1 Watch | P(snow_24h) ≥ 0.15, or P(phase_risk) ≥ 0.30, or P(freeze_after_wet) ≥ 0.30 |
| 2 to 7 days | 2 Standby | P(snow_24h) ≥ 0.40 |
| 2 to 7 days | 1 Watch | P(snow_24h) ≥ 0.15, or P(first_snow) ≥ 0.30 before first snow |
| 7 to 15 days | 1 Watch | P(snow_24h) ≥ 0.30, or P(first_snow) ≥ 0.40 before first snow |
Before the season's first snow every probability threshold is multiplied by 2/3, because the first snow does the most damage. The season resets on 1 September. The season_state field says which applies.
7. CAP feed
If your operations centre already ingests CAP (Common Alerting Protocol) warnings, point it at GET /v1/cap/{id}.xml and no code is needed. Each alert carries one <info> block in the national language (nb-NO for Norway) and one in English, with the level and action code as <parameter> values for routing rules.
| CAP element | Value |
|---|---|
status | Exercise throughout the pilot, so no system treats it as a live alert; Actual from Phase 1 |
severity | Level 0 to 2: Minor; level 3: Moderate. Never higher, so it never outranks the met service's own warnings |
urgency | Level 0: Unknown, 1: Future, 2: Expected, 3: Immediate |
certainty | Likely when the driving probability is 0.5 or more, else Possible |
parameter readiness_level | 0 to 3 |
parameter action_code | The 0 to 48 h action code |
web | The municipality's city page |
The alert covers the 0 to 48 h horizon only. Use the JSON endpoint for the longer horizons. Many CAP readers cannot set headers; for those, put the key in the address: /v1/cap/NO-0301.xml?api_key=<your key>, and treat that address as a secret.
8. Polling, caching and freshness
Polling every 15 minutes is enough: levels can only change when new data arrives on this schedule (UTC).
| Data | Updated |
|---|---|
| Official warnings (MetAlerts) | Every 15 min |
| MEPS forecast (0 to 61 h) | Checked every 30 min; a new run lands about every 3 h |
| ECMWF ensemble (to 15 days) | Checked hourly; new runs four times a day |
| Observations (season state) | Hourly, once the pilot's station feed is connected |
Every JSON response has an ETag and Cache-Control: max-age=300 (60 s for /v1/status). Send the ETag back as If-None-Match and an unchanged answer costs a 304 with no body:
curl -H "Authorization: Bearer $SNOWREADY_KEY" -H 'If-None-Match: "8215c486fd84359904e4a69c6ef5ab43"' \
https://snow.benefelds.lv/v1/readiness/NO-0301
Before acting on a level, check data_age_h in the response or /v1/status. A source is flagged degraded when its last success is older than: MEPS 12 h, ECMWF 24 h, warnings 45 min, observations 3 h. When MEPS is stale the 0 to 48 h horizon falls back to ECMWF and says so in source; when nothing is fresh the action is no_data. The first request after a quiet spell can take a few seconds while the service wakes; later requests are fast.
9. Data sources and liability
| Source | Used for | Licence |
|---|---|---|
| MET Norway MEPS ensemble: 30 members, 2.5 km, to about 61 h | 0 to 48 h | CC BY 4.0 |
| ECMWF IFS and AIFS ensembles via Open-Meteo: 51 + 51 members, to 15 days | 2 to 15 days; fallback for 0 to 48 h | CC BY 4.0 |
| MET Norway MetAlerts | Official warnings | CC BY 4.0 |
| MET Norway Frost | Station observations, season state | CC BY 4.0 |
| Kartverket terrain model | Height of each point | CC BY 4.0 |
If you republish our numbers, keep the attribution list from the response with them.
This is decision support, not a warning service. The national met service issues official warnings, and every response and the CAP feed link them. Probabilities in the pilot are raw ensemble shares, not yet calibrated against what actually happened. Treat a level as a prompt to look, not an order to act.
10. Adding your municipality
Adding a municipality is a configuration change, not new code. It is live after its first forecast run, within an hour. The request form covers the first two items; the rest we settle together:
- Municipality id and a contact in roads, operations or transit
- 3 to 10 named points that matter: steep bus routes, depots, main arteries, plus a centre point (latitude and longitude; we look up the heights)
- Your thresholds if they differ from the defaults: cm in 6 h and in 24 h that start ploughing under your contracts
- Which actions your contracts actually trigger at each stage, so the action codes match your work
- Optional, and the most valuable: a log of plough dispatches and route closures this winter, so the pilot can show how often each level was followed by real disruption
Oslo's points are first candidates and still need confirming against Ruter's network: Holmenkollveien, Grefsenkollveien, Ekebergveien, Lambertseter, Romsås and Tonsenhagen, with Rådhuset and Blindern.