← Back to all posts
DMS Signs API

Real-Time DMS Sign Messages API: What's on the Highway Signs Right Now

April 30, 2026 · 6 min read

Dynamic Message Signs — the big electronic boards over the highway — are the fastest traveler-information channel a state DOT controls. When a crash happens, the sign two miles upstream often updates before the incident appears in the public 511 feed. Amber alerts, travel times, lane closures, weather warnings, evacuation routes — it all flows through DMS first.

The catch: every state publishes its DMS inventory differently — from clean JSON APIs to multi-district web endpoints to obfuscated binary feeds. Some publish a clean “current message” field; others give you multiple message phases you have to reassemble into the line that’s actually displayed.

Road511 normalizes all of it into one endpoint. One API call returns the live text on every DMS across 45 jurisdictions — 12,668 active signs as of 14 August 2026. And, as far as we have been able to find, we’re the only public API that also keeps a historical archive of message changes; if you know of another, we’d genuinely like to hear about it.

What You Get

curl "https://api.road511.com/api/v1/features?type=signs&jurisdiction=GA&limit=10" \
  -H "X-API-Key: your_key"
{
  "data": [
    {
      "id": "GA-sign-GDOT--5467",
      "jurisdiction": "GA",
      "feature_type": "signs",
      "name": "GDOT-DMS-0204",
      "properties": {
        "message": "CRASH I-20 WB\nEXIT TO CANDLER RD\nLEFT LANE BLOCKED",
        "direction": "Southbound"
      }
    }
  ],
  "total": 212,
  "has_more": true
}

That is a real record, shape and all. Note the name: it is GDOT-DMS-0204, the agency’s own device identifier, not a friendly “I-85 NB at Clairmont Rd” string. Most DOTs publish the sign’s cabinet ID rather than a human-readable location, so if your UI needs to tell a driver where a sign is, plan to reverse-geocode the row’s latitude/longitude yourself — don’t plan on name.

The message field is the exact text on the sign, with the sign’s own line breaks preserved as newlines (\n). A dark or default sign comes back with an empty message or a source sentinel such as NO_MESSAGE. What else rides in properties varies by DOT, and less generously than you might hope: of the 12,668 active signs, 3,462 (27%) carry a direction and 4,164 (33%) a sign_type. Only message is effectively universal. Write your client so that every property except message may be absent.

One field that is not there, and that developers keep asking for: there is currently no per-sign “last changed” timestamp. No active sign in the inventory carries one today, because none of the upstream feeds we ingest stamps its own message-change time in a form we can pass through. The row’s last_updated tells you when we last read the feed, not when the operator last retyped the board. To know when a message actually changed, diff the archive (see below) or diff successive polls yourself.

Filter to the Signs That Are Saying Something

Combine type=signs with a bounding box to pull a metro area in one call:

curl "https://api.road511.com/api/v1/features?type=signs\
&bbox=-84.5,33.6,-84.2,33.9\
&limit=200" \
  -H "X-API-Key: your_key"

That box is metro Atlanta, and on 14 August 2026 it returned 70 signs — all 70 of them carrying text. Which is worth dwelling on, because the obvious first filter does nothing.

Dark boards are the exception, not the rule. Across the whole active inventory, 9,152 of 12,668 signs — 72% — are displaying non-empty text right now; only 3,407 are blank and a mere 109 return the NO_MESSAGE sentinel. So dropping empties and sentinels leaves you with roughly seven signs in ten, not a handful. If you have budgeted for “a tiny percentage,” re-budget.

The real filtering problem is a different one: most of that text is routine. Boards spend their lives showing travel times, posted speeds, and safety campaigns, and the incident messages hide among them. Here are four real Georgia boards from that same pull:

GDOT-DMS-0401  "I-285 SPEEDS:\nWEST\n64 MPH"
GDOT-DMS-0405  "TRAVEL TIME TO\nHAYNES BR: 3-5 MIN\nWINDWARD: 6-8 MIN"
GDOT-DMS-0204  "CRASH I-20 WB\nEXIT TO CANDLER RD\nLEFT LANE BLOCKED"
GDOT-DMS-0108  "ROADWORK 316 EB\nAT CEDARS CREEK\nRIGHT LN BLKD"

Two are wallpaper, two are events. No flag in the payload separates them — the DOT doesn’t publish one, so we don’t invent one. Classification is a keyword pass you write over message, and it is genuinely the interesting part of the integration: match on CRASH, CLOSED, BLOCKED, ACCIDENT, AMBER, SILVER, EVACUAT, and exclude the boards whose text is dominated by TRAVEL TIME, SPEEDS, and MPH. Build that list against your own corridors, and expect to keep tuning it — each DOT abbreviates differently, as RIGHT LN BLKD above should warn you.

GeoJSON for Map Overlays

Same query, GeoJSON shape, ready to drop onto Leaflet, Mapbox, or QGIS:

curl "https://api.road511.com/api/v1/features/geojson?type=signs\
&bbox=-84.5,33.6,-84.2,33.9" \
  -H "X-API-Key: your_key"
const res = await fetch(
  'https://api.road511.com/api/v1/features/geojson'
  + '?type=signs&bbox=-84.5,33.6,-84.2,33.9',
  { headers: { 'X-API-Key': 'your_key' } }
);
const geojson = await res.json();

L.geoJSON(geojson, {
  filter: (f) => f.properties.message && f.properties.message !== 'NO_MESSAGE',
  pointToLayer: (f, latlng) => L.circleMarker(latlng, {
    radius: 7, color: '#f59e0b', fillOpacity: 0.85
  }),
  onEachFeature: (f, layer) => {
    const lines = (f.properties.message || '').split('\n').join('<br>');
    layer.bindPopup(`
      <strong>${f.properties.name}</strong>
      <pre style="font-family: monospace; font-size: 12px">${lines}</pre>
    `);
  }
}).addTo(map);

Why Aggregating This Matters

DMS messages are a leading indicator. The traffic operator who runs the sign also runs the 511 feed, but the sign update happens first — sometimes ten or fifteen minutes before the event appears in the public incident list. Cross-referencing live sign text with traffic events gives you several things you can’t get from either feed alone:

Coverage

DMS data lives in the signs feature type. We pull from every state and province that exposes a public sign feed — 45 jurisdictions, 12,668 active signs. The counts below are active records on 14 August 2026; hardware comes and goes, so read them as this month’s shape rather than a fixed number.

Jurisdiction Active signs Jurisdiction Active signs
Massachusetts (MA)2,192Maryland (MD)206
Texas (TX)1,290Maine (ME)155
Pennsylvania (PA)1,198British Columbia (BC)148
Florida (FL)1,137Oregon (OR)143
Michigan (MI)567Wisconsin (WI)141
New York (NY)486Iowa (IA)131
Virginia (VA)446Oklahoma (OK)115
Nevada (NV)405Wyoming (WY)102
North Carolina (NC)400Minnesota (MN)97
Missouri (MO)331North Dakota (ND)91
Illinois (IL)294Vermont (VT)91
California (CA)275Kentucky (KY)88
Indiana (IN)246New Hampshire (NH)82
Colorado (CO)245Montana (MT)80
Utah (UT)221Connecticut (CT)79
Arizona (AZ)221Tennessee (TN)72
Kansas (KS)214Louisiana (LA)67
Georgia (GA)21210 more jurisdictions11–56 each

The tail is long and worth knowing about: Nebraska, Alabama, Alberta, Ohio, Mississippi, South Carolina, Idaho, Delaware, Yukon and Alaska each publish between 11 and 56 boards. Alaska has 11. If your coverage requirement is “a sign on this specific corridor,” check the corridor rather than the state — a jurisdiction being in the list does not mean it is instrumented everywhere.

If a state publishes DMS data, we ingest it. New states roll in regularly — we’ve done the per-source unification work so you don’t have to.

Who Uses This

One Note on Polling and Freshness — Read This Before You Build an Alerting Product

Be realistic about latency, because this is the number people most often assume in our favour. As of 14 August 2026, of the 49 enabled sign feeds:

So the honest end-to-end figure is: a message you read may be up to ten minutes old on most states, plus whatever cache your own client adds. Polling faster on your side buys you nothing — you will re-read the same value we last fetched. If you need sub-minute DMS latency for a specific corridor, the poll interval is a per-source setting and we can raise it for a state on request; ask rather than assume.

What this does not undermine is the leading-indicator argument above. The gap between a DOT operator updating a board and the same incident appearing in the public 511 feed is often ten or fifteen minutes; a ten-minute poll still lands you inside that window a good share of the time. It does mean you should not promise anyone “within seconds of the operator posting it,” and it means an AMBER-alert pipeline built on DMS should treat this as one signal among several rather than the fastest one available.

Try It

See what every highway sign is saying right now

One API call returns the live message on 12,668 DMS boards across 45 states and provinces — plus a historical archive of how those messages changed. Free 14-day trial. No credit card.

Get Free API Key Explore the Map