Skip to content

Road events

Every request needs an API key; see Authentication.

Three endpoints over one set of data:

GET /incidentsevery current road event
GET /closuresthe subset where a road is actually shut
GET /road-conditionsthe subset carrying a surface condition

British Columbia and Alberta. See Coverage for what that does and does not include.

curl "https://api.unmap.dev/closures?key=$UNMAP_API_KEY&limit=3"
{
  "data": [
    {
      "id": "um:road:ca-bc:drivebc.ca%2FRIDE-101916",
      "source_id": "drivebc.ca/RIDE-101916",
      "jurisdiction": "CA-BC",
      "type": "closure",
      "severity": "major",
      "condition": "closed",
      "description": "Bridge construction at Kicking Horse Drive. Starting Fri Sep 25. All day, every day. Closed for repairs.",
      "roads": [
        { "name": "Other Roads", "direction": "both", "closed": true, "from": "Kicking Horse Drive", "condition": "closed" }
      ],
      "geometry": { "type": "Point", "coordinates": [-116.96, 51.29] },
      "source_updated_at": "2026-09-17T08:06:31-07:00",
      "unmap_updated_at": "2026-09-17T15:55:16.508Z",
      "source": "roads.drivebc-open511"
    }
  ],
  "meta": {
    "source": "Government of British Columbia, DriveBC: DriveBC Open511 events",
    "status": "current",
    "attribution": "Contains information licensed under the Open Government Licence - British Columbia"
  }
}

From the SDK

import { Unmap } from "@unmap/sdk";
const unmap = new Unmap({ key: process.env.UNMAP_API_KEY! });
 
const { data, meta } = await unmap.roads.closures({ jurisdiction: "CA-BC" });
if (meta.unavailable?.length) {
  // A province could not be reached. An empty `data` is NOT the same as "nothing is shut".
  console.warn(meta.unavailable);
}

unmap.roads.incidents(), .closures() and .conditions() take the same query as the endpoints.

Parameters

All three endpoints take the same ones.

ParameterNotes
jurisdictionComma-separated ISO 3166-2. CA-BC and CA-AB today; anything else is a 400 naming what is covered. Omitted means both
bboxwest,south,east,north in WGS84
near + radiuslng,lat and metres, default 25000, max 500000
typeclosure, incident, construction, weather, restriction, ferry, hazard, special_event, unknown
severityminor, major, unknown
statusactive, planned, archived
updated_sinceISO 8601. Events the publisher changed at or after this time
limit1 to 500, default 100

Results are sorted most severe first, then most recently updated.

Four decisions worth knowing about

A closure means the publisher said closed. It is read from the upstream's road state and from nothing else. The event category says why a road is affected, never whether it is shut: on the day this shipped, British Columbia's 16 closures were spread across construction and incident events, and several construction events described brief closures in prose while reporting all lanes open. Those do not appear in /closures.

The publisher's description is returned verbatim. Our categories are a lossy projection of someone else's taxonomy, and the sentence a driver actually needs ("Single Lane Alternating Traffic will only be required for a brief duration during mobilization") is in the prose. If our category and the description disagree, believe the description and tell us.

An unmapped upstream value becomes unknown, never a guess. A feed inventing SNOW_SQUALL surfaces as unknown with its description intact rather than being rounded into weather because the word looks weathery.

/road-conditions is thin, and dry is rare. Open511's road state describes lane availability, not the surface. All lanes being open is not evidence of a dry road, so unmap reports unknown rather than inventing a measurement nobody took.

Freshness, and what happens when a province is down

These are polled live, not ingested on a schedule. A road event that is an hour old is worthless. British Columbia is fetched per request behind a 60-second edge cache. Alberta is polled every five minutes instead, because its API key allows ten calls per minute for all of unmap, so customer traffic must not reach it. The response says how old each Alberta feed is:

{ "meta": { "polled": [{ "jurisdiction": "CA-AB", "feed": "events", "polled_at": "2026-10-09T17:05:00.000Z", "age_seconds": 140, "max_age_seconds": 900 }] } }

An Alberta feed that has not been polled for 15 minutes is not served. It is listed in meta.unavailable instead.

meta.status is computed from what was just fetched: current normally, stale when the upstream is answering but nothing in it has moved for longer than the feed's window (an hour for British Columbia, six for the slower-moving Alberta feed, the shorter of the two when both are asked for), which usually means a problem at their end rather than an unusually quiet province.

When a jurisdiction cannot be reached, the response says so rather than returning an empty list:

{
  "data": [],
  "meta": {
    "status": "partial",
    "unavailable": [{ "jurisdiction": "CA-BC", "reason": "upstream timed out" }]
  }
}

That distinction matters most on /closures, where an empty list reads as "nothing is shut" to someone deciding whether to drive. If every requested jurisdiction fails, the status code is 503.

Coverage

Two provinces of thirteen.

British Columbia (DriveBC, Open511) and Alberta (511 Alberta) are covered. Alberta needs an API key. If the key is ever missing, the jurisdiction is skipped with a stated reason rather than requested without one, and the response says so:

{ "meta": { "status": "partial", "unavailable": [{ "jurisdiction": "CA-AB", "reason": "ALBERTA_511_KEY is not configured" }] } }

The two publishers are not alike, and the differences show up in the response:

  • Alberta states closure explicitly. IsFullClosure is a boolean the publisher sets. British Columbia has no such field, so closure there is read from the road state and from nothing else.
  • Alberta reports the road surface; British Columbia does not. Open511's road state describes lane availability, so /road-conditions is thin for BC by design. Alberta publishes a separate winter-roads feed with actual surface conditions, which is what makes that endpoint answer at all for Alberta.
  • Alberta's severity is usually absent. 270 of 276 events carried none when first measured. We report unknown rather than defaulting to minor, because an unreported province is not a calm one.

Open511 is a specification rather than one province's invention, so another jurisdiction serving it openly is a configuration entry rather than new code. Ontario's closures are fetched every five minutes for the routing restriction layer but are not served here.

Alberta's winter vocabulary is not mapped yet

The condition terms were sampled in September, when 1,228 of 1,237 segments read Bare Dry. Alberta's winter terms were not observable and are therefore not in the mapping: an unmapped value becomes unknown rather than being guessed at from the words in it. This will be re-sampled in winter. Until then /road-conditions under-reports Alberta in exactly the season it matters most, which is a stated gap rather than a silent one.

Other limits:

  • Provincial highways only. Municipal streets are in neither feed.
  • British Columbia caps at 500 events, unpaged. It carried 286 when measured, so the cap is not currently binding.
  • Bare-dry Alberta segments are dropped from /road-conditions rather than returned, whatever their visibility, unless the publisher reports it Poor (that is reduced_visibility). Twelve hundred records saying the road is fine would bury the handful that are not.
  • Routing does not avoid closures. Nothing in /route is affected yet. Feeding confirmed closures into the routing graph is a separate stage and is not done.

Next steps

  • Coverage for which provinces are covered and what each one is missing.
  • Routing API for what routing does and does not know about closures.
  • Errors for the failure contract.