Quick Start
Get up and running in 3 steps:
1. Get an API key
Sign up at portal.road511.com to get a free API key (14-day trial, 1,000 requests/day).
2. Make your first request
curl -H "X-API-Key: YOUR_API_KEY" \ "https://api.road511.com/api/v1/events?jurisdiction=WA&limit=5"
3. Parse the response
{
"data": [
{
"id": "WA-691391",
"type": "construction",
"severity": "minor",
"title": "Maintenance - US 101",
"description": "Travelers in both directions...",
"latitude": 46.449,
"longitude": -123.872,
"jurisdiction": "WA",
"start_time": "2026-03-23T07:00:00Z"
}
],
"total": 90,
"limit": 5,
"has_more": true
}OpenAPI Spec
The full machine-readable API spec — covering only the data endpoints (events, features, map, analytics, truck corridor, fuel prices, metadata). Use it to generate clients, import into Postman or Insomnia, or browse the interactive reference.
Authentication
All API requests require an API key. Pass it via the X-API-Key header (recommended) or the api_key query parameter.
curl -H "X-API-Key: sk_live_abc123" https://api.road511.com/api/v1/events
curl "https://api.road511.com/api/v1/events?api_key=sk_live_abc123"
Errors & Rate Limits
The API returns standard HTTP status codes. Rate limit headers are included in every response.
| Status | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request (missing/invalid parameters) |
| 401 | Missing or invalid API key |
| 403 | Plan limit exceeded (jurisdiction, feature gate, expired trial) |
| 429 | Rate limit exceeded — retry after Retry-After seconds |
| 500 | Server error — please retry or contact support |
Events
Traffic events include incidents, construction, closures, weather alerts, restrictions, and special events across all active jurisdictions.
GET /api/v1/events
| Parameter | Type | Description |
|---|---|---|
| ids | string | Comma-separated event IDs for batch lookup. When present, all other filters are ignored and pagination is disabled — the response always returns the full set in one page, regardless of status. Bounded by the plan's max_batch_size. |
| jurisdiction | string | Filter by state/province code (e.g., WA, ON) required on Free. Querying a state's primary code auto-expands to all members of its group (state + WZDx + sub-state regional siblings). |
| type | string | Filter by event type: incident, construction, closure, weather, special_event, advisory, restriction |
| severity | string | Filter: minor, moderate, major, critical |
| lat, lng, radius_km | number | Radius search (e.g., lat=47.6&lng=-122.3&radius_km=50) |
| bbox | string | Bounding box: minLng,minLat,maxLng,maxLat |
| limit | int | Results per page (default 100, max per plan) |
| offset | int | Pagination offset |
curl -H "X-API-Key: $KEY" \ "https://api.road511.com/api/v1/events?ids=on-evt-401-001,ga-evt-i75-002,wa-evt-i90-015"
# Events near Seattle, WA curl -H "X-API-Key: $KEY" \ "https://api.road511.com/api/v1/events?jurisdiction=WA&lat=47.6&lng=-122.3&radius_km=100"
import requests resp = requests.get("https://api.road511.com/api/v1/events", params={ "jurisdiction": "WA", "lat": 47.6, "lng": -122.3, "radius_km": 100 }, headers={"X-API-Key": KEY}) events = resp.json()["data"]
const res = await fetch( `https://api.road511.com/api/v1/events?jurisdiction=WA&lat=47.6&lng=-122.3&radius_km=100`, { headers: { "X-API-Key": KEY } } ); const { data, total } = await res.json();
req, _ := http.NewRequest("GET", "https://api.road511.com/api/v1/events?jurisdiction=WA&lat=47.6&lng=-122.3&radius_km=100", nil) req.Header.Set("X-API-Key", key) resp, _ := http.DefaultClient.Do(req)
{
"data": [
{
"id": "on-evt-401-001",
"source_id": "EVT-2026-04521",
"source": "on",
"jurisdiction": "ON",
"type": "incident",
"severity": "major",
"status": "active",
"title": "Multi-vehicle collision on Highway 401 WB near Yonge St",
"description": "Two right lanes blocked. Emergency services on scene.",
"affected_roads": ["Highway 401"],
"direction": "westbound",
"lanes_affected": "2 of 4 lanes blocked",
"start_time": "2026-03-29T14:30:00Z",
"end_time": null,
"estimated_end_time": "2026-03-29T17:00:00Z",
"latitude": 43.65,
"longitude": -79.38,
"road_class": "interstate",
"last_updated": "2026-03-29T14:45:00Z",
"created_at": "2026-03-29T14:30:00Z"
}
],
"total": 42,
"limit": 100,
"offset": 0,
"has_more": false
}GET /api/v1/events/{id}
Single event by ID. Returns the full event with all metadata fields populated.
{
"id": "on-evt-401-001",
"source_id": "EVT-2026-04521",
"source": "on",
"jurisdiction": "ON",
"type": "incident",
"severity": "major",
"status": "active",
"title": "Multi-vehicle collision on Highway 401 WB near Yonge St",
"description": "Two right lanes blocked. Emergency services on scene.",
"location": { "type": "Point", "coordinates": [-79.38, 43.65] },
"affected_roads": ["Highway 401"],
"direction": "westbound",
"lanes_affected": "2 of 4 lanes blocked",
"start_time": "2026-03-29T14:30:00Z",
"end_time": null,
"estimated_start_time": null,
"estimated_end_time": "2026-03-29T17:00:00Z",
"source_created_at": "2026-03-29T14:28:55Z",
"source_updated_at": "2026-03-29T14:45:00Z",
"latitude": 43.65,
"longitude": -79.38,
"road_class": "interstate",
"metadata": { "vehicles_involved": 3 },
"last_updated": "2026-03-29T14:45:00Z",
"created_at": "2026-03-29T14:30:00Z",
"archived_at": null,
"archive_reason": null
}archive_reason is null while the event is active. When an event is archived it carries one of two values:
observed— the upstream feed responded cleanly and the event was no longer in the response, so we retired it. This is the normal case.stale_sweep— the per-poll archival path could not retire the event (upstream responses were empty or errored) and a safety-net sweep eventually forced it to archived after roughlypoll_interval × sweep_factor(default factor 15) with no fresh observation. Treatarchived_aton these rows as a "presumed gone by" timestamp rather than a precise end. A persistentstale_sweepratio on a given source is a signal the upstream feed is unreliable about emitting clearance records.
GET /api/v1/events/geojson Starter+
Same parameters as /events, returns a GeoJSON FeatureCollection. Use it directly with Leaflet, Mapbox, or any GeoJSON-aware tool. Content-Type: application/geo+json.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-79.38, 43.65] },
"properties": {
"id": "on-evt-401-001",
"source": "on",
"jurisdiction": "ON",
"type": "incident",
"severity": "major",
"status": "active",
"title": "Multi-vehicle collision on Highway 401 WB near Yonge St",
"start_time": "2026-03-29T14:30:00Z",
"last_updated": "2026-03-29T14:45:00Z"
}
}
]
}Features
Features are non-event data: cameras, weather stations, rest areas, signs, truck parking, EV charging, and more. All types share the same endpoint — specify a single type, or use group to fan out across all types in a category (e.g. group=trucking returns nine truck-related types in one call).
GET /api/v1/features
| Parameter | Type | Description |
|---|---|---|
| type | string | Feature type (see Feature Types). Either type or group is required. |
| group | string | Feature group id (see Feature Groups). Convenience expansion to all types in the group. |
| jurisdiction | string | Filter by jurisdiction code. Querying a state's primary code auto-expands to all members of its group. |
| active | bool | true to only return active features |
| lat, lng, radius_km | number | Radius search |
| bbox | string | Bounding box: minLng,minLat,maxLng,maxLat |
| limit, offset | int | Pagination |
Every row carries a has_details boolean. When true, the source publishes richer data than the list row carries — call GET /features/{id}/details (or batch via POST /features/details/batch below) to pull it. When false, the list row already has everything the source exposes for that (source, feature_type) pair.
curl -H "X-API-Key: $KEY" \ "https://api.road511.com/api/v1/features?type=cameras&jurisdiction=CO&lat=39.7&lng=-104.9&radius_km=50"
curl -H "X-API-Key: $KEY" \ "https://api.road511.com/api/v1/features?group=trucking&jurisdiction=WA"
The shape of properties varies by feature_type. The samples below show the typical fields for the most common types — pick a tab to see each shape.
{
"data": [
{
"id": "on-cam-401-yonge",
"source": "on",
"jurisdiction": "ON",
"feature_type": "cameras",
"name": "Hwy 401 at Yonge St",
"latitude": 43.6532,
"longitude": -79.3832,
"road_name": "Highway 401",
"direction": "westbound",
"is_active": true,
"properties": {
"url": "https://511on.ca/cameras/cam_401_yonge.jpg",
"video_url": null
},
"last_updated": "2026-03-29T14:50:00Z",
"has_details": true
}
],
"total": 12450, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "ga-wx-i75-atl",
"feature_type": "weather_stations",
"name": "I-75 Atlanta Weather Station",
"latitude": 33.749, "longitude": -84.388,
"road_name": "I-75",
"is_active": true,
"properties": {
"temperature_c": 18.5,
"wind_speed_kmh": 12.3,
"wind_direction": "NW",
"precipitation": "none",
"road_surface": "dry",
"humidity": 62,
"visibility_km": 16.0
},
"last_updated": "2026-03-29T14:45:00Z",
"has_details": false
}
],
"total": 3142, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "tx-dms-i35-512",
"feature_type": "signs",
"name": "I-35 NB DMS at MM 230",
"latitude": 30.2672, "longitude": -97.7431,
"road_name": "I-35",
"direction": "northbound",
"is_active": true,
"properties": {
"message": "CRASH AHEAD / 2 RIGHT LANES CLOSED / EXPECT DELAYS",
"posted_at": "2026-03-29T14:35:00Z",
"sign_type": "DMS"
},
"last_updated": "2026-03-29T14:50:00Z",
"has_details": true
}
],
"total": 4651, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "nrel-198321",
"source": "nrel",
"feature_type": "ev_charging",
"name": "Tesla Supercharger - Mountain View",
"latitude": 37.3861, "longitude": -122.0839,
"is_active": true,
"properties": {
"network": "Tesla",
"connector_types": ["NACS", "CCS"],
"max_kw": 250,
"port_count": 12,
"access": "public",
"pricing": "$0.36/kWh"
},
"last_updated": "2026-03-15T00:00:00Z",
"has_details": true
}
],
"total": 98500, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "US-nbi-WA-531234",
"source": "nbi",
"feature_type": "bridge_clearances",
"name": "I-90 over Snoqualmie River",
"latitude": 47.53, "longitude": -121.82,
"is_active": true,
"properties": {
"min_vertical_clearance_m": 4.93,
"posting_status": "open",
"weight_limit_tons": null,
"year_built": 1969,
"structure_kind": "steel girder",
"nbi_number": "531234"
},
"last_updated": "2026-01-15T00:00:00Z",
"has_details": false
}
],
"total": 45200, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "wa-tp-i90-mp34",
"feature_type": "truck_parking",
"name": "I-90 Truck Parking at MP 34",
"latitude": 47.45, "longitude": -121.55,
"is_active": true,
"properties": {
"capacity": 42,
"available": 8,
"availability_pct": 19,
"amenities": ["restrooms", "vending", "lighting"],
"observed_at": "2026-03-29T14:30:00Z"
},
"last_updated": "2026-03-29T14:50:00Z",
"has_details": false
}
],
"total": 312, "limit": 100, "offset": 0, "has_more": true
}GET /api/v1/features/types
Returns every active feature_type with total and active counts. Cached 2 minutes. Useful for building a UI picker that only shows types you actually have data for.
[
{ "type": "cameras", "count": 12450, "active_count": 11893 },
{ "type": "weather_stations", "count": 3200, "active_count": 3142 },
{ "type": "signs", "count": 4870, "active_count": 4651 },
{ "type": "ev_charging", "count": 98500, "active_count": 98500 },
{ "type": "bridge_clearances", "count": 45200, "active_count": 45200 }
]GET /api/v1/features/groups
Returns the 16-group feature taxonomy with member feature types. Use the id field as the group parameter on /features. Cached for 1 hour. See Feature Groups below for the full table.
[
{
"id": "imagery",
"name": "Imagery",
"description": "Live traffic camera feeds",
"sort_order": 1,
"feature_types": ["cameras"]
},
{
"id": "trucking",
"name": "Trucking & Commercial Vehicles",
"description": "Truck restrictions, routes, parking, inspections",
"sort_order": 8,
"feature_types": [
"truck_restrictions", "weight_restrictions", "bridge_clearances",
"truck_routes", "freight_corridors", "truck_parking",
"truck_rest_areas", "weigh_stations", "inspection_stations"
]
}
]GET /api/v1/features/{id}/details
Lazy-loaded detail for a single feature. Some sources expose only compact fields in the list endpoint; this returns the full properties object plus a detail_available flag and a cache hint (hit / miss / none / poll / error). Use the has_details field on the list response to decide whether the call is worth making.
{
"data": {
"id": "on-cam-401-yonge",
"feature_type": "cameras",
"name": "Hwy 401 at Yonge St",
"latitude": 43.6532, "longitude": -79.3832,
"properties": {
"url": "https://511on.ca/cameras/cam_401_yonge.jpg",
"video_url": "https://511on.ca/cameras/cam_401_yonge.m3u8",
"views": [
{ "name": "Westbound", "url": "https://511on.ca/cameras/cam_401_yonge_wb.jpg" },
{ "name": "Eastbound", "url": "https://511on.ca/cameras/cam_401_yonge_eb.jpg" }
]
}
},
"detail_available": true,
"cache": "miss"
}POST /api/v1/features/details/batch
Resolve detail for many features in one round-trip. Per-id caching is identical to the single-id endpoint (Redis-backed, keyed on the id) — a batch that overlaps with previous calls costs nothing for the overlap. Partial success is preserved: a single failed upstream fetch sets cache: "error" on that entry without failing the whole request; missing ids land in the response with not_found: true. The batch size is bounded by the plan's max_batch_size.
curl -X POST -H "X-API-Key: $KEY" \ -H "Content-Type: application/json" \ -d '{"ids": ["on-cam-401-yonge", "ga-cam-i75-249", "missing-id"]}' \ "https://api.road511.com/api/v1/features/details/batch"
{
"count": 3,
"data": [
{
"id": "on-cam-401-yonge",
"data": { "id": "on-cam-401-yonge", "name": "Hwy 401 at Yonge St", "properties": { "url": "…", "views": ["…"] } },
"detail_available": true,
"cache": "hit"
},
{
"id": "ga-cam-i75-249",
"data": { "id": "ga-cam-i75-249", "name": "I-75 at Exit 249" },
"detail_available": true,
"cache": "miss"
},
{
"id": "missing-id",
"detail_available": false,
"cache": "",
"not_found": true
}
]
}Truck Corridor Pro+
Find all truck restrictions along a route. Draws a buffered corridor between two points and returns every bridge clearance, weight restriction, truck route, and restriction that intersects it. Uses PostGIS spatial queries for accurate great-circle buffering.
GET /api/v1/truck/corridor
| Parameter | Type | Description |
|---|---|---|
| from_lat required | number | Origin latitude |
| from_lng required | number | Origin longitude |
| to_lat required | number | Destination latitude |
| to_lng required | number | Destination longitude |
| buffer_km | number | Buffer distance around corridor (default 5, max 50 km) |
| height | number | Vehicle height in meters — filters clearances below this |
| weight | number | Vehicle weight in metric tons — filters weight limits below this |
| jurisdiction | string | Restrict to one jurisdiction |
| limit | int | Max results (default 500, max 2000) |
bridge_clearances, bridges, weight_restrictions, truck_restrictions, truck_routes, freight_corridors, truck_parkingcurl -H "X-API-Key: $KEY" \ "https://api.road511.com/api/v1/truck/corridor?\ from_lat=47.61&from_lng=-122.33&\ to_lat=47.66&to_lng=-117.43&\ buffer_km=10"
{
"corridor": {
"from": [47.61, -122.33],
"to": [47.66, -117.43],
"buffer_km": 10,
"distance_km": 371.2
},
"data": [
{
"id": "US-nbi-WA-531234",
"feature_type": "bridge_clearances",
"name": "I-90 over Snoqualmie River",
"latitude": 47.53,
"longitude": -121.82,
"properties": {
"posting_status": "open",
"year_built": 1969,
"_distance_km": "2.31"
}
},
{
"id": "WA-cvr-R-WA-90-1",
"feature_type": "truck_restrictions",
"name": "Snoqualmie Pass",
"properties": {
"restriction": "Oversize loads require pilot car",
"_distance_km": "0.45"
}
}
],
"total": 142,
"limit": 500
}GET /api/v1/truck/corridor/geojson Pro+ GeoJSON
Same query, returns GeoJSON FeatureCollection. Use this to render corridor results directly on a Leaflet or Mapbox map.
// Render truck corridor on Leaflet map const url = `https://api.road511.com/api/v1/truck/corridor/geojson` + `?from_lat=47.61&from_lng=-122.33&to_lat=47.66&to_lng=-117.43&buffer_km=10`; fetch(url, { headers: { "X-API-Key": KEY } }) .then(r => r.json()) .then(geojson => { L.geoJSON(geojson, { pointToLayer: (f, ll) => L.circleMarker(ll, { radius: 6 }), onEachFeature: (f, layer) => { layer.bindPopup(`<b>${f.properties.name}</b><br>${f.properties.feature_type}`); } }).addTo(map); });
import requests resp = requests.get("https://api.road511.com/api/v1/truck/corridor", params={ "from_lat": 47.61, "from_lng": -122.33, "to_lat": 47.66, "to_lng": -117.43, "buffer_km": 10 }, headers={"X-API-Key": KEY}) for feat in resp.json()["data"]: if feat["feature_type"] == "bridge_clearances": print(f"Bridge: {feat['name']} ({feat['properties'].get('posting_status', 'unknown')})")
{
"type": "FeatureCollection",
"corridor": {
"from": [47.61, -122.33],
"to": [47.66, -117.43],
"buffer_km": 10,
"distance_km": 371.2
},
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-121.82, 47.53] },
"properties": {
"id": "US-nbi-WA-531234",
"feature_type": "bridge_clearances",
"name": "I-90 over Snoqualmie River",
"min_vertical_clearance_m": 4.93,
"posting_status": "open",
"_distance_km": "2.31"
}
}
]
}Analytics Pro+
Historical analytics and trends across events, features, and source health. All endpoints require Pro or Enterprise plan.
GET /api/v1/analytics/history/{id}
Lifecycle timeline for a single event — created, severity changes, lane changes, end-time changes, archived.
[
{ "id": 1, "event_id": "on-evt-401-001",
"change_type": "created",
"old_value": null, "new_value": "active",
"changed_at": "2026-03-29T14:30:00Z" },
{ "id": 2, "event_id": "on-evt-401-001",
"change_type": "severity_change",
"old_value": "moderate", "new_value": "major",
"changed_at": "2026-03-29T14:35:00Z" }
]GET /api/v1/analytics/changes
Recent change feed across all events. Paginated. Useful for syncing a downstream copy of event state without polling each event individually.
{
"data": [
{ "id": 45, "event_id": "on-evt-401-001",
"change_type": "severity_change",
"old_value": "moderate", "new_value": "major",
"jurisdiction": "ON",
"event_title": "Multi-vehicle collision on Highway 401 WB",
"changed_at": "2026-03-29T14:35:00Z" }
],
"total": 128, "limit": 100, "offset": 0, "has_more": true
}GET /api/v1/analytics/clearance
P50 / P95 / average clearance times by jurisdiction and event type. Computed from start_time → archived_at.
[
{ "jurisdiction": "ON", "event_type": "incident",
"total_events": 320,
"p50_minutes": 45.2, "p95_minutes": 180.5, "avg_minutes": 62.8 },
{ "jurisdiction": "GA", "event_type": "construction",
"total_events": 150,
"p50_minutes": 4320.0, "p95_minutes": 21600.0, "avg_minutes": 7200.0 }
]GET /api/v1/analytics/corridors
Per-road event frequency and average clearance time. Best signal for "which corridor is unreliable today."
[
{ "road": "Highway 401", "jurisdiction": "ON",
"total_events": 245,
"avg_clearance_minutes": 52.3,
"incident_count": 180, "construction_count": 65 }
]GET /api/v1/analytics/trends
Daily event totals with type breakdown.
[
{ "period": "2026-03-29", "total": 142,
"by_type": { "incident": 58, "construction": 62, "closure": 12, "weather": 10 } },
{ "period": "2026-03-28", "total": 128,
"by_type": { "incident": 45, "construction": 60, "closure": 15, "weather": 8 } }
]GET /api/v1/analytics/hotspots
Geographic clusters of events with the dominant type and road per cluster.
[
{ "cluster_id": 1,
"latitude": 43.6510, "longitude": -79.3470,
"event_count": 28, "radius_km": 2.5,
"top_type": "incident", "top_road": "Highway 401" }
]GET /api/v1/analytics/weather
Historical weather station readings — the data behind the weather-incident correlation features.
[
{ "station_id": "ga-wx-i75-atl",
"name": "I-75 Atlanta Weather Station",
"latitude": 33.749, "longitude": -84.388,
"temperature_c": 18.5, "wind_speed_kmh": 12.3, "wind_direction": "NW",
"precipitation": "none", "road_surface": "dry",
"humidity": 62, "visibility_km": 16.0,
"recorded_at": "2026-03-29T14:45:00Z" }
]Other analytics endpoints
Same shape as the examples above — see the interactive reference for full schemas.
| Endpoint | Description |
|---|---|
| /analytics/feature-history | Feature lifecycle changes (created, deactivated, reactivated). |
| /analytics/source-health | Per-source fetch success rate, last success, last error. |
| /analytics/fetch-log | Recent fetch attempts with duration and item count. |
| /analytics/event-weather/{id} | Weather snapshot recorded at event creation time (PostGIS nearest-station lookup). |
| /analytics/weather-correlation | Aggregated event-vs-weather correlation rollup. |
Metadata & Status
Lightweight catalogue and status endpoints. /health and /status are public (no API key); the rest are gated by API key but not by plan tier.
GET /api/v1/jurisdictions
Active jurisdictions with code, name, country, and scope. By default only real geographic jurisdictions are returned (scope=state) — cross-cutting federal sources and sub-state regional feeds are filtered out so customer pickers stay clean. Pass ?scope=federal for sources like EIA, FHWA, WZDX_NPS, ?scope=regional for 511SF / WZDX_AUSTIN / WZDX_MCDOT / WZDX_STC_MO, or ?scope=all to return everything.
When group_id is non-null the jurisdiction belongs to a group (see /api/v1/jurisdictions/groups below) — a state's primary feed bundled with its WZDX_* / WZDX_CWZ_* / sub-state siblings. Querying ?jurisdiction=<primary> on /events or /features auto-expands to all members; query a non-primary code (e.g. WZDX_CA) or a comma-list to opt out.
[
{ "code": "ON", "name": "Ontario", "country": "CA", "scope": "state", "group_id": null, "is_active": true },
{ "code": "WA", "name": "Washington", "country": "US", "scope": "state", "group_id": "us-wa", "is_active": true },
{ "code": "CA", "name": "California", "country": "US", "scope": "state", "group_id": "us-ca", "is_active": true }
]GET /api/v1/jurisdictions/groups
Canonical bundles for states whose feed is split across multiple codes (state + WZDx + optional CWZ + optional sub-state regional). The primary_jurisdiction_code is the one to use on /events and /features — auto-expansion transparently fans the query out to every members entry.
[
{ "id": "us-ca", "name": "California",
"primary_jurisdiction_code": "CA",
"members": ["CA", "WZDX_CA", "511SF"] },
{ "id": "us-co", "name": "Colorado",
"primary_jurisdiction_code": "CO",
"members": ["CO", "WZDX_CO", "WZDX_CWZ_CO"] },
{ "id": "us-tx", "name": "Texas",
"primary_jurisdiction_code": "TX",
"members": ["TX", "WZDX_TX", "WZDX_AUSTIN"] }
]GET /api/v1/stats/summary
Rich rollup stats — used by the marketing pages for live counters. Heavier query than /stats; cached 60s.
{
"jurisdictions": { "total": 47, "active": 45, "by_country": { "US": 35, "CA": 12 } },
"events": {
"active": 4523,
"by_type": { "incident": 1230, "construction": 2150, "closure": 380 },
"by_severity": { "minor": 1520, "moderate": 2100, "major": 780, "critical": 123 }
},
"features": { "total": 176800, "by_type": { "cameras": 12450, "ev_charging": 98500 } },
"last_sync": "2026-03-29T14:55:00Z",
"uptime": "72h15m"
}GET /api/v1/fuel-prices
Weekly US state-level (EIA) and monthly Canadian province-level (StatCan) fuel prices. Use latest=true for the most recent observation per jurisdiction + fuel_type.
{
"data": [
{ "jurisdiction": "CA", "country": "US", "fuel_type": "regular",
"price": 4.85, "currency": "USD", "unit": "gallon",
"observed_at": "2026-05-05", "source": "EIA" },
{ "jurisdiction": "ON", "country": "CA", "fuel_type": "regular",
"price": 1.62, "currency": "CAD", "unit": "litre",
"observed_at": "2026-04-01", "source": "StatCan" }
],
"total": 312, "limit": 100, "offset": 0, "has_more": true
}GET /api/v1/status
Public status snapshot — safe for an unauthenticated status page or external uptime monitor. Aggregate counts plus per-jurisdiction breakdown with 7-day uptime. 60s cache.
{
"overall": "operational",
"generated_at": "2026-05-08T14:00:00Z",
"worker": { "last_seen": "2026-05-08T13:59:50Z", "staleness_seconds": 10, "healthy": true },
"total_resources": 312, "healthy": 295, "warning": 12, "critical": 5,
"jurisdictions": [
{ "code": "ON", "name": "Ontario", "country": "CA",
"status": "operational", "total_resources": 8, "open_circuits": 0,
"uptime_7day_percent": 99.8 }
]
}Webhooks Pro+
Subscribe to real-time event changes. When a traffic event is created, changes severity or status, or is archived, the API sends an HMAC-SHA256 signed POST to your endpoint. Pro and Enterprise customers configure subscriptions in their portal — portal.road511.com/webhooks. The auto-generated secret is shown once on creation; copy it then.
Each webhook is scoped by jurisdiction (mandatory, multi-select — pick one or more), and optionally by event type and severity (also multi-select). Filters AND-combine across dimensions; within a dimension, the webhook fires when the event matches any selected value.
Event types
| Event | Fires when |
|---|---|
event.created | A new traffic event arrives from a 511 source. |
event.severity_change | An existing event's severity changes (e.g. moderate → major). |
event.status_change | Status flips (e.g. active → archived). |
event.archived | The event has ended. |
test | Sent only by the portal "Test" button — fires immediately, never recorded to delivery history. |
Immediate payload (default)
One POST per change. Use this when you need the lowest possible latency and your receiver can handle bursts.
{
"event": "event.severity_change",
"timestamp": "2026-03-29T14:35:00Z",
"data": {
"event_id": "on-evt-401-001",
"jurisdiction": "ON",
"type": "incident",
"severity": "major",
"road_name": "Highway 401",
"changes": {
"old_severity": "moderate",
"new_severity": "major"
}
}
}Batched payload
Set batch_window_seconds (1–300) when creating the webhook to collect changes for that window and deliver them as a single POST. Reduces HTTP volume on chatty receivers (Slack, Teams, Lambda) at the cost of up to window_seconds of added latency.
{
"batch": {
"window_seconds": 60,
"started_at": "2026-03-29T14:30:00Z",
"ended_at": "2026-03-29T14:31:00Z",
"event_count": 2
},
"events": [
{
"event": "event.created",
"timestamp": "2026-03-29T14:30:15Z",
"data": { "event_id": "on-evt-401-002", "jurisdiction": "ON", "type": "construction", "severity": "minor", "road_name": "QEW" }
},
{
"event": "event.severity_change",
"timestamp": "2026-03-29T14:30:42Z",
"data": { "event_id": "on-evt-401-001", "jurisdiction": "ON", "type": "incident", "severity": "major", "road_name": "Highway 401", "changes": { "old_severity": "moderate", "new_severity": "major" } }
}
]
}Receivers branch on the presence of the batch key:
if ("batch" in body) { // batched: iterate body.events for (const ev of body.events) handle(ev); } else { // immediate: handle the body itself handle(body); }
A single batch is capped at 100 events; longer queues are split across multiple POSTs in the same window.
Verifying the signature
Every delivery includes an X-Webhook-Signature header containing the lowercase-hex HMAC-SHA256 of the request body, keyed with your webhook secret. Reject requests where the signature doesn't match.
import crypto from "crypto"; app.post("/road511-webhook", (req, res) => { const sig = req.headers["x-webhook-signature"]; const expected = crypto .createHmac("sha256", process.env.WEBHOOK_SECRET) .update(req.rawBody) // raw bytes, NOT JSON.stringify(req.body) .digest("hex"); if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) { return res.status(401).end(); } // process req.body… res.status(200).end(); });
Reliability
Failed deliveries are retried up to 3 times with exponential backoff (10s, 30s, 90s). After 10 consecutive failures the subscription is auto-disabled — re-enable it from the portal once your endpoint is healthy. Use the Test button on each row in the portal to fire a dummy test event right now and confirm reachability before depending on a webhook in production.
Truck Routing Pro+
A truck-aware route from origin to destination — backed by HERE for the routing geometry, then enriched with every nearby hazard our database knows about: live incidents, planned construction, bridge clearances filtered for the requested truck's height, public at-grade rail crossings (with 49 CFR 392.10 hazmat-stop awareness), truck restrictions, weigh stations, weather along the corridor, and active alerts.
POST /api/v1/routing/truck
| Field | Type | Description |
|---|---|---|
| origin required | object | { "lat": ..., "lng": ... } |
| destination required | object | { "lat": ..., "lng": ... } |
| truck required | object | Vehicle block — profile (tractor, straight_truck, van), weight_t, height_m, optional length_m, width_m, axles, hazmat |
| alternatives | int | Number of alternate routes (0–3, default 0) |
| avoid | array | Optional: tolls, ferries, tunnels |
| enrichment | object | Tunes the post-HERE warnings filter — none of these fields are sent to HERE. Subkeys: exclude_types (drop e.g. weigh_stations), min_severity (info/warning/critical), buffer_m (10–1000, default 100), clearance_pad_m (0–1, default 0.15), skip_temporal_filter (planning-ahead), max_distance_m (only warn within first N m). |
| cargo | object | Cargo metadata stored on the saved route. Subkey: hazmat_class (DOT placard, e.g. "1.3D"). |
| truck.permit_number | string | Oversize/overweight permit ID. Echoed in the response; future enrichment will soften permit-valid corridor warnings. |
| customer_route_id | string | Opaque correlation key (≤128 chars). Echoed verbatim. |
| tags | array | Fleet/lane analytics labels (max 16 entries, each ≤64 chars). Echoed and stored. |
curl -X POST -H "X-API-Key: $KEY" \ -H "Content-Type: application/json" \ https://api.road511.com/api/v1/routing/truck \ -d '{ "origin": { "lat": 47.6062, "lng": -122.3321 }, "destination": { "lat": 47.6588, "lng": -117.4260 }, "truck": { "profile": "tractor", "weight_t": 20, "height_m": 4.0 }, "enrichment": { "exclude_types": ["weigh_stations"], "min_severity": "warning", "clearance_pad_m": 0.3 }, "customer_route_id": "my-route-42", "tags": ["fleet-east", "lane-LA-NYC"] }'
{
"route_id": "rt_01HXYZ4K8Q2V7R9N3M5P6T8W0E",
"expires_at": "2026-05-18T14:32:00Z",
"routes": [
{
"summary": {
"distance_km": 446.8,
"duration_min": 282,
"toll_cost_usd": 0.00
},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.3321, 47.6062],
[-121.8200, 47.5300],
[-117.4260, 47.6588]
]
},
"warnings": [
{
"type": "bridge_clearance",
"severity": "critical",
"feature_id": "US-nbi-WA-531234",
"name": "I-90 over Snoqualmie River",
"latitude": 47.5300,
"longitude": -121.8200,
"clearance_m": 4.0,
"truck_height_m": 4.2,
"message": "Posted vertical clearance is below the requested truck height."
}
]
}
]
}Saved routes
Every call is auto-saved for 30 minutes under its route_id. Refetch via GET /api/v1/routing/truck/saved/{id} at any point inside that window to get re-evaluated warnings against the current live event/feature state — refetches do not consume routing quota. To keep a route beyond the 30-minute TTL, call POST /api/v1/routing/truck/saved/{id}/persist; persisted routes count against your plan's saved-route slot limit.
Full request/response schema (every truck profile field, every warning type, error envelopes): see the interactive reference at /reference.html.
Feature Groups
57 feature types organized into 16 groups. Pass the id as the group parameter on /features to fan out across all members in a single call.
| Group ID | Name | Members |
|---|---|---|
| imagery | Imagery | cameras |
| weather | Weather & Environment | weather_stations, regional_weather, weather_forecasts, weather_alerts, wind_warnings |
| road_conditions | Road & Surface Conditions | road_conditions, ice_roads, snow_plans |
| traffic_performance | Traffic Performance | traffic_segments, speed_data, travel_times, express_lanes, hov_lanes |
| planned_events | Planned Events & Closures | workzones, future_construction, future_roadwork, special_events, seasonal_loads |
| alerts_advisories | Alerts & Advisories | alerts, emergency_alerts, advisories, general_info |
| wildfires | Wildfires | wildfires, wildfire_incidents, wildfire_perimeters |
| trucking | Trucking & Commercial Vehicles | truck_restrictions, weight_restrictions, bridge_clearances, truck_routes, freight_corridors, truck_parking, truck_rest_areas, weigh_stations, inspection_stations |
| traveler_services | Traveler Services & POIs | rest_areas, service_centres, info_centres, visitor_locations, parks, communities, carpool_lots, airports |
| fuel_charging | Fuel & Charging | ev_charging, alt_fuel_stations |
| borders | Borders & Crossings | border_crossings, ports_of_entry |
| ferries | Ferries | ferries, coastal_ferries |
| transit | Public Transit | transit_hubs, transit_stops |
| tolls | Tolls | tolls |
| static_infrastructure | Static Infrastructure & Enforcement | bridges, signs, roundabouts, speed_cameras |
| operations | Operations | service_vehicles |
Feature Types
Pass any of these as the type parameter to /features.
Event Types
| Type | Severity Range | Description |
|---|---|---|
| incident | moderate–critical | Crashes, disabled vehicles, hazards |
| construction | minor–moderate | Road work, maintenance, paving |
| closure | major–critical | Full road closures |
| weather | moderate–critical | Weather-related road impacts |
| special_event | minor–moderate | Planned events (parades, races) |
| advisory | minor–moderate | Travel advisories, warnings |
| restriction | minor–moderate | Weight, height, speed restrictions |
Plans & Limits
| Limit | Free (14-day trial) | Starter ($29/mo) | Pro ($99/mo) | Enterprise |
|---|---|---|---|---|
| RPM | 60 | 300 | 1,000 | 5,000 |
| Daily requests | 1,000 | 50,000 | 500,000 | Unlimited |
| API keys | 1 | 3 | 10 | 50 |
| Jurisdictions/req | 2 | 10 | Unlimited | Unlimited |
| Results/page | 100 | 500 | 1,000 | Unlimited |
| GeoJSON export | No | Yes | Yes | Yes |
| Analytics | No | No | Yes | Yes |
| Truck corridor | No | No | Yes | Yes |
| Data delay | 15 min | Real-time | Real-time | Real-time |