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
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.road511.com/api/v1/events?jurisdiction=WA&limit=5"

3. Parse the response

Response JSON
{
  "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.

Header (recommended)
curl -H "X-API-Key: sk_live_abc123" https://api.road511.com/api/v1/events
Query parameter
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.

StatusMeaning
200Success
400Bad request (missing/invalid parameters)
401Missing or invalid API key
403Plan limit exceeded (jurisdiction, feature gate, expired trial)
429Rate limit exceeded — retry after Retry-After seconds
500Server 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

ParameterTypeDescription
idsstringComma-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.
jurisdictionstringFilter 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).
typestringFilter by event type: incident, construction, closure, weather, special_event, advisory, restriction
severitystringFilter: minor, moderate, major, critical
lat, lng, radius_kmnumberRadius search (e.g., lat=47.6&lng=-122.3&radius_km=50)
bboxstringBounding box: minLng,minLat,maxLng,maxLat
limitintResults per page (default 100, max per plan)
offsetintPagination offset
Batch lookup — fetch known event IDs in one request
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.

Response
{
  "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 roughly poll_interval × sweep_factor (default factor 15) with no fresh observation. Treat archived_at on these rows as a "presumed gone by" timestamp rather than a precise end. A persistent stale_sweep ratio 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.

Response
{
  "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

ParameterTypeDescription
typestringFeature type (see Feature Types). Either type or group is required.
groupstringFeature group id (see Feature Groups). Convenience expansion to all types in the group.
jurisdictionstringFilter by jurisdiction code. Querying a state's primary code auto-expands to all members of its group.
activebooltrue to only return active features
lat, lng, radius_kmnumberRadius search
bboxstringBounding box: minLng,minLat,maxLng,maxLat
limit, offsetintPagination

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.

Example: cameras near Denver
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"
Example: all trucking layers in Washington (one call)
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.

Response
[
  { "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.

Response (truncated to 2 of 16 groups)
[
  {
    "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.

Response
{
  "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.

Request
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"
Response (parallel array, one entry per requested id, in order)
{
  "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

ParameterTypeDescription
from_lat requirednumberOrigin latitude
from_lng requirednumberOrigin longitude
to_lat requirednumberDestination latitude
to_lng requirednumberDestination longitude
buffer_kmnumberBuffer distance around corridor (default 5, max 50 km)
heightnumberVehicle height in meters — filters clearances below this
weightnumberVehicle weight in metric tons — filters weight limits below this
jurisdictionstringRestrict to one jurisdiction
limitintMax results (default 500, max 2000)
Searches across: bridge_clearances, bridges, weight_restrictions, truck_restrictions, truck_routes, freight_corridors, truck_parking
Example: I-90 Seattle to Spokane
curl -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"
Response
{
  "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')})")
Response (FeatureCollection)
{
  "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.

Response
[
  { "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.

Response
{
  "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_timearchived_at.

Response
[
  { "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."

Response
[
  { "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.

Response
[
  { "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.

Response
[
  { "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.

Response
[
  { "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.

EndpointDescription
/analytics/feature-historyFeature lifecycle changes (created, deactivated, reactivated).
/analytics/source-healthPer-source fetch success rate, last success, last error.
/analytics/fetch-logRecent fetch attempts with duration and item count.
/analytics/event-weather/{id}Weather snapshot recorded at event creation time (PostGIS nearest-station lookup).
/analytics/weather-correlationAggregated 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.

Response
[
  { "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.

Response
[
  { "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.

Response
{
  "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.

Response
{
  "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.

Response (truncated)
{
  "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

EventFires when
event.createdA new traffic event arrives from a 511 source.
event.severity_changeAn existing event's severity changes (e.g. moderatemajor).
event.status_changeStatus flips (e.g. activearchived).
event.archivedThe event has ended.
testSent 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.

Webhook POST body
{
  "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.

Batched POST body (batch_window_seconds=60)
{
  "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:

Receiver pseudo-code
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.

Node.js receiver
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

FieldTypeDescription
origin requiredobject{ "lat": ..., "lng": ... }
destination requiredobject{ "lat": ..., "lng": ... }
truck requiredobjectVehicle block — profile (tractor, straight_truck, van), weight_t, height_m, optional length_m, width_m, axles, hazmat
alternativesintNumber of alternate routes (0–3, default 0)
avoidarrayOptional: tolls, ferries, tunnels
enrichmentobjectTunes 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).
cargoobjectCargo metadata stored on the saved route. Subkey: hazmat_class (DOT placard, e.g. "1.3D").
truck.permit_numberstringOversize/overweight permit ID. Echoed in the response; future enrichment will soften permit-valid corridor warnings.
customer_route_idstringOpaque correlation key (≤128 chars). Echoed verbatim.
tagsarrayFleet/lane analytics labels (max 16 entries, each ≤64 chars). Echoed and stored.
Each successful call counts against your monthly routing quota — the subscription bucket is consumed first, then any top-up packs in FIFO order. Saved-route refetches (see below) do not consume quota.
Example: Seattle → Spokane, 20 t tractor, 4.0 m tall
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"]
  }'
Response
{
  "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 IDNameMembers
imageryImagerycameras
weatherWeather & Environmentweather_stations, regional_weather, weather_forecasts, weather_alerts, wind_warnings
road_conditionsRoad & Surface Conditionsroad_conditions, ice_roads, snow_plans
traffic_performanceTraffic Performancetraffic_segments, speed_data, travel_times, express_lanes, hov_lanes
planned_eventsPlanned Events & Closuresworkzones, future_construction, future_roadwork, special_events, seasonal_loads
alerts_advisoriesAlerts & Advisoriesalerts, emergency_alerts, advisories, general_info
wildfiresWildfireswildfires, wildfire_incidents, wildfire_perimeters
truckingTrucking & Commercial Vehiclestruck_restrictions, weight_restrictions, bridge_clearances, truck_routes, freight_corridors, truck_parking, truck_rest_areas, weigh_stations, inspection_stations
traveler_servicesTraveler Services & POIsrest_areas, service_centres, info_centres, visitor_locations, parks, communities, carpool_lots, airports
fuel_chargingFuel & Chargingev_charging, alt_fuel_stations
bordersBorders & Crossingsborder_crossings, ports_of_entry
ferriesFerriesferries, coastal_ferries
transitPublic Transittransit_hubs, transit_stops
tollsTollstolls
static_infrastructureStatic Infrastructure & Enforcementbridges, signs, roundabouts, speed_cameras
operationsOperationsservice_vehicles

Feature Types

Pass any of these as the type parameter to /features.

camerasTraffic camera snapshots with image URLs
weather_stationsRWIS sensors: temperature, wind, humidity, road condition
rest_areasRest stops with amenities (restrooms, wifi, RV dump)
signsDynamic message signs with current text
road_conditionsSegment-level road surface conditions
ev_chargingEV charging stations (NREL data)
bridge_clearancesBridge vertical clearance, load ratings, posting status
truck_restrictionsHeight/weight limits, hazmat zones, permits
weight_restrictionsSeasonal/permanent weight limits (polylines)
truck_routesSTAA designated truck routes (polylines)
freight_corridorsNHFN freight network corridors (polylines)
truck_parkingTruck parking lots with capacity and amenities
weigh_stationsWeigh station locations and status
traffic_segmentsTraffic flow sensor data (speed/volume)
service_vehiclesSnow plows, DOT service vehicles (seasonal)
ferriesFerry terminals and route status

Event Types

TypeSeverity RangeDescription
incidentmoderate–criticalCrashes, disabled vehicles, hazards
constructionminor–moderateRoad work, maintenance, paving
closuremajor–criticalFull road closures
weathermoderate–criticalWeather-related road impacts
special_eventminor–moderatePlanned events (parades, races)
advisoryminor–moderateTravel advisories, warnings
restrictionminor–moderateWeight, height, speed restrictions

Plans & Limits

LimitFree (14-day trial)Starter ($29/mo)Pro ($99/mo)Enterprise
RPM603001,0005,000
Daily requests1,00050,000500,000Unlimited
API keys131050
Jurisdictions/req210UnlimitedUnlimited
Results/page1005001,000Unlimited
GeoJSON exportNoYesYesYes
AnalyticsNoNoYesYes
Truck corridorNoNoYesYes
Data delay15 minReal-timeReal-timeReal-time

Get Free API Key