<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Roman Kotenko</title>
    <description>The latest articles on DEV Community by Roman Kotenko (@road511).</description>
    <link>https://dev.to/road511</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3869750%2F0aff7c69-b543-4a64-a0a2-32b0207973a3.png</url>
      <title>DEV Community: Roman Kotenko</title>
      <link>https://dev.to/road511</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/road511"/>
    <language>en</language>
    <item>
      <title>One Bridge, Two Border Waits — and Only One of Them Is Yours</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Sat, 05 Sep 2026 11:58:08 +0000</pubDate>
      <link>https://dev.to/road511/one-bridge-two-border-waits-and-only-one-of-them-is-yours-h78</link>
      <guid>https://dev.to/road511/one-bridge-two-border-waits-and-only-one-of-them-is-yours-h78</guid>
      <description>&lt;p&gt;A cross-border load has one place where the plan reliably falls apart, and it isn’t traffic. It’s the primary inspection lane. Everything upstream — the route, the hours, the appointment at the consignee — is built on an ETA that assumes the truck rolls through the plaza. Twenty-five minutes in the commercial queue is a missed dock window; ninety is a reset of the whole day.&lt;/p&gt;

&lt;p&gt;The wait is published. Both governments publish it, in fact — and that is exactly where it gets interesting, because &lt;strong&gt;they publish different halves of it&lt;/strong&gt;. Here is the Blue Water Bridge between Port Huron, Michigan and Point Edward, Ontario, as our North American production database held it &lt;strong&gt;shortly after 07:00 UTC on 8 August 2026&lt;/strong&gt; — 3 a.m. in Michigan, the quietest hour of the day:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Publisher&lt;/th&gt;
&lt;th&gt;Commercial wait&lt;/th&gt;
&lt;th&gt;Reported at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Into the US (northbound plaza, Port Huron)&lt;/td&gt;
&lt;td&gt;U.S. Customs and Border Protection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;“At Midnight EST”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Into Canada (Point Edward)&lt;/td&gt;
&lt;td&gt;Canada Border Services Agency&lt;/td&gt;
&lt;td&gt;&lt;code&gt;25 minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2026-08-08 02:22 EDT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same bridge. Same minute. Zero one way, twenty-five the other — and neither agency publishes the other one’s direction. CBP’s feed covers arrivals into the United States. CBSA’s covers arrivals into Canada; in that same snapshot, its US-bound commercial column read &lt;code&gt;--&lt;/code&gt; at &lt;strong&gt;all 29&lt;/strong&gt; of its offices, because that isn’t CBSA’s number to report. If your app looked up “the Blue Water Bridge wait” from one source, there was a coin-flip chance it was quoting a queue your truck will never sit in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Both Halves, One Field, On the Route
&lt;/h2&gt;

&lt;p&gt;Road511 polls both feeds every five minutes and normalizes them into the same &lt;code&gt;border_crossings&lt;/code&gt; feature type. On a truck-aware route they arrive as &lt;code&gt;warnings[]&lt;/code&gt; entries of &lt;code&gt;type: "border_crossing"&lt;/code&gt; — the same array that already carries bridge clearances, work zones and weigh stations, from the same single call to &lt;code&gt;POST /api/v1/routing/route&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here are the two sides of that bridge exactly as the routing enrichment renders them, from the property blobs quoted above (abridged — &lt;code&gt;distance_along_route_m&lt;/code&gt; and &lt;code&gt;projected_arrival_time&lt;/code&gt; depend on your own route):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"border_crossing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Blue Water Bridge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Commercial border wait ~25 min."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"as_of"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-08T07:03:04Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CBSA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commercial_wait_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commercial_canada_bound"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"25 minutes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commercial_us_bound"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point Edward, ON/Port Huron, MI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"last_updated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-08 02:22 EDT"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"border_crossing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Port Huron - Bluewater Bridge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Commercial border wait ~0 min."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"as_of"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-08T07:10:43Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CBP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commercial_wait_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"wait_trend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"steady"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hours"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24 hrs/day"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commercial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"delay_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no delay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"trend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"steady"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lanes_open"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"max_lanes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fast_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lanes Closed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"update_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"At Midnight EST"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"passenger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"delay_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lanes_open"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"max_lanes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One field means the same thing on both: &lt;code&gt;commercial_wait_minutes&lt;/code&gt;, an integer, commercial lanes, always minutes. Underneath it the raw agency shapes are passed through untouched, so nothing is lost if you want to read CBP’s lane counts or CBSA’s own wording.&lt;/p&gt;

&lt;p&gt;Note that there are two clocks here, and they are different clocks. &lt;code&gt;as_of&lt;/code&gt; is &lt;em&gt;our&lt;/em&gt; observation time — when we last confirmed this record in an upstream poll, minutes old on a five-minute cycle. The agency’s own stamp is a separate field kept in &lt;code&gt;properties&lt;/code&gt;: CBSA’s &lt;code&gt;last_updated&lt;/code&gt; (“2026-08-08 02:22 EDT” above) and CBP’s &lt;code&gt;update_time&lt;/code&gt; (“At Midnight EST”), each in the agency’s own wording and timezone. A fresh &lt;code&gt;as_of&lt;/code&gt; means we are certain the feed still says this; it does not mean the agency re-measured the queue a minute ago. If your logic needs to know how old the &lt;em&gt;reading&lt;/em&gt; is rather than how old the fetch is, read the agency field.&lt;/p&gt;

&lt;p&gt;Severity is keyed on the commercial wait, because this is a truck API and the passenger queue is not your problem: &lt;code&gt;info&lt;/code&gt; below 15 minutes, &lt;code&gt;warning&lt;/code&gt; from 15, &lt;code&gt;critical&lt;/code&gt; past 45 minutes or whenever the port is reported closed. That makes the whole thing filterable with the knob you already have — &lt;code&gt;"min_severity": "warning"&lt;/code&gt; returns only the crossings on your route that are actually going to cost you time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The direction caveat, stated up front
&lt;/h3&gt;

&lt;p&gt;We do not yet resolve which side of the crossing your truck is on. When both directions are known, the warning carries &lt;strong&gt;the larger of the two&lt;/strong&gt; — deliberately, so a route never under-warns — which means a southbound run into Michigan can read a Canada-bound figure. Resolving direction from the route heading is the next iteration of this feature. Until it ships, treat &lt;code&gt;commercial_wait_minutes&lt;/code&gt; as “the worst commercial wait reported at this crossing right now”, and read the per-direction fields underneath it if you need to be exact. We would rather write that sentence than let you discover it on a load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Vocabularies for One Number
&lt;/h2&gt;

&lt;p&gt;The two agencies do not merely publish different directions — they publish different &lt;em&gt;kinds of thing&lt;/em&gt;. CBP sends a structured integer per lane group. CBSA sends free text. Across the 29 CBSA offices in that snapshot the Canada-bound commercial column held &lt;code&gt;No Delay&lt;/code&gt; (21 offices), four actual figures (&lt;code&gt;3 minutes&lt;/code&gt;, &lt;code&gt;5 minutes&lt;/code&gt;, &lt;code&gt;12 minutes&lt;/code&gt;, &lt;code&gt;25 minutes&lt;/code&gt;), &lt;code&gt;Not Applicable&lt;/code&gt; (3), and &lt;code&gt;Temporarily closed&lt;/code&gt; (1).&lt;/p&gt;

&lt;p&gt;Normalizing that is the boring part of the job and the part that has to be right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;No Delay&lt;/code&gt; → &lt;code&gt;0&lt;/code&gt;. A real, published zero.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;25 minutes&lt;/code&gt; → &lt;code&gt;25&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--&lt;/code&gt;, &lt;code&gt;N/A&lt;/code&gt;, &lt;code&gt;Not Applicable&lt;/code&gt;, empty → &lt;strong&gt;no number at all&lt;/strong&gt;. Not zero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last line is the one that matters. “This agency does not report this direction” and “there is no queue” are opposite facts, and collapsing them into &lt;code&gt;0&lt;/code&gt; is how a dispatch board ends up confidently green in front of a two-hour queue. When nothing parseable is there, the warning stays &lt;code&gt;info&lt;/code&gt; and says &lt;code&gt;"Border crossing on route."&lt;/code&gt; — a crossing you should know about, with no wait claimed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"border_crossing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sumas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Border crossing on route."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"as_of"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-08T07:05:42Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CBP"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  “No Delay” With Two Lanes Open
&lt;/h2&gt;

&lt;p&gt;Look again at the Port Huron block: &lt;code&gt;"lanes_open": 2, "max_lanes": 9&lt;/code&gt;. The zero-minute wait is true — and it is true of a plaza running two of its nine commercial lanes at three in the morning. Detroit’s Ambassador Bridge in the same snapshot: &lt;strong&gt;2 of 13&lt;/strong&gt; commercial lanes open, no delay. Buffalo’s Peace Bridge: &lt;strong&gt;4 of 8&lt;/strong&gt;, no delay.&lt;/p&gt;

&lt;p&gt;A wait figure is a measurement of the trucks that are there now. Lane capacity is the thing that decides what happens when the morning wave arrives, and it is in the payload for exactly that reason. If you are building an arrival-time model rather than a dashboard light, &lt;code&gt;lanes_open&lt;/code&gt; against &lt;code&gt;max_lanes&lt;/code&gt; is the more interesting series of the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closed Is a State, Not an Absence
&lt;/h2&gt;

&lt;p&gt;In that snapshot, &lt;strong&gt;27 of the 81 CBP ports were reported closed&lt;/strong&gt; — 24 of them on the Mexican border, where limited hours are normal (54 of the 81 ports run 24 hrs/day; the rest keep windows like &lt;code&gt;6 am-10 pm&lt;/code&gt; or &lt;code&gt;8 am-4 pm&lt;/code&gt;). Laredo’s World Trade Bridge, a commercial-only crossing, was shut: it was 2 a.m. in Texas and its published hours are &lt;code&gt;7 am-Midnight&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A closed port comes back as &lt;code&gt;critical&lt;/code&gt;, with the closure in the title:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"border_crossing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Laredo - World Trade Bridge — closed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Border crossing reported closed."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"closed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"as_of"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-08T07:00:15Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CBP"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it stays in the data while it is closed. That is a deliberate design rule and worth stating, because the obvious implementation gets it backwards: if a crossing disappeared from the API the moment it shut, a dispatcher planning a 4 a.m. departure would see nothing on the corridor and read that as “no border crossing here” rather than “the gate is down until seven.” Silence is the one answer a routing API must never give to that question. A closed port is still a port, it has published hours, and it will open.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Much of It Is Actually Populated
&lt;/h2&gt;

&lt;p&gt;Now the part most vendors leave out. In that same snapshot, of the 81 CBP ports, &lt;strong&gt;17 carried a commercial delay figure&lt;/strong&gt;. The other 64 were &lt;code&gt;Update Pending&lt;/code&gt; (36), &lt;code&gt;Lanes Closed&lt;/code&gt; (15) or &lt;code&gt;N/A&lt;/code&gt; (13). On the CBSA side, 21 offices reported &lt;code&gt;No Delay&lt;/code&gt;, three &lt;code&gt;Not Applicable&lt;/code&gt;, one &lt;code&gt;Temporarily closed&lt;/code&gt;, and four reported an actual number. FAST-lane figures — which we surface as &lt;code&gt;fast_lane_wait_minutes&lt;/code&gt; and a derived &lt;code&gt;fast_lane_savings_minutes&lt;/code&gt; when they are published — existed at exactly &lt;strong&gt;one&lt;/strong&gt; port. (That last figure in particular is an artefact of the hour, and we come back to it below.)&lt;/p&gt;

&lt;p&gt;Some of that is the hour: a plaza with its commercial lanes closed overnight has no wait to report, and the numbers thicken as the border wakes up. But the shape is the point — &lt;strong&gt;a border-wait API is mostly a “no figure right now” API&lt;/strong&gt;, and a client that assumes every crossing has a live number will be wrong most of the time. Ours tells you which case you are in, on every single crossing, rather than smoothing it into a plausible-looking integer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Same Query at Ten in the Morning
&lt;/h2&gt;

&lt;p&gt;“Some of that is the hour” is the kind of hedge that deserves a measurement rather than a promise, so here is one. Everything above is a 3 a.m. reading. We re-ran the identical queries against the same production database on &lt;strong&gt;14 August 2026 at 13:57 UTC — 09:57 Eastern, mid-morning on a working Friday&lt;/strong&gt;. The difference is large enough that you should not size your integration on the overnight figures:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;3 a.m. ET, 8 Aug&lt;/th&gt;
&lt;th&gt;10 a.m. ET, 14 Aug&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CBP ports carrying a commercial delay figure&lt;/td&gt;
&lt;td&gt;17 of 81&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;35 of 81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBP ports reported closed&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Highest commercial wait anywhere&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30 min&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ports publishing a FAST figure&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBSA offices publishing a US-bound wait&lt;/td&gt;
&lt;td&gt;0 of 29&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 of 27&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two of those rows carry a lesson each. &lt;strong&gt;The population rate roughly doubles&lt;/strong&gt; and the closures nearly vanish — so a coverage number quoted from an overnight sample understates the daytime product badly, and one quoted from a daytime sample overstates the overnight one. Whichever direction you are being sold, ask what hour the sample came from. &lt;strong&gt;The FAST count moved by a factor of eleven&lt;/strong&gt;, which is the honest correction to the paragraph above: FAST is not a feature that exists at one port, it is a feature CBP populates when the FAST lanes are staffed. At mid-morning it was publishing FAST at eleven ports, and at Laredo’s Colombia Solidarity bridge the FAST lane read &lt;code&gt;5&lt;/code&gt; against a standard &lt;code&gt;30&lt;/code&gt; — a genuine 25-minute saving, and exactly the case an enrolled carrier wants surfaced.&lt;/p&gt;

&lt;p&gt;And the last row is the one that did &lt;em&gt;not&lt;/em&gt; move. Every one of CBSA’s 27 offices still had &lt;code&gt;--&lt;/code&gt; in its US-bound commercial column at ten in the morning, just as all 29 did at three. That asymmetry is structural, not a nocturnal quirk — it is the design fact this whole post is built on, and it is stable at every hour we have looked.&lt;/p&gt;

&lt;p&gt;The lane-capacity story sharpens in daylight too. Detroit’s Ambassador Bridge had gone from &lt;strong&gt;2 of 13&lt;/strong&gt; commercial lanes to &lt;strong&gt;5 of 13&lt;/strong&gt;, and had picked up a 5-minute wait doing it — more lanes and a longer queue, at the same time, which is precisely why the wait figure alone is a poor arrival-time input. Port Huron ran &lt;strong&gt;4 of 9&lt;/strong&gt; with a 2-minute wait. Best of all, Laredo’s World Trade Bridge — the commercial-only crossing that was shut at 2 a.m. Texas time in the earlier snapshot — was open with &lt;strong&gt;11 of its 19&lt;/strong&gt; lanes running and no delay at all. The closed record you saw overnight had turned into the busiest commercial plaza in the dataset, without ever leaving the API.&lt;/p&gt;

&lt;p&gt;The northern border, meanwhile, was quiet: the Peace Bridge, the Ambassador, the Windsor Tunnel, Alexandria Bay, Sweetgrass and Blaine all sat at 5 minutes or under. The 30-minute figures were both on the Mexican border, at Laredo’s Colombia Solidarity and Pharr. If your lanes are Canadian, budget your expectations accordingly — and if they are Mexican, note that the busiest reading in this dataset is still a half-hour, not the horror stories.&lt;/p&gt;

&lt;p&gt;The wider inventory works the same way, and it is the coverage number to plan against. As of 14 August 2026 we hold &lt;strong&gt;197 active border crossings&lt;/strong&gt; across North America; &lt;strong&gt;108 of them&lt;/strong&gt; — 81 CBP ports and 27 CBSA offices — sit behind a live wait feed. The remaining &lt;strong&gt;89&lt;/strong&gt; come from provincial and state 511 map layers — Manitoba (32), Saskatchewan (24), Alberta (12), New Brunswick (6), Québec (5), Newfoundland (4), British Columbia (4) and California (2) — and are location records: they tell you a crossing exists and where, and they publish no wait at all, ever. They still ride your route as &lt;code&gt;info&lt;/code&gt;, never with an invented number.&lt;/p&gt;

&lt;p&gt;Read that ratio before you design around this: &lt;strong&gt;45% of the land border crossings we know about have no wait feed behind them at any hour&lt;/strong&gt;, and no amount of waiting for daytime will change it — unlike the &lt;code&gt;Update Pending&lt;/code&gt; case above, this is a permanent absence rather than a temporary one. Note also that the CBSA office count itself drifts: it was 29 on 8 August and 27 on 14 August, because the agency’s own list of reporting offices changes. Don’t hard-code it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Don’t Do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No history, no “typical Thursday.”&lt;/strong&gt; We publish the live reading and a poll-over-poll &lt;code&gt;wait_trend&lt;/code&gt; (&lt;code&gt;rising&lt;/code&gt; / &lt;code&gt;falling&lt;/code&gt; / &lt;code&gt;steady&lt;/code&gt;, moving on a ±5-minute threshold so a two-minute wobble doesn’t flap). There is no border-wait time series behind this endpoint yet, so no hour-of-day base rate — and we would rather say so than dress a single reading up as a pattern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No modelled waits.&lt;/strong&gt; Every figure is the agency’s own, or there is no figure. No crowd-sourced estimate, no inference of a queue from silence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No bypass or clearance claims.&lt;/strong&gt; FAST is surfaced when CBP publishes it. Whether your carrier is enrolled, and what that means at primary, is between you and the program.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Same Field on the Other Side of the Atlantic
&lt;/h2&gt;

&lt;p&gt;The schema is not North-America-shaped by accident. Our European deployment runs the identical &lt;code&gt;border_crossing&lt;/code&gt; warning over Ukraine’s electronic queue at the EU external frontier, where commercial waits are measured in &lt;em&gt;days&lt;/em&gt; rather than minutes — the same &lt;code&gt;commercial_wait_minutes&lt;/code&gt; integer, rendered as “4 d 3 h” instead of “25 min”. If you run both continents, it’s one integration. That story is &lt;a href="https://napspan.com/blog/eu-border-queue-wait-times.html" rel="noopener noreferrer"&gt;on the NAPSPAN blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://road511.com/docs.html#truck-routing" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — the full &lt;code&gt;warnings[]&lt;/code&gt; reference, including &lt;code&gt;border_crossing&lt;/code&gt; severity bands and properties&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://road511.com/blog/truck-routing-hazard-warnings.html" rel="noopener noreferrer"&gt;Truck routing with hazard warnings&lt;/a&gt; — how the whole corridor &lt;code&gt;warnings[]&lt;/code&gt; channel works&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://road511.com/data-sources.html" rel="noopener noreferrer"&gt;Data sources&lt;/a&gt; — every feed behind the API, with its licence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wait times from U.S. Customs and Border Protection (US public domain) and the Canada Border Services Agency. Contains information licensed under the Open Government Licence – Canada, © Canada Border Services Agency. Crossing coordinates are geocoded from OpenStreetMap data (© OpenStreetMap contributors, ODbL). Figures in this post come from two snapshots of our production database: the overnight one taken shortly after 07:00 UTC on 8 August 2026 (03:00 Eastern), and the mid-morning comparison taken at 13:57 UTC on 14 August 2026 (09:57 Eastern). Each was correct at that moment only, and every section states which one it is drawing on — query the API for the live picture.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://road511.com/blog/us-canada-border-wait-times-api.html?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=us-canada-border-wait-times-api&amp;amp;utm_content=dev-11" rel="noopener noreferrer"&gt;road511.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>logistics</category>
      <category>trucking</category>
      <category>data</category>
    </item>
    <item>
      <title>Know the Conditions at the Incident, Not Just That There Is One</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Thu, 27 Aug 2026 08:06:53 +0000</pubDate>
      <link>https://dev.to/road511/know-the-conditions-at-the-incident-not-just-that-there-is-one-354d</link>
      <guid>https://dev.to/road511/know-the-conditions-at-the-incident-not-just-that-there-is-one-354d</guid>
      <description>&lt;p&gt;“Crash on I-80, right lane blocked.”&lt;/p&gt;

&lt;p&gt;That sentence describes two completely different events. In one, it’s a dry afternoon, the lane clears in twenty minutes, and the right call is to hold the load and wait. In the other, the pavement is iced, visibility is down to a quarter mile, and the wind is gusting across an exposed grade — in which case the crash you can see is the least interesting fact on the screen, because the conditions that produced it are still there, and they are about to produce another one three miles up.&lt;/p&gt;

&lt;p&gt;Every incident feed on the market gives you the first sentence. Almost none of them tell you which of the two events it actually is. &lt;strong&gt;Dispatch doesn’t respond to incidents. Dispatch responds to incidents in context&lt;/strong&gt; — and the context has to arrive attached to the incident, at the moment it appears, or it arrives too late to change the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Context Already Exists, Sitting in a Different Feed
&lt;/h2&gt;

&lt;p&gt;State DOTs have spent decades installing roadside weather stations — RWIS, in the trade — the instrumented poles that report air temperature, wind, visibility, humidity, precipitation type, and, critically, &lt;em&gt;what the road surface itself is doing&lt;/em&gt; — dry, wet, icy, snow-covered. Each agency says that in its own words rather than a shared vocabulary, which turns out to matter a great deal and which we come back to in detail below.&lt;/p&gt;

&lt;p&gt;Road511 currently carries &lt;strong&gt;4,082 active roadside weather stations across 37 states and provinces&lt;/strong&gt;, alongside &lt;strong&gt;8,132 active road-condition segments across 20&lt;/strong&gt; — the latter being the DOT’s own segment-level assessment of a stretch of road, as opposed to a single station’s point reading. (Counts measured 27 August 2026, active records only. Stations come and go as agencies retire hardware, so treat these as this month’s figure rather than a constant.)&lt;/p&gt;

&lt;p&gt;The data is public, official, and already flowing. The problem is that it lives in a different layer from the incidents, on a different schema, keyed to different geometry. So the dispatcher gets an incident in one panel and a weather map in another, and does the spatial join in their head, under time pressure, at 3am. That join is the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every Event Carries Its Own Weather
&lt;/h2&gt;

&lt;p&gt;When a new incident lands in Road511 — a crash, a closure, a work zone — we immediately look for the nearest active weather station within 50 km and write down what it was reporting at that moment. That reading is stapled to the event, permanently. Here is an actual record, pulled from the production database as this post was last revised — a Pennsylvania crash on I‑70 from the afternoon of 16 August 2026, verbatim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/analytics/event-weather/PA-evt-348347

{
  "id": 2188447,
  "event_id": "PA-evt-348347",
  "distance_km": 1.7816931,
  "temperature": 80.1,
  "temperature_c": 26.722221,
  "wind_speed": 3.6,
  "wind_direction": "SW",
  "visibility": 12.43,
  "precipitation": "No Precipitation",
  "road_surface": "Dry",
  "humidity": 83,
  "reading_recorded_at": "2026-08-16T17:00:10.704866Z",
  "created_at": "2026-08-16T17:04:39.913287Z"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The event itself reads &lt;em&gt;CRASH on I-70 Eastbound at ramp from PA 51 South. All lanes disrupted.&lt;/em&gt; Read the weather as a dispatcher rather than as a developer. Dry pavement, no precipitation, a visibility of 12.43 — miles, on this feed — almost no wind, from a station 1.8 km away, recorded four minutes before the incident was written. That is a &lt;em&gt;hold the load and wait&lt;/em&gt; incident, and you knew it without reading the description. Flip three of those fields — a road surface the agency has flagged for ice, visibility under a mile, a crosswind on an exposed grade — and the same incident text supports the opposite decision: reroute the following three loads now, don’t just delay them. The value here is not that the conditions are dramatic. It is that they are &lt;em&gt;present&lt;/em&gt;, on the incident, at the moment it appears, in one call.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;distance_km&lt;/code&gt; field is doing real work there, and it’s the reason we return it. A station 1.8 km from the incident is describing the incident. A station 46 km away is describing a different valley, and you should discount it accordingly. Across the whole archive the average snapshot sits about 16 km out, so this is a filter you will actually use. We don’t hide that number behind a confidence score we made up — we give you the distance and let you decide what it’s worth.&lt;/p&gt;

&lt;h3&gt;
  
  
  What those field values actually look like
&lt;/h3&gt;

&lt;p&gt;Notice that &lt;code&gt;precipitation&lt;/code&gt; reads &lt;code&gt;"No Precipitation"&lt;/code&gt; and &lt;code&gt;road_surface&lt;/code&gt; reads &lt;code&gt;"Dry"&lt;/code&gt; — capitalised, spelled out, in the reporting agency’s own words. &lt;strong&gt;These are not normalized enums, and you must not code against them as though they were.&lt;/strong&gt; Across the archive the same two columns hold &lt;code&gt;None&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;No Precipitation&lt;/code&gt;, &lt;code&gt;not available&lt;/code&gt;, &lt;code&gt;Unknown&lt;/code&gt;, &lt;code&gt;light rain&lt;/code&gt;, &lt;code&gt;rain moderate&lt;/code&gt; and &lt;code&gt;0 mm&lt;/code&gt; for precipitation; &lt;code&gt;Dry&lt;/code&gt;, &lt;code&gt;dry&lt;/code&gt;, &lt;code&gt;Wet&lt;/code&gt;, &lt;code&gt;Trace Moisture&lt;/code&gt;, &lt;code&gt;Trace Wet&lt;/code&gt;, &lt;code&gt;Moist&lt;/code&gt;, &lt;code&gt;wet and dry&lt;/code&gt;, &lt;code&gt;Ice Watch&lt;/code&gt;, &lt;code&gt;Ice Warning&lt;/code&gt;, &lt;code&gt;Snow Warning&lt;/code&gt; and &lt;code&gt;Slushy&lt;/code&gt; for road surface. A handful of feeds leak raw sensor codes into the field — you will find literal &lt;code&gt;4.0&lt;/code&gt; and &lt;code&gt;65535&lt;/code&gt; values in there.&lt;/p&gt;

&lt;p&gt;Two consequences worth internalising before you build on this. First, &lt;strong&gt;match case-insensitively and on substrings&lt;/strong&gt;, and treat anything you don’t recognise as unknown rather than as safe. Second, and less obvious: some agencies publish their ice-detection subsystem’s &lt;em&gt;alarm state&lt;/em&gt; in this field rather than an observation, which is why &lt;code&gt;Ice Watch&lt;/code&gt; turns up on a Virginia road at 30 °C in August. &lt;code&gt;Ice Watch&lt;/code&gt; means a sensor is armed, not that there is ice. If you are going to escalate on a surface value, escalate on it &lt;em&gt;together with&lt;/em&gt; the temperature, never on the string alone. We pass the agency’s word through rather than mapping it to a tidy enum, because the mapping would have to guess at exactly the cases where guessing is most expensive — but that means the interpretation is genuinely yours to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Anecdote to a Number You Can Argue With
&lt;/h2&gt;

&lt;p&gt;“Bad weather causes accidents” is a thing everyone knows and nobody can put a figure on. Once an event carries a snapshot, the figure becomes a query. Group the incident history by what the road was doing and what was falling out of the sky:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/analytics/weather-correlation?jurisdiction=OH

[
  { "precipitation": "None",    "road_surface": "",
    "event_count": 7348, "avg_severity": 1.95, "avg_temp_c": 22.0 },
  { "precipitation": "None",    "road_surface": "Dry",
    "event_count": 5806, "avg_severity": 1.95, "avg_temp_c": 24.8 },
  { "precipitation": "None",    "road_surface": "Unknown",
    "event_count": 2000, "avg_severity": 1.94, "avg_temp_c": 24.7 },
  { "precipitation": "Unknown", "road_surface": "",
    "event_count": 1709, "avg_severity": 1.98, "avg_temp_c": 21.9 },
  { "precipitation": "Other",   "road_surface": "",
    "event_count": 1229, "avg_severity": 1.96, "avg_temp_c": 23.4 },
  { "precipitation": "None",    "road_surface": "Wet",
    "event_count":  530, "avg_severity": 2.05, "avg_temp_c": 22.0 },
  { "precipitation": "Other",   "road_surface": "Unknown",
    "event_count":  470, "avg_severity": 2.00, "avg_temp_c": 24.3 },
  { "precipitation": "Unknown", "road_surface": "Unknown",
    "event_count":  461, "avg_severity": 2.02, "avg_temp_c": 27.1 },
  { "precipitation": "Rain",    "road_surface": "",
    "event_count":  445, "avg_severity": 2.04, "avg_temp_c": 19.0 },
  { "precipitation": "Rain",    "road_surface": "Wet",
    "event_count":  256, "avg_severity": 2.09, "avg_temp_c": 21.9 }
]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the real response for Ohio on 16 August 2026 — the top ten rows exactly as returned, in the order returned — and it is worth showing you exactly because it is &lt;em&gt;not&lt;/em&gt; the tidy result a marketing example would invent. Three things in it are the honest state of this endpoint today, and you should know all three before you build on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The severity spread is currently tiny.&lt;/strong&gt; 1.95 on dry, 2.05 on wet, 2.09 in rain — the ordering is the one you would predict, but the whole range spans about a seventh of a single severity step, and further down the same response &lt;code&gt;Trace Wet&lt;/code&gt; sits at 1.79, &lt;em&gt;below&lt;/em&gt; dry. Treat that as a gradient thin enough that one unusual month could erase it, not as a finding. (&lt;code&gt;avg_severity&lt;/code&gt; is a weighted score: critical counts 4, major 3, moderate 2, everything else 1.) We are not going to tell you we have measured that ice doubles incident severity, because in this archive, so far, we have not. What we have built is the instrument; the reading it currently gives on most lanes is “no strong effect yet.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The archive is young.&lt;/strong&gt; Weather snapshots begin on &lt;strong&gt;10 April 2026&lt;/strong&gt;. There is no winter in this data at all yet, which is the single biggest reason the ice story above cannot be told from it — the interesting conditions have not happened during the archive’s lifetime. A date range starting before April 2026 returns an empty array, so don’t write one and conclude the endpoint is broken. The first real winter of this dataset is the 2026–27 one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most rows have an empty &lt;code&gt;road_surface&lt;/code&gt;.&lt;/strong&gt; Of the &lt;strong&gt;646,539&lt;/strong&gt; snapshots in the archive on 27 August 2026, &lt;strong&gt;118,860&lt;/strong&gt; carry a road-surface string and &lt;strong&gt;133,766&lt;/strong&gt; a precipitation string; only &lt;strong&gt;22,964 — about 3.6% — carry surface, precipitation, wind and visibility all four&lt;/strong&gt;. Most stations report a subset, and the grouping key you most want is the one most often absent. Ohio is better than that average, at roughly half its rows carrying a surface string, and it still leads with an empty one: that top row is not a bug, it is 7,348 events whose nearest station published a temperature and nothing about the pavement.&lt;/p&gt;

&lt;p&gt;Filter by &lt;code&gt;jurisdiction&lt;/code&gt;, by event &lt;code&gt;type&lt;/code&gt;, by &lt;code&gt;severity&lt;/code&gt;, or by date range, and you can put the question your own lanes actually pose to the data — which corridors punish ice, which ones shrug it off. Just budget for the answer being “not enough observations yet” on a lot of them, and check &lt;code&gt;event_count&lt;/code&gt; before you believe an &lt;code&gt;avg_severity&lt;/code&gt;. We would rather hand you a thin dataset labelled thin than a thick one we smoothed.&lt;/p&gt;

&lt;h2&gt;
  
  
  On the Route
&lt;/h2&gt;

&lt;p&gt;Weather stations also ride the corridor. Send a route to &lt;code&gt;POST /routing/route&lt;/code&gt; and the stations near the line come back in &lt;code&gt;warnings[]&lt;/code&gt; as &lt;code&gt;type: "weather"&lt;/code&gt;, each carrying its readings and the time the truck is projected to reach it — the same channel that already carries &lt;a href="https://road511.com/blog/truck-routing-hazard-warnings.html" rel="noopener noreferrer"&gt;bridge clearances and corridor hazards&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Be clear about what that is and isn’t. Those weather markers are surfaced at &lt;code&gt;info&lt;/code&gt; severity, as observations. &lt;strong&gt;Road511 does not currently threshold them&lt;/strong&gt; — there is no rule that escalates “road_surface: ice” to a &lt;code&gt;warning&lt;/code&gt;, no wind-speed limit that trips a flag for a high-profile trailer. The reading is handed to you; the policy is yours, because the policy is genuinely yours: the wind speed that stops a loaded reefer is not the one that stops a flatbed, and we’d rather give you the number than guess your fleet’s tolerance. When you write that policy, apply the two cautions above — the surface strings are the agency’s vocabulary rather than an enum, and wind and visibility carry no unit — because a threshold is exactly where an unnormalized field does its damage.&lt;/p&gt;

&lt;p&gt;And the segment-level &lt;code&gt;road_conditions&lt;/code&gt; layer — the DOT’s own “this stretch is snow-covered” assessment — is &lt;em&gt;not&lt;/em&gt; enriched onto routes today. It’s queryable as a layer over any bounding box or corridor you define, but it does not currently arrive attached to a route response. If route-level surface state is what you need, that’s a gap, and we’d rather tell you now than have you discover it in integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Build With It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Triage that reads the conditions, not just the headline&lt;/strong&gt; — auto-escalate an incident whose snapshot shows a freezing temperature &lt;em&gt;and&lt;/em&gt; an icy surface string, before a human has read the description. (Both halves: the surface string alone will fire on a summer sensor alarm.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Winter corridor scorecards&lt;/strong&gt; — rank your lanes by the severity they produce under the same conditions. The endpoint for this exists today; the archive to fill it starts accumulating its first winter in November 2026.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-incident review that isn’t guesswork&lt;/strong&gt; — when a load is late or a claim is filed, the conditions at the event are already recorded, timestamped, and attributable to a named agency’s station.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver-facing condition alerts&lt;/strong&gt; — surface the observations along the route with your own thresholds applied, tuned to the trailer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weather beside everything else on the corridor&lt;/strong&gt; — conditions alongside &lt;a href="https://road511.com/blog/work-zones-wzdx-routing-api.html" rel="noopener noreferrer"&gt;work zones&lt;/a&gt;, &lt;a href="https://road511.com/blog/planned-construction-api.html" rel="noopener noreferrer"&gt;planned construction&lt;/a&gt;, and closures in one schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Provenance, and the Honest Limits
&lt;/h2&gt;

&lt;p&gt;These readings come from state DOT roadside stations — official instruments, not a modelled forecast and not crowd-sourced. That’s the strength, and it dictates the limits, which are worth stating plainly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The snapshot is taken once, at the moment the event is created, and never refreshed.&lt;/strong&gt; It answers “what were the conditions when this appeared,” which is the question that matters for triage and for later analysis. It does not track how the weather evolved over the life of a six-hour closure. For current conditions, query the station directly — &lt;code&gt;/analytics/weather&lt;/code&gt; returns its reading time-series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If no active station sits within 50 km, the event gets no snapshot at all.&lt;/strong&gt; We do not reach further and pretend a reading from the next county describes this stretch of road. On a rural corridor, expect gaps; a missing snapshot is an honest absence, not a zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Readings pass through in the units the reporting agency publishes.&lt;/strong&gt; Most US stations report Fahrenheit, miles, and miles per hour — but not all of them: British Columbia and, of all places, California publish Celsius. So &lt;code&gt;temperature&lt;/code&gt; is the agency’s own number, and &lt;code&gt;temperature_c&lt;/code&gt; sits beside it, normalized, for when you need to compare or average across jurisdictions. Where we haven’t established a feed’s unit, &lt;code&gt;temperature_c&lt;/code&gt; is simply absent rather than guessed — roughly a quarter of snapshots carry it — and the correlation endpoint leaves those readings out of its average instead of blending them in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Temperature is the only field we normalize. Wind and visibility are not.&lt;/strong&gt; This matters more than it sounds: in the same &lt;code&gt;visibility&lt;/code&gt; column you will find &lt;code&gt;12.43&lt;/code&gt; from a Delaware station reporting miles and &lt;code&gt;621&lt;/code&gt; from a Virginia one that plainly is not. There is no unit field to tell them apart. Compare visibility and wind &lt;em&gt;within a single jurisdiction&lt;/em&gt; and never across them, and put a sanity bound on the value before you feed it to anything that makes a decision. If you need one cross-jurisdiction number, &lt;code&gt;temperature_c&lt;/code&gt; is the one field that is safe to average.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is observation, not prediction.&lt;/strong&gt; There is no weather forecasting here, and we’re not going to bolt a forecast model onto a road-data API and call it dispatch intelligence. What we do is guarantee that when an incident exists, whatever the nearest station measured exists next to it, in one schema, from 4,082 stations we keep parsed and current as their feeds drift — so that job isn’t yours.&lt;/p&gt;

&lt;p&gt;Weather snapshots, the correlation endpoint, and the reading time-series are analytics-tier features (Pro and above). The underlying &lt;code&gt;weather_stations&lt;/code&gt; and &lt;code&gt;road_conditions&lt;/code&gt; layers are available on every plan, free trial included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — the analytics surface, plus the &lt;code&gt;weather_stations&lt;/code&gt; and &lt;code&gt;road_conditions&lt;/code&gt; feature types&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see the station network and current road conditions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://road511.com/blog/weather-aware-dispatch-api.html?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=weather-aware-dispatch-api&amp;amp;utm_content=dev-09" rel="noopener noreferrer"&gt;road511.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>trucking</category>
      <category>api</category>
      <category>logistics</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Plan Around Next Month's Closure, Not Today's</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Tue, 11 Aug 2026 10:16:35 +0000</pubDate>
      <link>https://dev.to/road511/plan-around-next-months-closure-not-todays-fge</link>
      <guid>https://dev.to/road511/plan-around-next-months-closure-not-todays-fge</guid>
      <description>&lt;p&gt;Every traffic API on the market answers the same question: &lt;em&gt;what is closed right now?&lt;/em&gt; That question is worth answering. It is also the wrong one for most of the decisions a planner actually makes. The oversize load ships in three weeks. The seasonal lane is being quoted for August. The construction crew is scheduling a detour route for a job that starts after Labor Day. Nobody planning that work cares what is coned off this morning — they care what will be dug up &lt;em&gt;when the truck gets there&lt;/em&gt;, and a live incident feed structurally cannot tell them.&lt;/p&gt;

&lt;p&gt;So the planner does the thing the tooling forces: opens six state DOT project pages in six browser tabs, reads six different PDF schedules, and writes the dates into a spreadsheet that is stale within a week. &lt;strong&gt;The data exists. It is published. It just isn’t queryable as data.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Planned Work Is a Different Dataset Than Active Work
&lt;/h2&gt;

&lt;p&gt;It’s tempting to assume that future construction is just a work zone with a start date in the future, and that whatever feed carries today’s lane closures will carry next month’s too. It doesn’t, and the distinction matters operationally.&lt;/p&gt;

&lt;p&gt;Active work zones — the ones we wrote about in &lt;a href="https://road511.com/blog/work-zones-wzdx-routing-api.html" rel="noopener noreferrer"&gt;work zones along a route&lt;/a&gt; — come from real-time feeds describing conditions that exist right now. They appear when the cones go out and disappear when the crew leaves. Their whole design assumes you are asking about the present.&lt;/p&gt;

&lt;p&gt;Planned construction comes from somewhere else entirely: the agency’s project pipeline. A DOT’s capital program, its STIP project list, the “upcoming closures” page maintained by the district office. These records exist before a single cone is placed — but how far before depends entirely on the agency, and that variation is the first thing to plan around. Delaware’s pipeline carries start dates booked into 2030; Virginia’s layer is overnight lane work on a roughly three-day horizon, with a median window of eight hours. Same feature type, two very different horizons: bound your query window rather than trusting the field to mean the same thing everywhere. What these records do carry, and live feeds lack, is &lt;strong&gt;a projected start and end date&lt;/strong&gt;. They also carry the property that makes them awkward to consume: &lt;em&gt;those dates are estimates, and they move.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Dates Come From
&lt;/h2&gt;

&lt;p&gt;Road511 now normalizes planned-construction records from nine states and provinces into one &lt;code&gt;future_construction&lt;/code&gt; feature type — &lt;strong&gt;2,161 projects&lt;/strong&gt; as of today, each with whatever schedule the agency published. Virginia’s upcoming-closures layer is the deepest (927 records), with Delaware’s project portal close behind (774), then New Jersey’s STIP project locations (247), West Virginia’s project records (162) and North Carolina’s future-closures feed (42), with Idaho, New Brunswick, Nevada, and Prince Edward Island contributing the rest. West Virginia also publishes planned work as events carrying &lt;code&gt;status=planned&lt;/code&gt; — another shape, same question.&lt;/p&gt;

&lt;p&gt;Treat that total as a reading, not a constant. Virginia’s layer is a rolling window that drops records once their dates pass: it stood at 2,176 in early July and 927 today, which is the feed working as designed rather than coverage being lost. Feeds also go quiet — Manitoba, Newfoundland, Nevada and Prince Edward Island are each returning nothing at the moment, so on any given day the number of jurisdictions actually publishing is smaller than the number we can parse. Query it rather than quoting it.&lt;/p&gt;

&lt;p&gt;Nine jurisdictions is not fifty, and we won’t pretend otherwise. But it’s the corridors where DOTs actually publish forward-looking schedules as machine-readable data, and each one arrives on its own schema, its own date format, and its own idea of what “start” means. Folding them into a single feature type with a single pair of timestamps is the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asking the Question Directly
&lt;/h2&gt;

&lt;p&gt;Once every project shares one schema, “what will be under construction on this corridor in August” stops being a spreadsheet exercise and becomes a query. The &lt;code&gt;/features&lt;/code&gt; endpoint now takes four scheduled-window filters — &lt;code&gt;starts_after&lt;/code&gt;, &lt;code&gt;starts_before&lt;/code&gt;, &lt;code&gt;ends_after&lt;/code&gt;, &lt;code&gt;ends_before&lt;/code&gt; — each accepting an RFC3339 timestamp or a plain &lt;code&gt;YYYY-MM-DD&lt;/code&gt; date.&lt;/p&gt;

&lt;p&gt;Bound the window from both ends and you get overlap: every project whose scheduled dates intersect the period you care about, whether it starts inside the window, ends inside it, or spans straight through.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/features?
  type=future_construction&amp;amp;
  jurisdiction=VA&amp;amp;
  starts_before=2026-09-01&amp;amp;
  ends_after=2026-08-01

{
  "data": [
    {
      "id": "VA-fcon-WZNO4585395-12252025-298",
      "source": "VA",
      "jurisdiction": "VA",
      "feature_type": "future_construction",
      "name": "On I-66W Express Lanes from mile marker 65 to mile marker 43",
      "road_name": "On I-66W Express Lanes from mile marker 65 to mile marker 43",
      "latitude": 38.886177,
      "longitude": -77.221801,
      "is_active": true,
      "estimated_start_time": "2026-08-12T02:00:00Z",
      "estimated_end_time": "2026-08-12T09:00:00Z",
      "last_updated": "2026-08-11T08:43:55Z"
    }
  ],
  "total": 801,
  "limit": 100,
  "offset": 0,
  "has_more": true
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole ask: one call, one corridor, one month, every project that will be in the way. Add &lt;code&gt;bbox&lt;/code&gt; or &lt;code&gt;lat&lt;/code&gt;/&lt;code&gt;lng&lt;/code&gt;/&lt;code&gt;radius_km&lt;/code&gt; to narrow it to the geography the load actually travels, or hit &lt;code&gt;/features/geojson&lt;/code&gt; to drop the same result straight onto a map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two caveats on that example.&lt;/strong&gt; &lt;code&gt;total&lt;/code&gt; is a live reading, not a constant — run the query rather than quoting the number. And Virginia is the short-horizon case described above: it publishes overnight lane work about three days out, so the same query aimed at a month further ahead returns almost nothing. That is a property of the agency, not of the API. For a long-horizon pipeline, run it against &lt;code&gt;jurisdiction=DE&lt;/code&gt;, whose records carry start dates years out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two details worth knowing
&lt;/h3&gt;

&lt;p&gt;The filters match on the &lt;strong&gt;planned&lt;/strong&gt; dates where an agency publishes them and fall back to actual dates where it doesn’t — so a source that reports only a real start time stays queryable on the same parameters, and you don’t need to know which kind of source you’re talking to.&lt;/p&gt;

&lt;p&gt;And a project with a known start but no published end is treated as &lt;strong&gt;open-ended&lt;/strong&gt;: it matches &lt;code&gt;ends_after&lt;/code&gt;, because a job with no end date certainly might still be running in August. It never matches &lt;code&gt;ends_before&lt;/code&gt;, because nothing about it promises it will be finished. A record with no dates at all — a project the agency has announced but not scheduled — drops out of window queries entirely rather than quietly padding your results. Those two rules are the difference between a date filter you can plan against and one that lies to you at the edges.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dates Move. That’s the Interesting Part.
&lt;/h2&gt;

&lt;p&gt;A planned start date is a forecast, and forecasts slip. Utility work runs long, a bridge job waits on steel, the whole thing gets pushed a season. Any planner who has been burned once knows not to treat &lt;code&gt;estimated_start_time&lt;/code&gt; as gospel — and the useful response is not to distrust the data but to &lt;em&gt;watch it change&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So we keep the history. Every time an agency revises a project’s projected start or end, the change is recorded — old value, new value, when we saw it — and it’s queryable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/analytics/feature-history?
  change_type=estimated_start_time_change
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That turns a static schedule into a signal. A project that has been rescheduled four times is telling you something a single date field never could. At the jurisdiction level, &lt;code&gt;/analytics/scorecard&lt;/code&gt; asks the same question of a wider pool — how far does an agency move the end dates it publishes? — so you can calibrate how much slack a given DOT’s published dates historically need. It rolls up event-modelled roadwork rather than the &lt;code&gt;future_construction&lt;/code&gt; records above, and it reports only the jobs whose revisions we can still see, so read the two as complementary views of the same habit rather than one number. Both live behind the analytics tier (Pro and up); the &lt;code&gt;future_construction&lt;/code&gt; records themselves are available on every plan, free trial included.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Build With It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permit and oversize-load planning&lt;/strong&gt; — check the corridor for the delivery week, not for today, and catch the lane restriction that lands between quote and haul.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seasonal lane and contract pricing&lt;/strong&gt; — know which corridors carry scheduled disruption during the months you’re quoting, before the rate is locked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detour design that doesn’t collide&lt;/strong&gt; — route around a work zone without routing into the one starting next week two miles over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schedule-drift monitoring&lt;/strong&gt; — watch the projects on your key corridors and get told when the dates move, instead of rediscovering it on arrival.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planning views that pair with the present&lt;/strong&gt; — planned construction beside live work zones, bridge clearances, and &lt;a href="https://road511.com/blog/spring-load-limits-api-trucking.html" rel="noopener noreferrer"&gt;seasonal load limits&lt;/a&gt; in one schema, on one corridor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Provenance, and the Honest Limits
&lt;/h2&gt;

&lt;p&gt;These records come from the DOTs’ own project pipelines — official, attributed, and refreshed on our poll rather than transcribed by hand. The limits are real and worth stating plainly. Coverage is nine states and provinces, not the whole map, because forward-looking schedules are the least consistently published data a DOT produces — and as noted above, on any given day fewer than nine are actually returning records. Every date is an estimate, published by the agency and subject to revision without notice; that is a property of construction, not of the feed, and the change log exists precisely because of it. Detail varies — some sources give a full project description and route, others give a name and a pair of dates. And a planned closure is a plan: it tells you what an agency currently intends, which is the best information that exists three weeks out, and still not a promise.&lt;/p&gt;

&lt;p&gt;The job we take on is keeping nine project pipelines parsed, normalized, and current as they drift — so it isn’t yours. This is the same integration tax we described in &lt;a href="https://road511.com/blog/north-american-road-data-challenges.html" rel="noopener noreferrer"&gt;the five road-data problems every NA fleet team hits&lt;/a&gt;, applied to the one dataset that lets you get ahead of the problem instead of reacting to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — the &lt;code&gt;features&lt;/code&gt; surface, including &lt;code&gt;future_construction&lt;/code&gt; and the scheduled-window filters&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see planned construction alongside live conditions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://road511.com/blog/planned-construction-api.html?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=planned-construction-api&amp;amp;utm_content=dev-07" rel="noopener noreferrer"&gt;road511.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>logistics</category>
      <category>trucking</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Your ETA Assumes the Border Takes an Hour. It Can Take Four Days.</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Sat, 08 Aug 2026 08:19:40 +0000</pubDate>
      <link>https://dev.to/road511/your-eta-assumes-the-border-takes-an-hour-it-can-take-four-days-4g3</link>
      <guid>https://dev.to/road511/your-eta-assumes-the-border-takes-an-hour-it-can-take-four-days-4g3</guid>
      <description>&lt;p&gt;Inside Schengen, a border is a road sign. You cross it at motorway speed and your ETA never notices. Twenty years of that has trained every routing engine, every TMS, and every dispatcher's intuition to treat a European frontier as a rounding error.&lt;/p&gt;

&lt;p&gt;Then a load runs to the EU's &lt;em&gt;external&lt;/em&gt; frontier, and the rounding error becomes the trip.&lt;/p&gt;

&lt;p&gt;On the morning of 16 July 2026, the loaded-truck queue at Yahodyn–Dorohusk — the main Ukraine–Poland freight crossing — was reporting a wait of &lt;strong&gt;5,964 minutes&lt;/strong&gt;. That is &lt;strong&gt;four days and three hours&lt;/strong&gt;, with &lt;strong&gt;1,006 trucks&lt;/strong&gt; physically in the queue. The queue for empty trucks at the same crossing was worse: &lt;strong&gt;6,555 minutes&lt;/strong&gt;, or four days and thirteen hours, with 1,334 trucks waiting.&lt;/p&gt;

&lt;p&gt;No amount of clever road-network routing tells you that. It isn't a traffic condition, it isn't congestion, and it will not resolve in twenty minutes. It is a queue with a length, and the only way to know about it is to read the queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "Average Border Wait" Is a Useless Number
&lt;/h2&gt;

&lt;p&gt;The instinct is to reach for an average and pad the ETA. The live data says don't bother.&lt;/p&gt;

&lt;p&gt;Across the 38 truck checkpoints on Ukraine's EU-facing frontier that morning, the distribution wasn't a bell curve. It was two clusters and nothing in between: &lt;strong&gt;21 checkpoints reported a wait of exactly zero&lt;/strong&gt;, and the rest ran from about ten hours to four and a half days. There was essentially no middle.&lt;/p&gt;

&lt;p&gt;That shape is the whole planning problem. A border crossing is either flowing — in which case it costs you nothing and needs no padding — or it is a multi-day parking lot. An average across those two states describes no crossing that actually exists, and a fixed buffer is either wasted margin or nowhere near enough. The only useful question is binary and current: &lt;em&gt;is this specific crossing, for this kind of truck, moving right now?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Queue Is Published. It Just Isn't in Your Stack.
&lt;/h2&gt;

&lt;p&gt;Ukraine runs an electronic queue system for border crossings — єЧерга (eCherha) — operated by the State Customs Service. Trucks book and hold a place in it, and it publishes the live state of every checkpoint: how long the queue is taking, and how many vehicles are in it. The underlying dataset is open data, published under CC BY 4.0.&lt;/p&gt;

&lt;p&gt;NAPSPAN normalizes it into the same &lt;code&gt;border_crossings&lt;/code&gt; feature type that carries the CBP and CBSA land-border waits on the North American side, so it answers to the same &lt;code&gt;/features&lt;/code&gt; call as everything else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: &lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.napspan.com/api/v1/features?type=border_crossings&amp;amp;jurisdiction=UKR"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echerga-truck-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UKR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UKR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"feature_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"border_crossings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ягодин – Дорогуськ (для вантажівок ≥ 7,5 тонн)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;51.1880897&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;23.8105033&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"is_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"source_feed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echerga"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"vehicle_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"truck"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"queue_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dynamic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"wait_time_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5964&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"vehicles_in_queue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1006&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"neighbor_country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Польща"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"is_paused"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"last_updated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-16T09:40:00Z"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"has_more"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole frontier is in there. That call returns 68 queues facing five neighbours — Poland, Slovakia, Hungary, Romania, and Moldova — each with a live wait and a live queue depth, refreshed on a ten-minute poll. &lt;strong&gt;38 of them are truck queues&lt;/strong&gt;; the rest are the coach queues the same system publishes. &lt;code&gt;vehicle_type&lt;/code&gt; tells them apart, and it's the field to filter on — a coach queue says nothing about your freight lane.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part That Changes Dispatch: It Rides the Route
&lt;/h2&gt;

&lt;p&gt;Querying the frontier by jurisdiction is the manual path. The one that changes a dispatcher's day is that &lt;strong&gt;the queue comes back attached to the route.&lt;/strong&gt; Send a normal truck routing request to &lt;code&gt;POST /api/v1/routing/route&lt;/code&gt;, and any border crossing on the corridor arrives in the &lt;code&gt;warnings[]&lt;/code&gt; channel — no second call, no spatial join on your side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"warnings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"border_crossing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ягодин – Дорогуськ (для вантажівок ≥ 7,5 тонн)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Commercial border wait ~4 d 3 h. 1006 trucks in queue."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"as_of"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-16T09:40:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;288000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"projected_arrival_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-16T14:05:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UKR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;23.8105033&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;51.1880897&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"commercial_wait_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5964&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"vehicles_in_queue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1006&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few deliberate choices in that object are worth calling out, because they are what make it usable rather than merely present:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The wait reads like a wait.&lt;/strong&gt; &lt;code&gt;description&lt;/code&gt; renders as &lt;code&gt;4 d 3 h&lt;/code&gt;, not &lt;code&gt;~5964 min&lt;/code&gt;. Nobody can act on 5,964 minutes. The machine-readable &lt;code&gt;commercial_wait_minutes&lt;/code&gt; stays in minutes so your own thresholds keep working — the humanising is presentation, not data loss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue depth comes with it.&lt;/strong&gt; &lt;code&gt;vehicles_in_queue&lt;/code&gt; is the figure that makes the wait concrete. "Four days" is abstract; "a thousand trucks are in front of you" is a decision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Severity is banded for a truck.&lt;/strong&gt; &lt;code&gt;info&lt;/code&gt; under 15 minutes, &lt;code&gt;warning&lt;/code&gt; from 15, &lt;code&gt;critical&lt;/code&gt; past 45 or whenever the crossing is closed. Set &lt;code&gt;min_severity: "warning"&lt;/code&gt; and a flowing border stays silent while a jammed one shouts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only the truck queue counts.&lt;/strong&gt; The feed publishes each checkpoint once per vehicle type. A ninety-minute coach queue at the same crossing never bands your freight route — we key severity on the truck queue alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You get the crossing before the driver does.&lt;/strong&gt; &lt;code&gt;distance_along_route_m&lt;/code&gt; and &lt;code&gt;projected_arrival_time&lt;/code&gt; place it on the corridor, so this is a decision made at dispatch — reroute to a quieter checkpoint, re-sequence the load, warn the customer — not a surprise found at hour ninety.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It rides the same request as the corridor's &lt;a href="https://napspan.com/blog/cross-border-truck-restrictions-vms.html" rel="noopener noreferrer"&gt;truck restrictions and VMS signs&lt;/a&gt;, its &lt;a href="https://napspan.com/blog/toll-cost-routing-api.html" rel="noopener noreferrer"&gt;toll costs&lt;/a&gt;, and &lt;a href="https://napspan.com/blog/hos-break-planning-api.html" rel="noopener noreferrer"&gt;561/2006 break planning&lt;/a&gt;. One call returns a drivable truck route, the rules that would stop it, where the driver must legally rest, what it costs — and now how long the frontier will hold it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Expect From the Data
&lt;/h2&gt;

&lt;p&gt;A few honest notes, so the coverage behaves the way you expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This is the external frontier, not every EU border.&lt;/strong&gt; Intra-Schengen crossings do not queue, so there is nothing to report and no warning to give. The live queue layer covers Ukraine's EU-facing checkpoints, which is where the EU's freight border problem actually lives. Other external frontiers do not yet have an equivalent live feed wired — we will say so plainly rather than pad the map.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One physical crossing publishes several queues.&lt;/strong&gt; Yahodyn–Dorohusk reports loaded trucks, empty trucks, and certain cargo categories as separate rows at the same coordinates, and they diverge — on 16 July the empty-truck queue ran ten hours longer than the loaded one. Read the checkpoint name; don't assume one number per border post.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paused checkpoints drop out.&lt;/strong&gt; When the operator suspends a queue, the feed flags it and we mark the crossing inactive, so it stops generating warnings rather than reporting a stale wait as live. Ten of the 38 were paused that morning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some checkpoints are scheduled, not live.&lt;/strong&gt; Where a checkpoint runs on booked slots instead of a live queue, there is no wait to report — it stays &lt;code&gt;info&lt;/code&gt; and we publish no invented number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's a planning input.&lt;/strong&gt; The queue is authoritative about the queue. It is not a promise about customs, paperwork, or what the officer at the window decides.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Common Thread
&lt;/h2&gt;

&lt;p&gt;This is the same shape as the &lt;a href="https://napspan.com/blog/european-road-data-challenges.html" rel="noopener noreferrer"&gt;five road-data problems every cross-border European fleet hits&lt;/a&gt;: the information exists, a public authority publishes it, and it is in a national format that nobody's routing stack speaks. Solving that once per country is a permanent maintenance commitment that competes with the product you actually sell.&lt;/p&gt;

&lt;p&gt;NAPSPAN does that aggregation once. The Ukrainian electronic queue arrives in the same &lt;code&gt;border_crossings&lt;/code&gt; schema as a US CBP lane wait, filterable by jurisdiction, attached to your route on the same request as the tolls and the rest breaks, served from Europe, and licence-classified before it ships — this feed under CC BY 4.0, credited on every record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://napspan.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — &lt;code&gt;/features&lt;/code&gt; with &lt;code&gt;type=border_crossings&lt;/code&gt;, plus &lt;code&gt;POST /routing/route&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.napspan.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — the frontier, as it stands right now&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.napspan.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://napspan.com/blog/eu-border-queue-wait-times.html?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=eu-border-queue-wait-times&amp;amp;utm_content=dev-09" rel="noopener noreferrer"&gt;napspan.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>logistics</category>
      <category>eu</category>
      <category>api</category>
      <category>trucking</category>
    </item>
    <item>
      <title>Truck Parking Availability on the Route, Before the Clock Runs Out</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Fri, 24 Jul 2026 07:31:59 +0000</pubDate>
      <link>https://dev.to/road511/truck-parking-availability-on-the-route-before-the-clock-runs-out-17n4</link>
      <guid>https://dev.to/road511/truck-parking-availability-on-the-route-before-the-clock-runs-out-17n4</guid>
      <description>&lt;p&gt;It's 2am and a driver's 11-hour clock has maybe 30 minutes left on it. The rest area at the next exit is full — trucks are already lined up on the on-ramp shoulder. The next public lot is 40 miles ahead, which the clock won't cover, and nobody knows whether &lt;em&gt;it&lt;/em&gt; has room either. So the driver does what tens of thousands do every night: parks somewhere they shouldn't, or keeps rolling past the legal limit. Truck parking consistently ranks among the trucking industry's top concerns year after year, and the reason it stays unsolved at the cab level is simple — &lt;strong&gt;a legal break is worthless if there's nowhere to stop, and the driver finds out too late to do anything about it.&lt;/strong&gt; That last part is the part software can fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding a Space Is a Planning Problem, Not a Luck Problem
&lt;/h2&gt;

&lt;p&gt;The shortage is real and structural — there are far more trucks needing overnight rest than there are legal spaces for them. But a huge share of the daily pain isn't the raw shortage; it's the &lt;em&gt;information gap&lt;/em&gt;. The driver can't see which lots are full until pulling in, and the dispatcher planning the load has no parking picture at all. A stop that looks fine on a map at 9am dispatch is a gravel shoulder by the time the truck arrives at midnight.&lt;/p&gt;

&lt;p&gt;Turning that into a planning decision needs two things the route already implies: &lt;strong&gt;where the parking is&lt;/strong&gt; along the corridor, and &lt;strong&gt;how much of it is actually open&lt;/strong&gt; when the truck will be there. The first has existed for years. The second is finally becoming available — in pieces — and that's the part that changes the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Exists — in Two Forms
&lt;/h2&gt;

&lt;p&gt;Truck-parking data comes from two complementary public sources, and they answer different questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The national inventory — where parking exists.&lt;/strong&gt; A federal facilities dataset catalogs public rest areas and known truck-parking sites across the country, with locations, capacity, and basic attributes. This is your map of &lt;em&gt;where&lt;/em&gt; a truck can legally stop — comprehensive in coverage, but static: it tells you a site has 60 spaces, not how many are free right now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live availability — how many spaces are open.&lt;/strong&gt; A growing number of state DOTs run &lt;strong&gt;Truck Parking Information Management Systems (TPIMS)&lt;/strong&gt; — the in-pavement sensors and roadside signs you've seen reading "REST AREA — 12 SPACES." Those same counts are published as data. Coverage is still a handful of states rather than the whole map, but where a site is instrumented, you get a live, refreshing count of open spaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The catch is the one that shows up everywhere in this domain: these are separate feeds in separate formats from separate agencies, each on its own schema and refresh cadence — the same per-source integration tax we wrote about in &lt;a href="https://road511.com/blog/north-american-road-data-challenges.html" rel="noopener noreferrer"&gt;the five road-data problems every NA fleet team knows&lt;/a&gt;. Stitched together and normalized, though, they become one answer: the full map of where to stop, with live counts layered on wherever a state publishes them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Parking Stop Needs to Tell You
&lt;/h2&gt;

&lt;p&gt;A parking feature is only useful to a planner when it answers the operational questions, not just "there's a rest area here":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Where, and which side&lt;/strong&gt; — the exact location and the direction of travel it serves, so a northbound truck isn't routed to a southbound-only lot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What kind, and how big&lt;/strong&gt; — public rest area versus known truck stop, and the total truck-space capacity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How many are open right now&lt;/strong&gt; — the live available-space count where the site is instrumented, with the timestamp of the last update so stale data is obvious rather than misleading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's there&lt;/strong&gt; — amenities that decide whether a stop is viable for a 10-hour reset versus a quick 30-minute break.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting It on the Corridor
&lt;/h2&gt;

&lt;p&gt;The operational need isn't "a database of rest areas." It's "show me the truck parking &lt;em&gt;on this route&lt;/em&gt;, with whatever live counts exist, before the driver commits to a stop." Once the inventory and the TPIMS feeds are normalized into one schema, &lt;code&gt;/truck/corridor&lt;/code&gt; buffers the straight line between two points and returns every truck-related feature that intersects it — parking among them — instead of a fan-out across federal and state sources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/truck/corridor?
  from_lat=41.88&amp;amp;from_lng=-87.63&amp;amp;
  to_lat=39.10&amp;amp;to_lng=-94.58&amp;amp;
  buffer_km=5

{
  "features": [
    {
      "id": "tpims-IL00055IS002210",
      "feature_type": "truck_parking",
      "name": "I-55 NB Rest Area (Lincoln)",
      "jurisdiction": "IL",
      "road_name": "I-55 NB",
      "latitude": 40.71, "longitude": -89.62,
      "properties": {
        "capacity": 62,
        "available_spaces": 9,
        "occupancy_pct": 85,
        "data_updated": "2026-06-26T06:18:00Z",
        "amenities": ["Vending Machines"],
        "open": true,
        "_distance_km": "1.20"
      }
    },
    {
      "id": "BTS-tp-4471",
      "feature_type": "truck_parking",
      "name": "US-67 Truck Parking",
      "jurisdiction": "IL",
      "road_name": "US-67",
      "latitude": 39.80, "longitude": -90.20,
      "properties": {
        "capacity": 40,
        "county": "Morgan",
        "milepost": "12.4",
        "truck_parking": true,
        "_distance_km": "3.80"
      }
    }
  ],
  "total": 2
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One call, the Chicago–Kansas City corridor, every known parking site on it, each stamped with &lt;code&gt;_distance_km&lt;/code&gt; off the line. Where a state runs TPIMS, the site carries a live &lt;code&gt;available_spaces&lt;/code&gt; / &lt;code&gt;occupancy_pct&lt;/code&gt; and a &lt;code&gt;data_updated&lt;/code&gt; timestamp; where it doesn't, the site still shows up from the national BTS inventory with &lt;code&gt;capacity&lt;/code&gt; only and no &lt;code&gt;available_spaces&lt;/code&gt; key at all — an honest "we know it's here, we don't have a live count" instead of a false promise. That distinction is the whole point: a planner can prefer instrumented sites near the deadline and fall back to known lots everywhere else. (&lt;code&gt;/truck/corridor&lt;/code&gt; is a paid-plan endpoint; the same features are on &lt;code&gt;/features?type=truck_parking&amp;amp;bbox=…&lt;/code&gt; for an area query.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Parking Meets the HOS Clock
&lt;/h2&gt;

&lt;p&gt;Parking and hours-of-service are the same problem looked at from two ends. The clock decides &lt;em&gt;when&lt;/em&gt; the truck has to stop; the parking data decides &lt;em&gt;whether it can&lt;/em&gt;. On its own, a list of nearby lots is useful; tied to the driver's remaining hours, it becomes a go/no-go answer.&lt;/p&gt;

&lt;p&gt;That's exactly how it connects to &lt;a href="https://road511.com/blog/hos-break-planning-api.html" rel="noopener noreferrer"&gt;HOS break planning on the route&lt;/a&gt;: send the driver's clock with the route, and each required break and drive-limit stop comes back with the truck parking reachable &lt;em&gt;before&lt;/em&gt; the deadline — and a &lt;code&gt;feasible&lt;/code&gt; flag when nothing legal is in range. Layer live availability on top and "reachable" sharpens into "reachable and likely to have room," so dispatch can move the stop earlier while there's still slack in the clock, instead of discovering the lot is full when there's no clock left to spend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Build With It
&lt;/h2&gt;

&lt;p&gt;With truck parking in the same schema as everything else on the route, the integrations get short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dispatch that plans the overnight, not just the delivery&lt;/strong&gt; — place the rest stop at load planning, with capacity and live counts in view, not at 2am.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver alerts as the clock winds down&lt;/strong&gt; — surface the next few viable lots ahead, instrumented ones flagged, while there are still hours to reach them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest ETAs&lt;/strong&gt; — a reset has to happen somewhere; planning the actual stop makes the arrival estimate real instead of optimistic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Corridor risk and compliance views&lt;/strong&gt; — parking alongside bridge clearances, work zones, and incidents in one layer (see &lt;a href="https://road511.com/blog/truck-routing-hazard-warnings.html" rel="noopener noreferrer"&gt;truck routing with corridor hazard warnings&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Provenance, and the Honest Limits
&lt;/h2&gt;

&lt;p&gt;Parking data comes from official sources — the federal facilities inventory and state DOT TPIMS feeds — which is what makes it authoritative rather than crowd-guessed. We don't overstate it. Live counts exist only where a state has instrumented a site, so most of the map is still inventory-only; capacity and amenity detail vary by source; and a live count is only as fresh as the feed behind it, which is why every count carries its update timestamp. This is &lt;em&gt;information&lt;/em&gt;, not a reservation — the public feeds tell you what's open, not hold a space for you; booking lives in separate private systems. The job we take on is keeping the federal inventory and each state's TPIMS feed parsed, current, and in one shape as they grow and drift — so it's not yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — the &lt;code&gt;features&lt;/code&gt; and corridor surface, including &lt;code&gt;truck_parking&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see truck-parking sites and live counts where states publish them&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The next legal stop, with room — before the clock runs out
&lt;/h3&gt;

&lt;p&gt;The national truck-parking inventory plus live space counts where states publish them, normalized into one schema and queryable along any corridor — and tied to the HOS clock so the break is planned, not gambled. Free 14-day trial. No credit card. &lt;strong&gt;&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Get a free API key&lt;/a&gt;&lt;/strong&gt; or &lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;read the docs&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://road511.com/blog/truck-parking-availability-api.html" rel="noopener noreferrer"&gt;road511.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>trucking</category>
      <category>api</category>
      <category>logistics</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Five Road-Data Problems Every Cross-Border European Fleet Hits</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Sun, 12 Jul 2026 07:18:21 +0000</pubDate>
      <link>https://dev.to/road511/five-road-data-problems-every-cross-border-european-fleet-hits-2i3</link>
      <guid>https://dev.to/road511/five-road-data-problems-every-cross-border-european-fleet-hits-2i3</guid>
      <description>&lt;p&gt;If you build routing, dispatch, or telematics software for European trucking, the routing algorithm is rarely what slows the roadmap down. The road data is. A truck plan from Rotterdam to Bucharest crosses a dozen jurisdictions, and each one publishes what's happening on its roads — incidents, tolls, restrictions, charging — its own way, on its own schedule, under its own rules. Below are the five problems that come up in nearly every conversation we have with European fleet and logistics-software teams. None are exotic. All of them quietly burn engineering time.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Twenty-Seven Countries, Twenty-Seven Access Points
&lt;/h2&gt;

&lt;p&gt;The EU ITS Directive did get every Member State to stand up a National Access Point (NAP) publishing road data, mostly as DATEX II. That sounds like a solved problem until you actually connect to a second country. The standard leaves room, and the NAPs use all of it — different DATEX profiles and versions, different authentication, different update cadences, different ideas of what a given field means. And plenty of operationally useful data never travels as DATEX II at all: it sits on municipal open-data portals or in national feeds that predate the plumbing.&lt;/p&gt;

&lt;p&gt;So a team that wants genuine continental coverage ends up writing and &lt;em&gt;maintaining&lt;/em&gt; a separate integration per country, then babysitting all of them as feeds quietly change shape. This is the problem that eats the most time, because it never finishes. We wrote up &lt;a href="https://napspan.com/blog/datex-ii-normalization-european-naps.html" rel="noopener noreferrer"&gt;how we normalize DATEX II across the European NAPs&lt;/a&gt; and &lt;a href="https://napspan.com/blog/open-data-beyond-datex-ii.html" rel="noopener noreferrer"&gt;how the non-DATEX sources fold into the same schema&lt;/a&gt; — the short version is that the per-country mess becomes one consistent &lt;code&gt;events&lt;/code&gt; and &lt;code&gt;features&lt;/code&gt; contract, and keeping the adapters alive becomes our problem instead of yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Toll Bill Changes at Every Border
&lt;/h2&gt;

&lt;p&gt;There is no single European toll. There are vignettes, distance-based schemes, per-axle HGV charges, urban congestion fees, and tunnel and bridge tolls — each country with its own model, its own vehicle classes, and its own emission-class surcharges. A route that looks cheapest on distance can be the most expensive once the tolls land, and the difference between a 5-axle Euro VI rig and a smaller van is real money on every leg.&lt;/p&gt;

&lt;p&gt;For a planner, an estimate that ignores vehicle class is an estimate that's wrong at invoice time. For a software team, pricing a cross-border route by hand means modelling a dozen national toll systems and keeping them current as rates change. We pulled this together so a route comes back with a vehicle-class-aware toll breakdown per country — see &lt;a href="https://napspan.com/blog/toll-cost-routing-api.html" rel="noopener noreferrer"&gt;vehicle-class-aware toll costs across Europe&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Where the Clock Runs Out — and Whether There's Parking
&lt;/h2&gt;

&lt;p&gt;EU Regulation 561/2006 driving-time rules and the chronic shortage of secure truck parking are really one problem wearing two hats. Knowing a driver will hit the daily driving limit at 18:40 is only half of it. The half that strands a truck — or forces an unsafe roadside stop — is whether there's a legal, reachable place to take the break when the clock runs out, and that answer needs both the driving-time projection &lt;em&gt;and&lt;/em&gt; live parking data on the same corridor.&lt;/p&gt;

&lt;p&gt;Most teams solve the first half and discover the second half at the lay-by. We tied them together: send the driver's clock with the route and get back every required break, each paired with the parking reachable before the deadline and a &lt;code&gt;feasible&lt;/code&gt; flag when none is. That's the subject of &lt;a href="https://napspan.com/blog/hos-break-planning-api.html" rel="noopener noreferrer"&gt;break planning under EU 561/2006&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Ban You Didn't Know About Until the Border
&lt;/h2&gt;

&lt;p&gt;Cross-border trucking runs on a patchwork of national restrictions that no consumer map knows about: weekend and night driving bans, sectoral and sector-specific bans on Alpine corridors, low-emission zones in city centres, country-by-country weight and dimension limits, and seasonal restrictions. A plan that's legal in one country can be illegal across the line at the same hour — and the penalty for finding out the hard way is a fine, a forced wait, or a turned-back load.&lt;/p&gt;

&lt;p&gt;The operational need isn't "a list of every rule in Europe." It's "tell me about the restrictions and variable-message signs &lt;em&gt;on this corridor&lt;/em&gt;, before the driver gets there" — restrictions and signage from the official national feeds, normalized into the same queryable &lt;code&gt;features&lt;/code&gt; the rest of the API speaks, filterable by country and bounding box. One schema for a German night ban and an Austrian sectoral ban means you check one place instead of twenty-seven.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Charging an Electrifying Fleet, Across the Gaps
&lt;/h2&gt;

&lt;p&gt;As fleets electrify, "where can this vehicle actually charge on the corridor" becomes a routing input, not a nice-to-have. The EU's AFIR regulation obliges charge-point operators to publish charging data through the NAPs, and where a Member State carries a native EV feed we serve it. But AFIR rollout is uneven, and several countries don't expose a mature EV feed yet — so most of our EV coverage comes from an openly-licensed registry rather than from live per-connector telemetry.&lt;/p&gt;

&lt;p&gt;That registry gives you coordinates, connector standards, and power ratings, and where a source publishes its own last-checked time we pass it through as &lt;code&gt;properties.source_updated_at&lt;/code&gt; so you can judge how fresh an entry is — typically days to months, because a community registry is not live telemetry. See &lt;a href="https://napspan.com/blog/open-data-beyond-datex-ii.html" rel="noopener noreferrer"&gt;folding open data into one European API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Common Thread
&lt;/h2&gt;

&lt;p&gt;Every one of these is the same shape of problem: the data exists, but it's fragmented across national authorities, published in incompatible formats, governed by different licenses, and only useful once it's joined to a route and kept fresh. Each one is solvable in-house — and each one, solved in-house, becomes a permanent maintenance commitment that competes with the product you actually sell.&lt;/p&gt;

&lt;p&gt;That's the entire premise of NAPSPAN: do that aggregation once, for everyone, and hand you one normalized API — events, features, tolls, restrictions, charging, routing with corridor hazards — instead of twenty-seven integrations to keep alive. The data is served from Europe, every source is license-classified before it ships, and you spend your engineering time on the part that makes your product yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://napspan.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — &lt;code&gt;events&lt;/code&gt;, &lt;code&gt;features&lt;/code&gt;, routing, and the &lt;code&gt;?jurisdiction=&lt;/code&gt; / &lt;code&gt;?type=&lt;/code&gt; filters in one reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.napspan.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see the normalized data across the European NAPs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.napspan.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;One API instead of twenty-seven integrations.&lt;/strong&gt; Continental coverage — incidents, tolls, truck restrictions, EV charging, and routing — normalized into one GeoJSON schema and served from Europe. Free 14-day trial. No card. &lt;a href="https://portal.napspan.com/signup" rel="noopener noreferrer"&gt;Get a free API key&lt;/a&gt; or &lt;a href="https://napspan.com/docs.html" rel="noopener noreferrer"&gt;read the docs&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://napspan.com/blog/european-road-data-challenges.html" rel="noopener noreferrer"&gt;napspan.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>logistics</category>
      <category>eu</category>
      <category>api</category>
      <category>trucking</category>
    </item>
    <item>
      <title>Five Road-Data Problems Every North American Fleet Team Knows Too Well</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Wed, 08 Jul 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/road511/five-road-data-problems-every-north-american-fleet-team-knows-too-well-nle</link>
      <guid>https://dev.to/road511/five-road-data-problems-every-north-american-fleet-team-knows-too-well-nle</guid>
      <description>&lt;p&gt;If you build routing, dispatch, or telematics software for North American trucking, your roadmap has a recurring tax line on it — road data. Not the routing algorithm, not the UI: the unglamorous job of knowing what's actually happening on the pavement, in fifty states and a dozen provinces that each run their own system their own way. Below are the five problems that come up in nearly every conversation we have with fleet and logistics-software teams. None of them are exotic. All of them quietly burn engineering time.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Fifty States, Fifty Dialects
&lt;/h2&gt;

&lt;p&gt;There is no single "US traffic API." There are dozens of state and provincial 511 systems, and they agree on almost nothing — different formats (some JSON, some XML, some an ArcGIS layer, some a scraped map endpoint), different field names for the same concept, different authentication, different refresh rates, different ideas of what "severity" even means. A team that wants national coverage ends up writing and &lt;em&gt;maintaining&lt;/em&gt; a separate integration per jurisdiction, then babysitting all of them as feeds silently change shape.&lt;/p&gt;

&lt;p&gt;This is the problem that eats the most time, because it never finishes. A feed that worked in March drops a field in September; a state migrates its portal and the old endpoint 404s. We wrote up &lt;a href="https://road511.com/blog/normalizing-511-traffic-apis.html" rel="noopener noreferrer"&gt;how we normalize 30+ of these systems into one schema&lt;/a&gt; — the short version is that the per-jurisdiction mess becomes one consistent &lt;code&gt;events&lt;/code&gt; and &lt;code&gt;features&lt;/code&gt; contract, and keeping the adapters alive becomes our problem instead of yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Bridge Your Truck Doesn't Fit Under
&lt;/h2&gt;

&lt;p&gt;A consumer nav app routes a 13′6″ trailer under a 12′8″ bridge without a second thought, because it has no idea a truck is driving. Low-clearance strikes are expensive, dangerous, and almost entirely preventable with the right data on the route — but that data is scattered across state bridge inventories and the federal National Bridge Inventory, in formats never meant for real-time routing.&lt;/p&gt;

&lt;p&gt;The operational need isn't "a database of bridges." It's "tell me about the low ones &lt;em&gt;on this corridor&lt;/em&gt;, before the driver gets there." That's a join between clearance data and the route geometry, and it's exactly the kind of thing teams underestimate until the first claim comes in. More on that in &lt;a href="https://road511.com/blog/bridge-clearance-api-fleet-routing.html" rel="noopener noreferrer"&gt;bridge clearances for fleet routing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Limits That Appear Every Spring
&lt;/h2&gt;

&lt;p&gt;Seasonal weight restrictions — spring thaw load limits, frost laws — are one of the most under-served data problems in North American trucking. They're posted by states and counties on their own schedules, in their own formats, sometimes as a PDF, and they change as the ground does. A load that's legal one week is over the limit the next, on the same road.&lt;/p&gt;

&lt;p&gt;For a planner, the cost of missing one is a fine, a forced reroute, or a refused load. For a software team, the cost is trying to track a hundred separate posting authorities by hand. We pulled this together so the restriction shows up as structured, queryable data alongside everything else — see &lt;a href="https://road511.com/blog/spring-load-limits-api-trucking.html" rel="noopener noreferrer"&gt;spring load limits as an API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Where the Clock Runs Out — and Whether There's Parking
&lt;/h2&gt;

&lt;p&gt;Hours-of-Service compliance and the chronic truck-parking shortage are really one problem wearing two hats. Knowing a driver will hit the 11-hour driving limit at 6:40 PM is only half of it. The half that strands a truck is whether there's a legal place to stop when the clock runs out — and that answer needs both the HOS projection &lt;em&gt;and&lt;/em&gt; live parking data on the same corridor.&lt;/p&gt;

&lt;p&gt;Most teams solve the first half and discover the second half at the roadside. We tied them together: send the driver's clock with the route and get back every required break and stop, each paired with the parking reachable before the deadline and a &lt;code&gt;feasible&lt;/code&gt; flag when none is. That's the subject of &lt;a href="https://road511.com/blog/hos-break-planning-api.html" rel="noopener noreferrer"&gt;HOS break planning on the route&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The Data Is Stale the Moment You Cache It
&lt;/h2&gt;

&lt;p&gt;Incidents, closures, and work zones are only useful while they're current. A closure feed you poll once an hour is a closure feed that's wrong for up to an hour — and a routing decision made on a stale closure is a driver sitting in a backup your system told them to enter. But polling every state's feed aggressively, parsing it, deduping it, and aging out the resolved events is its own piece of infrastructure that has nothing to do with your product.&lt;/p&gt;

&lt;p&gt;The honest answer here isn't "real-time magic" — it's that the freshness and lifecycle work should already be done by the time you query. Events carry their own start and end times and move from active to archived on their own; you ask for what's current on a corridor and get the current answer, with the polling cadence handled upstream. Pair it with the camera feeds for visual confirmation (&lt;a href="https://road511.com/blog/traffic-camera-feeds-api.html" rel="noopener noreferrer"&gt;traffic camera feeds&lt;/a&gt;) and the dispatcher has eyes on the corridor without standing up a single scraper.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Common Thread
&lt;/h2&gt;

&lt;p&gt;Every one of these is the same shape of problem: the data exists, but it's fragmented across authorities, published in incompatible formats, and only useful once it's joined to a route and kept fresh. Each one is solvable in-house — and each one, solved in-house, becomes a permanent maintenance commitment that competes with the product you actually sell.&lt;/p&gt;

&lt;p&gt;That's the entire premise of Road511: do that aggregation once, for everyone, and hand you one normalized API — events, features, bridge clearances, restrictions, routing with corridor hazards — instead of fifty integrations to keep alive. You spend your engineering time on the part that makes your product yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — the &lt;code&gt;events&lt;/code&gt;, &lt;code&gt;features&lt;/code&gt;, and truck-routing surface in one reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see the normalized data across states and provinces&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://road511.com/blog/north-american-road-data-challenges.html" rel="noopener noreferrer"&gt;road511.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>trucking</category>
      <category>api</category>
      <category>logistics</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Hours-of-Service Break Planning, Right on the Route</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Mon, 22 Jun 2026 10:00:44 +0000</pubDate>
      <link>https://dev.to/road511/hours-of-service-break-planning-right-on-the-route-2end</link>
      <guid>https://dev.to/road511/hours-of-service-break-planning-right-on-the-route-2end</guid>
      <description>&lt;p&gt;A consumer nav app tells the driver where to turn. It will not tell the dispatcher where the 11-hour driving clock runs out — and, more importantly, whether there is legal parking when it does. That second question is the one that strands a truck at 11&amp;nbsp;PM on the shoulder of an off-ramp with every nearby lot already full.&lt;/p&gt;

&lt;p&gt;Road511’s routing endpoint now answers it. Send the driver’s Hours-of-Service clock along with the route, and the response carries an &lt;code&gt;hos[]&lt;/code&gt; array: every point on the corridor where the driver must take a break or stop driving under the selected regime, the projected time they reach it, the legal deadline, and — the part that matters operationally — the truck parking and rest areas actually reachable before that deadline.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;It rides the same call as everything else routing does: &lt;code&gt;POST /api/v1/routing/route&lt;/code&gt;. You already send an origin, a destination, and a &lt;code&gt;truck&lt;/code&gt; profile. To get the HOS projection, add an &lt;code&gt;hos&lt;/code&gt; block &lt;em&gt;inside&lt;/em&gt; the truck object describing the driver’s clock at departure.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST "https://api.road511.com/api/v1/routing/route" \
  -H "X-API-Key: your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "origin":      { "lat": 41.8781, "lng": -87.6298 },
    "destination": { "lat": 39.7392, "lng": -104.9903 },
    "departure_time": "2026-06-08T06:00:00Z",
    "truck": {
      "profile":  "tractor",
      "weight_t": 36.0,
      "height_m": 4.2,
      "axles":    5,
      "hos": {
        "ruleset":           "us",
        "drive_remaining_s": 39600,
        "duty_remaining_s":  50400,
        "since_break_s":     0
      }
    },
    "enrichment": {
      "include_features": ["truck_parking", "rest_areas"]
    }
  }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That’s a Chicago→Denver run for a fresh driver on US rules: 11 hours of driving left (&lt;code&gt;39600&lt;/code&gt; s), a 14-hour duty window (&lt;code&gt;50400&lt;/code&gt; s), and zero driving time since the last break. Every counter is “seconds remaining” against the named ruleset’s limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The HOS Clock
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;hos&lt;/code&gt; object is the driver’s state, not a fixed policy. Store only the &lt;code&gt;ruleset&lt;/code&gt; on a reusable truck profile — the per-trip counters are supplied inline on each request and merge on top.&lt;/p&gt;

&lt;p&gt;Field&lt;/p&gt;

&lt;p&gt;Meaning&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ruleset&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The regime: &lt;code&gt;us&lt;/code&gt;, &lt;code&gt;canada_south&lt;/code&gt;, or &lt;code&gt;eu&lt;/code&gt;. Empty defaults from the deployment region.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drive_remaining_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Driving-limit budget left.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;duty_remaining_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On-duty window budget left.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;since_break_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Driving time accrued since the last qualifying break.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;elapsed_remaining_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Canada’s elapsed (wall-clock) window budget.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cycle_remaining_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Weekly / cycle budget (60/70-hour US, 70/120-hour Canada, 56h/90h EU).&lt;/p&gt;

&lt;p&gt;Any counter you leave out is read as “a fresh driver” — the ruleset’s full limit. The counters are pointers under the hood, so an explicit &lt;code&gt;0&lt;/code&gt; (“out of hours right now”) is distinct from “not supplied.”&lt;/p&gt;

&lt;h2&gt;
  
  
  The Response
&lt;/h2&gt;

&lt;p&gt;The HOS projection is attached to the primary route as an &lt;code&gt;hos[]&lt;/code&gt; array, alongside the &lt;code&gt;warnings[]&lt;/code&gt; and &lt;code&gt;features[]&lt;/code&gt; you may already be using.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "routes": [
    {
      "summary": { "distance_m": 1480200, "duration_s": 54600 },
      "geometry": { "type": "LineString", "coordinates": [ /* ... */ ] },
      "hos": [
        {
          "reason": "break_required",
          "distance_along_route_m": 642000,
          "projected_time":  "2026-06-08T14:00:00Z",
          "legal_deadline":  "2026-06-08T14:00:00Z",
          "feasible": true,
          "suggested_stops": [
            { "type": "rest_area", "name": "I-80 Walcott Rest Area",
              "distance_along_route_m": 631500, "capacity": 22,
              "geometry": { "type": "Point", "coordinates": [-90.7830, 41.5900] } },
            { "type": "truck_parking", "name": "I-80 Tipton Truck Parking",
              "distance_along_route_m": 612000,
              "capacity": 140, "available_spots": 31,
              "as_of": "2026-06-08T13:20:00Z",
              "geometry": { "type": "Point", "coordinates": [-91.1300, 41.7700] } }
          ]
        },
        {
          "reason": "drive_limit",
          "distance_along_route_m": 905800,
          "projected_time":  "2026-06-08T17:30:00Z",
          "legal_deadline":  "2026-06-08T17:30:00Z",
          "feasible": false,
          "suggested_stops": []
        }
      ]
    }
  ],
  "route_id": "rt_5d91c7e2"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Each stop carries a &lt;code&gt;reason&lt;/code&gt; — the limit that trips there — the &lt;code&gt;distance_along_route_m&lt;/code&gt; where it lands, the &lt;code&gt;projected_time&lt;/code&gt; the driver reaches it, and the &lt;code&gt;legal_deadline&lt;/code&gt; the rule allows. The four reasons:&lt;/p&gt;

&lt;p&gt;Reason&lt;/p&gt;

&lt;p&gt;What tripped&lt;/p&gt;

&lt;p&gt;&lt;code&gt;break_required&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The driver hit the drive-time-since-break limit and owes a rest break.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drive_limit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The daily driving limit is exhausted — the driver must stop for the day.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;duty_window&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The on-duty window closed (or, in Canada, the elapsed window) before the driving limit did.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cycle_limit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The weekly/cycle budget ran out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part That Matters: Reachable Parking
&lt;/h2&gt;

&lt;p&gt;Projecting &lt;em&gt;where&lt;/em&gt; the clock runs out is the easy half. The operationally useful half is whether the driver can actually stop there. Each &lt;code&gt;hos&lt;/code&gt; stop attaches a &lt;code&gt;suggested_stops[]&lt;/code&gt; list — the truck parking, truck rest areas, and rest areas reachable &lt;em&gt;before&lt;/em&gt; the legal deadline, ordered closest-to-the-deadline first so the driver squeezes the most legal driving out of the day. That list is drawn from Road511’s own live 511 parking data, the same normalized feeds behind the rest of the API.&lt;/p&gt;

&lt;p&gt;And the flag that no consumer navigation app surfaces: &lt;code&gt;feasible&lt;/code&gt;. When it’s &lt;code&gt;true&lt;/code&gt;, at least one legal stop is reachable in time. When it’s &lt;code&gt;false&lt;/code&gt; — as on the second stop above — &lt;strong&gt;no legal parking is reachable before the deadline&lt;/strong&gt;. That is a real compliance risk you want to see at dispatch time, not at the roadside. (&lt;code&gt;feasible&lt;/code&gt; is omitted entirely if the parking lookup didn’t run, so a missing flag never reads as a misleading “false.”)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Regimes
&lt;/h2&gt;

&lt;p&gt;Four Hours-of-Service regimes ship today, each implemented against its actual regulation:&lt;/p&gt;

&lt;p&gt;Ruleset&lt;/p&gt;

&lt;p&gt;Regulation&lt;/p&gt;

&lt;p&gt;Key limits&lt;/p&gt;

&lt;p&gt;&lt;code&gt;us&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;49 CFR 395 (FMCSA, property-carrying)&lt;/p&gt;

&lt;p&gt;11h driving, 14h duty window, 30-min break after 8h driving, 10h daily rest&lt;/p&gt;

&lt;p&gt;&lt;code&gt;canada_south&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;SOR/2005-313 (south of 60°N)&lt;/p&gt;

&lt;p&gt;13h driving, bounded by both a 14h duty window and a 16h elapsed window, 10h daily rest&lt;/p&gt;

&lt;p&gt;&lt;code&gt;canada_north&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;SOR/2005-313 (north of 60°N)&lt;/p&gt;

&lt;p&gt;15h driving, 18h duty window, 8h daily rest&lt;/p&gt;

&lt;p&gt;&lt;code&gt;eu&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Regulation (EC) No 561/2006&lt;/p&gt;

&lt;p&gt;9h driving, 45-min break after 4.5h driving, 11h daily rest&lt;/p&gt;

&lt;p&gt;The weekly/cycle caps (US 60/70-hour, Canada 70/120-hour, EU 56h/90h) are modeled when you supply &lt;code&gt;cycle_remaining_s&lt;/code&gt;; left unset, the projection focuses on the daily limits. The European &lt;code&gt;aetr&lt;/code&gt; regime is planned. If you don’t set a ruleset, the routing handler defaults it from the deployment region — but Canadian operators should set &lt;code&gt;canada_south&lt;/code&gt; or &lt;code&gt;canada_north&lt;/code&gt; explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Fits
&lt;/h2&gt;

&lt;p&gt;The HOS projection is computed on the route geometry that’s already in hand — it’s pure projection over the polyline, not a second round of routing. It runs for the primary route only, and it rides the routing call you’re already making: &lt;strong&gt;no separate endpoint, no separate charge&lt;/strong&gt; beyond the routing request itself. Routing is a paid-plan feature (featured on Pro+) with a free 14-day trial, so you can wire HOS into a dispatch flow and try it end-to-end before committing.&lt;/p&gt;

&lt;p&gt;Pair it with the corridor &lt;code&gt;warnings[]&lt;/code&gt; (bridge clearances, weight restrictions, rail crossings, work zones) and the same request answers three questions at once: can this truck legally run this corridor, where will the driver have to stop, and is there parking when they do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="///docs.html#truck-routing"&gt;API docs&lt;/a&gt; — full request and response reference for &lt;code&gt;POST /api/v1/routing/route&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="///blog/truck-routing-hazard-warnings.html"&gt;Truck routing with live hazard warnings&lt;/a&gt; — the corridor enrichment this builds on&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Plan the route, the legal stop, and the parking — in one call
&lt;/h3&gt;

&lt;p&gt;Send the driver’s HOS clock with the route and get back every required break, every drive-limit stop, and the parking reachable before each deadline. Free 14-day trial. No credit card.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Get Free API Key&lt;/a&gt; &lt;a href="///docs.html#truck-routing"&gt;Read the Docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>trucking</category>
      <category>api</category>
      <category>go</category>
      <category>logistics</category>
    </item>
    <item>
      <title>Normalizing DATEX II Across 30 European National Access Points</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Fri, 05 Jun 2026 11:37:15 +0000</pubDate>
      <link>https://dev.to/road511/normalizing-datex-ii-across-30-european-national-access-points-2g58</link>
      <guid>https://dev.to/road511/normalizing-datex-ii-across-30-european-national-access-points-2g58</guid>
      <description>&lt;p&gt;EU Directive 2010/40/EU mandates that every Member State operates a &lt;strong&gt;National Access Point (NAP)&lt;/strong&gt; that publishes real-time traffic, safety, and multimodal travel data in &lt;strong&gt;DATEX II&lt;/strong&gt;. On paper, that means one standard, 27 publishers, one integration.&lt;/p&gt;

&lt;p&gt;In practice, every NAP serves a different DATEX II profile, over a different transport, behind a different authentication flow, on a different cadence. "DATEX II compliant" is a checkbox each Member State ticks differently.&lt;/p&gt;

&lt;p&gt;This is what we ran into building &lt;a href="https://napspan.com" rel="noopener noreferrer"&gt;NAPSPAN&lt;/a&gt;, a unified API across EU 27 + UK + EFTA. Here's the architecture that lets us treat 30 incompatible DATEX II feeds as if they were one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spec, Then Reality
&lt;/h2&gt;

&lt;p&gt;DATEX II is a mature European standard maintained by &lt;a href="https://datex2.eu/" rel="noopener noreferrer"&gt;datex2.eu&lt;/a&gt; and coordinated by &lt;a href="https://napcore.eu/" rel="noopener noreferrer"&gt;NAPCORE&lt;/a&gt;. Each delegated regulation references it as the mandatory exchange format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;(EU) 2022/670&lt;/strong&gt; — RTTI: real-time events, speeds, road conditions&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(EU) No 886/2013&lt;/strong&gt; — SRTI: free, open access to safety alerts&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(EU) No 885/2013&lt;/strong&gt; — SSTP: safe and secure truck parking&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(EU) 2017/1926&lt;/strong&gt; — MMTIS: multimodal travel information&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;AFIR Article 20&lt;/strong&gt; — live EV charging-station data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the spec is clear. The implementation is anything but.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the NAPs Actually Diverge
&lt;/h2&gt;

&lt;p&gt;"DATEX II compliant" hides at least five axes of divergence:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Profile
&lt;/h3&gt;

&lt;p&gt;Each Member State publishes a national &lt;em&gt;profile&lt;/em&gt; — a constrained subset of the DATEX II model. Germany's profile (&lt;em&gt;German Traffic Data Profile&lt;/em&gt;) defines incidents, road works, and traffic flow on Autobahn + Bundesstraßen. France's &lt;a href="https://transport.data.gouv.fr/" rel="noopener noreferrer"&gt;transport.data.gouv.fr&lt;/a&gt; uses different sub-elements. The Netherlands' NDW publishes a tighter event taxonomy. The UK's &lt;a href="https://nap.tdt.gov.uk/" rel="noopener noreferrer"&gt;NAP TDT&lt;/a&gt; wraps DATEX into TIH-specific containers. The &lt;a href="https://repo.datex2.eu/implementations/profile_directory/" rel="noopener noreferrer"&gt;DATEX II profiles directory&lt;/a&gt; lists them all — same root schema, every country a different shape.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Version
&lt;/h3&gt;

&lt;p&gt;DATEX II v2.3 and v3.x are both in the wild. Some NAPs publish both. Some are mid-migration. The XML namespaces are different. Some elements were renamed between versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Transport
&lt;/h3&gt;

&lt;p&gt;DATEX II is a payload format, not a transport. NAPs ship it over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;HTTPS pull&lt;/strong&gt; — you GET an XML document on a schedule&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;HTTPS push (subscription)&lt;/strong&gt; — you register an endpoint, the NAP POSTs to you&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SOAP-over-HTTPS&lt;/strong&gt; — envelope-wrapped pull&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;OCIT-C&lt;/strong&gt; — the German telematics interface used by Mobilithek&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;WFS / GeoJSON&lt;/strong&gt; — some city-level supplements (Hamburg, Düsseldorf)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Authentication
&lt;/h3&gt;

&lt;p&gt;The matrix here is wide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;None&lt;/strong&gt; — SRTI safety feeds are mandated free and open&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Registered consumer (no key)&lt;/strong&gt; — NAP whitelists your origin or service endpoint&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Bearer / API key&lt;/strong&gt; — standard token in a header&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;TLS client certificate (mTLS)&lt;/strong&gt; — Germany's Mobilithek&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;OAuth2&lt;/strong&gt; — some operator portals&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Cadence and freshness
&lt;/h3&gt;

&lt;p&gt;Some publications update every 30 seconds (motorway flow). Others refresh nightly (planned road-work calendars). Some sit behind a CDN that lies about freshness via stale &lt;code&gt;Last-Modified&lt;/code&gt; headers. Adaptive backoff matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Format Zoo, in DATEX II Form
&lt;/h2&gt;

&lt;p&gt;Here's roughly the same incident from three different NAPs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobilithek (DE)&lt;/strong&gt; — OCIT-C envelope wrapping a v2.3 &lt;code&gt;SituationPublication&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;d2:payloadPublication
    xsi:type="d2:SituationPublication"
    xmlns:d2="http://datex2.eu/schema/2/2_0"&amp;gt;
  &amp;lt;d2:situation id="DE_BAB7_4711"&amp;gt;
    &amp;lt;d2:situationRecord
        xsi:type="d2:Accident"
        id="DE_BAB7_4711_R1"&amp;gt;
      &amp;lt;d2:probabilityOfOccurrence&amp;gt;certain&amp;lt;/d2:probabilityOfOccurrence&amp;gt;
      &amp;lt;d2:groupOfLocations xsi:type="d2:Point"&amp;gt;
        &amp;lt;d2:pointByCoordinates&amp;gt;
          &amp;lt;d2:pointCoordinates&amp;gt;
            &amp;lt;d2:latitude&amp;gt;53.5511&amp;lt;/d2:latitude&amp;gt;
            &amp;lt;d2:longitude&amp;gt;9.9937&amp;lt;/d2:longitude&amp;gt;
          &amp;lt;/d2:pointCoordinates&amp;gt;
        &amp;lt;/d2:pointByCoordinates&amp;gt;
      &amp;lt;/d2:groupOfLocations&amp;gt;
    &amp;lt;/d2:situationRecord&amp;gt;
  &amp;lt;/d2:situation&amp;gt;
&amp;lt;/d2:payloadPublication&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NDW (NL)&lt;/strong&gt; — v3.x with a tighter &lt;code&gt;VmsTablePublication&lt;/code&gt;-style envelope, JSON-LD permitted:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "@context": "https://datex2.eu/schema/3/jsonld",
  "publicationCreator": { "country": "nl", "nationalIdentifier": "NDW" },
  "situation": [{
    "id": "NDW_2026_05_01_42",
    "situationRecord": [{
      "@type": "Accident",
      "severity": "high",
      "groupOfLocations": {
        "locationContainedInGroup": [{
          "pointByCoordinates": { "latitude": 52.0907, "longitude": 5.1214 }
        }]
      }
    }]
  }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;UK NAP TDT&lt;/strong&gt; — DATEX wrapped in a TIH container with a per-publisher feed envelope:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;tih:feed xmlns:tih="https://nap.tdt.gov.uk/tih"&amp;gt;
  &amp;lt;tih:publisher&amp;gt;National Highways&amp;lt;/tih:publisher&amp;gt;
  &amp;lt;d2:payloadPublication xsi:type="d2:SituationPublication"&amp;gt;
    &amp;lt;d2:situation id="NH-INC-9001"&amp;gt;...&amp;lt;/d2:situation&amp;gt;
  &amp;lt;/d2:payloadPublication&amp;gt;
&amp;lt;/tih:feed&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Same regulation. Same standard. Three different parsers required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;The solution is the same one we use on the North American side: an adapter registry with one function signature.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type FetchFunc func(ctx context.Context, sr SourceResource) (*FetchResult, error)

type FetchResult struct {
    Events        []TrafficEvent
    Features      []Feature
    ResponseBytes int
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Every NAP adapter — whether it's pulling Mobilithek's OCIT-C broker, polling NDW's v3 JSON-LD, or unwrapping a TIH envelope — implements this one interface. It takes a source-resource config (URL, credentials, country code, profile version) and returns normalized events and features.&lt;/p&gt;

&lt;p&gt;Registration happens at init time:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func init() {
    Register("mobilithek", "events",         fetchMobilithekSituations)
    Register("mobilithek", "afir_charging",  fetchMobilithekAFIR)
    Register("mobilithek", "parking",        fetchMobilithekParking)
    Register("ndw",        "events",         fetchNDWSituations)
    Register("ndw",        "vms",            fetchNDWVMS)
    Register("uk_tdt",     "events",         fetchUKTDTSituations)
    Register("transport_data_gouv_fr", "events", fetchFRSituations)
    // ... 30 NAPs, ~120 (adapter, resource) registrations
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The scheduler doesn't know or care which DATEX II profile, version, or transport each adapter handles. It just calls &lt;code&gt;Fetch()&lt;/code&gt; and gets back normalized events and features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Normalized Model
&lt;/h2&gt;

&lt;p&gt;Everything converges into two tables:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;traffic_events&lt;/code&gt;&lt;/strong&gt; — time-bounded incidents with lifecycle tracking:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type TrafficEvent struct {
    ID                 string
    Source             string      // "mobilithek", "ndw", "uk_tdt"
    Jurisdiction       string      // "DE", "NL", "GB"
    Type               EventType   // incident, construction, closure, weather
    Severity           Severity    // minor, moderate, major, critical
    Status             EventStatus // active, archived
    Title              string
    Description        string
    AffectedRoads      []string
    Direction          string
    LanesAffected      string
    Latitude, Longitude float64
    StartTime          time.Time
    EndTime            *time.Time
    EstimatedEndTime   *time.Time
    RoadClass          string      // motorway, trunk, primary, secondary
    Metadata           json.RawMessage // DATEX-II-specific fields preserved
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;features&lt;/code&gt;&lt;/strong&gt; — a generic table for everything else. Cameras, VMS signs, weather stations, parking sites, EV charging stations, truck-route segments, bridge clearances — all use the same table with a &lt;code&gt;feature_type&lt;/code&gt; discriminator and type-specific fields in a JSONB &lt;code&gt;properties&lt;/code&gt; column. Adding a new resource type requires zero schema migrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hard Parts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Profile Field Mapping
&lt;/h3&gt;

&lt;p&gt;The German profile uses &lt;code&gt;locationDescriptor&lt;/code&gt; with &lt;code&gt;roadName&lt;/code&gt;. The Dutch v3 profile uses a top-level &lt;code&gt;roadInformation&lt;/code&gt; structure. The UK feeds bury the road name three levels deep inside &lt;code&gt;tih:routeContext&lt;/code&gt;. Each adapter has a small mapping function that knows where its profile keeps each canonical field. Where a profile has nothing to map to, the field is left empty — we don't synthesize data we don't have.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Lifecycle Tracking
&lt;/h3&gt;

&lt;p&gt;A DATEX II &lt;code&gt;SituationRecord&lt;/code&gt; has a &lt;code&gt;situationRecordVersion&lt;/code&gt; and a &lt;code&gt;situationRecordVersionTime&lt;/code&gt;. Each new pull may bring a new version of the same record. Some NAPs increment the version on every minor metadata change; others only on substantive updates.&lt;/p&gt;

&lt;p&gt;The solution: diff the current state against the previous fetch. Track every change in an &lt;code&gt;event_history&lt;/code&gt; table — severity changes, description updates, lane changes, archival. This enables analytics like clearance time percentiles and corridor reliability across borders.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Coordinate Reference Systems
&lt;/h3&gt;

&lt;p&gt;Most NAPs publish WGS84 (EPSG:4326). Some German state feeds publish ETRS89 / UTM zones. A few city feeds use the local cadastral system. PostGIS handles transformation, but each adapter has to know what it's receiving.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Rate Limiting at Scale
&lt;/h3&gt;

&lt;p&gt;30 NAPs, each with multiple resource types (events, VMS, parking, EV charging, weather), each polling on a 30 sec to 5 min cadence. The scheduler uses per-server semaphores with configurable concurrency limits and request gaps. Circuit breakers back off on repeated failures. Adaptive backoff increases poll intervals when a NAP is slow or returning errors — important when a single NAP can otherwise drag down a whole region's freshness.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Schema-Free Feature Types
&lt;/h3&gt;

&lt;p&gt;When we started on the North American side, we had separate tables for cameras, VMS, parking. Every new data type meant a migration. The pivot to a generic &lt;code&gt;features&lt;/code&gt; table with JSONB properties was the best architectural decision in the project. On the EU side, that decision pays off again the moment AFIR EV charging data comes online — zero schema changes, just a new &lt;code&gt;feature_type&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API
&lt;/h2&gt;

&lt;p&gt;After all that normalization work, the API is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get active incidents in Germany:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl "https://api.napspan.com/api/v1/events?jurisdiction=DEU&amp;amp;type=incident&amp;amp;status=active" \
  -H "X-API-Key: your_key"

{
  "data": [
    {
      "id": "de_mobilithek_4711",
      "jurisdiction": "DEU",
      "type": "incident",
      "severity": "major",
      "title": "Verkehrsunfall A7 Richtung Hamburg",
      "affected_roads": ["A7"],
      "direction": "Hamburg",
      "lanes_affected": "2 of 3 lanes closed",
      "latitude": 53.5511,
      "longitude": 9.9937,
      "start_time": "2026-05-01T08:15:00Z",
      "estimated_end_time": "2026-05-01T12:00:00Z"
    }
  ],
  "total": 142,
  "limit": 100,
  "offset": 0,
  "has_more": true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Get traffic cameras in the Netherlands as GeoJSON:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl "https://api.napspan.com/api/v1/features/geojson?type=cameras&amp;amp;jurisdiction=NLD" \
  -H "X-API-Key: your_key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Drop the response directly into Leaflet, Mapbox, or any GeoJSON-compatible tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query a cross-border box (Berlin to Amsterdam):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl "https://api.napspan.com/api/v1/events?status=active&amp;amp;\
bbox=4.90,52.37,13.40,52.52" \
  -H "X-API-Key: your_key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;One call, two NAPs (Mobilithek + NDW), one consistent response shape — a bounding box spans the border with no per-country fan-out. (For events tied to an actual driven path rather than a box, &lt;code&gt;POST /routing/route&lt;/code&gt; returns them as ranked &lt;code&gt;warnings[]&lt;/code&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the Data
&lt;/h2&gt;

&lt;p&gt;Once normalized, the dataset includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Real-time &lt;strong&gt;RTTI&lt;/strong&gt; events — incidents, road works, traffic flow&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SRTI&lt;/strong&gt; safety alerts (free, open under 886/2013)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;VMS&lt;/strong&gt; dynamic message-sign content with current displayed text&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSTP&lt;/strong&gt; safe and secure truck parking with availability&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;AFIR&lt;/strong&gt; Article 20 EV charging-point status&lt;/li&gt;
&lt;li&gt;  Traffic cameras with image URLs (where the NAP exposes them)&lt;/li&gt;
&lt;li&gt;  RWIS-style weather-station readings&lt;/li&gt;
&lt;li&gt;  TEN-T core network corridor metadata&lt;/li&gt;
&lt;li&gt;  Bridge clearances and weight restrictions per Member State&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Go&lt;/strong&gt; — chi router, pgx v5, slog, encoding/xml + custom DATEX schema parsers&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PostgreSQL + PostGIS&lt;/strong&gt; — spatial queries, GeoJSON generation, corridor intersection&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Redis&lt;/strong&gt; — response caching, feature detail caching, rate-limit counters, webhook idempotency (&lt;strong&gt;mandatory&lt;/strong&gt; — the API and worker refuse to start if Redis is unreachable)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Vue 3 + Leaflet&lt;/strong&gt; — &lt;a href="https://map.napspan.com" rel="noopener noreferrer"&gt;live traffic map&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;The API is live with a free tier (no credit card):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://map.napspan.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — explore the data visually&lt;/li&gt;
&lt;li&gt;  &lt;a href="///docs.html"&gt;API docs&lt;/a&gt; — full endpoint reference&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://portal.napspan.com" rel="noopener noreferrer"&gt;Developer portal&lt;/a&gt; — sign up and get an API key&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you're building anything on European mobility data — logistics, fleet routing, navigation, urban-mobility dashboards — we'd love to hear which NAPs and which DATEX II profiles you most need normalized first. The hardest part isn't the parsing; it's knowing which Member State publishes which data on which transport behind which auth flow. After 30 NAPs, we've built a pretty good map of that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready to try NAPSPAN?
&lt;/h3&gt;

&lt;p&gt;Free 14-day trial. No credit card. EU 27 + UK + EFTA, normalized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://portal.napspan.com/signup" rel="noopener noreferrer"&gt;Get Free API Key&lt;/a&gt; &lt;a href="https://map.napspan.com/" rel="noopener noreferrer"&gt;Explore the Map&lt;/a&gt;&lt;/p&gt;

</description>
      <category>datex2</category>
      <category>eu</category>
      <category>go</category>
      <category>api</category>
    </item>
    <item>
      <title>Truck Routing That Warns You Before the Bridge Does</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Sat, 30 May 2026 10:55:44 +0000</pubDate>
      <link>https://dev.to/road511/truck-routing-that-warns-you-before-the-bridge-does-2l5k</link>
      <guid>https://dev.to/road511/truck-routing-that-warns-you-before-the-bridge-does-2l5k</guid>
      <description>&lt;p&gt;A routing API will happily hand a truck the fastest line between two points. What it usually won't tell you is that the line runs under a 4.0 m overpass with a 4.2 m load on the trailer, crosses an at-grade rail line you're legally required to stop at with placarded hazmat, or threads a county road that's posted closed for construction this week.&lt;/p&gt;

&lt;p&gt;Flow APIs — HERE, TomTom, INRIX — are excellent at &lt;em&gt;speed&lt;/em&gt;: where traffic is moving, how long the trip takes. That's congestion data. It is not infrastructure data. The overpass height, the bridge weight rating, the truck-restricted segment, the rail crossing, the active work zone — those live in 57 different state and provincial 511 systems, and a flow API doesn't read them.&lt;/p&gt;

&lt;p&gt;That gap is what Road511's routing endpoint closes. We take the route geometry from HERE, then enrich the corridor from our own live traffic database — the same normalized 511 data behind the rest of the API — and return the hazards as a structured &lt;code&gt;warnings[]&lt;/code&gt; array scored against &lt;em&gt;your&lt;/em&gt; truck's dimensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;One call to &lt;code&gt;POST /api/v1/routing/route&lt;/code&gt;. You send an origin, a destination, and — required — a &lt;code&gt;truck&lt;/code&gt; profile. The profile is what makes a clearance "critical" instead of trivia: a 4.2 m height flags the 4.0 m overpass; a 36-tonne gross weight flags the posted 30-tonne bridge; a &lt;code&gt;hazardous&lt;/code&gt; flag turns public at-grade rail crossings into mandatory-stop warnings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.road511.com/api/v1/routing/route"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "origin":      { "lat": 47.6062, "lng": -122.3321 },
    "destination": { "lat": 47.6588, "lng": -117.4260 },
    "truck": {
      "profile":  "tractor",
      "weight_t": 36.0,
      "height_m": 4.2,
      "width_m":  2.6,
      "length_m": 21.0,
      "axle_count": 5,
      "hazardous": ["flammable"]
    },
    "alternatives": 1,
    "avoid": ["toll_road"],
    "enrichment": {
      "buffer_m": 100,
      "clearance_pad_m": 0.15,
      "min_severity": "warning",
      "include_features": ["truck_parking", "rest_areas"]
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's Seattle to Spokane for a 4.2 m, 36-tonne, placarded tractor-trailer. The &lt;code&gt;truck&lt;/code&gt; object accepts &lt;code&gt;profile&lt;/code&gt; (&lt;code&gt;tractor&lt;/code&gt;, &lt;code&gt;straight&lt;/code&gt;, or &lt;code&gt;box&lt;/code&gt;), &lt;code&gt;weight_t&lt;/code&gt;, &lt;code&gt;height_m&lt;/code&gt;, and optional &lt;code&gt;width_m&lt;/code&gt;, &lt;code&gt;length_m&lt;/code&gt;, &lt;code&gt;axle_count&lt;/code&gt;, and &lt;code&gt;hazardous&lt;/code&gt;. You can add &lt;code&gt;waypoints&lt;/code&gt;, request up to 3 &lt;code&gt;alternatives&lt;/code&gt;, and &lt;code&gt;avoid&lt;/code&gt; any of &lt;code&gt;tolls&lt;/code&gt;, &lt;code&gt;ferries&lt;/code&gt;, or &lt;code&gt;tunnels&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Response
&lt;/h2&gt;

&lt;p&gt;You get back a &lt;code&gt;routes[]&lt;/code&gt; array. Each route carries a &lt;code&gt;summary&lt;/code&gt;, a GeoJSON &lt;code&gt;geometry&lt;/code&gt; (a &lt;code&gt;LineString&lt;/code&gt; you can drop straight onto a map), and the part that matters — a &lt;code&gt;warnings[]&lt;/code&gt; array. Every warning is positioned: a &lt;code&gt;type&lt;/code&gt;, a &lt;code&gt;severity&lt;/code&gt; (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, or &lt;code&gt;critical&lt;/code&gt;), a &lt;code&gt;distance_along_route_m&lt;/code&gt;, a &lt;code&gt;projected_arrival_time&lt;/code&gt;, a &lt;code&gt;source&lt;/code&gt;, its own &lt;code&gt;geometry&lt;/code&gt;, and type-specific &lt;code&gt;properties&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"distance_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;449300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duration_s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16980&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"has_tolls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LineString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-122.3321&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.6062&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-120.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-117.4260&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.6588&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"warnings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bridge_clearance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bridge clearance below truck height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I-90 over Snoqualmie Pass on-ramp clearance 4.11m; your truck is 4.20m."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;38200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"projected_arrival_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-29T15:12:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nbi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WA-000000000123456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-121.97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.51&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"clearance_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"truck_height_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"facility_carried"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I-90"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"feature_crossed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Snoqualmie Pass on-ramp"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rail_crossing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mandatory hazmat stop — 49 CFR 392.10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Public at-grade crossing; hazmat carrier must stop."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;201400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"projected_arrival_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-29T17:41:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fra"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"094821X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-119.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"at_grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"railroad"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BNSF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"crossing_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"At Grade"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"traffic_event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Construction on US-2 EB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US-2 EB right lane closed for paving"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;372600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"projected_arrival_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-29T19:08:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wsdot-4471822"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-118.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.45&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"sub_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"construction"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"truck_parking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ritzville Rest Area"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;250100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"capacity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-118.38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.13&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rt_8f3c2a1b"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The route geometry is HERE's. Everything in &lt;code&gt;warnings[]&lt;/code&gt; and &lt;code&gt;features[]&lt;/code&gt; is Road511's enrichment of the corridor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Warning Types
&lt;/h2&gt;

&lt;p&gt;A warning is only emitted when the corridor actually contains the hazard — and for the dimension-checked types, only when your profile would violate it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What it flags&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bridge_clearance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overpass clearance vs your &lt;code&gt;height_m&lt;/code&gt; + &lt;code&gt;clearance_pad_m&lt;/code&gt;. &lt;strong&gt;Critical&lt;/strong&gt; — the one that ends loads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;truck_restriction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A segment your profile is barred from. &lt;strong&gt;Critical&lt;/strong&gt;, per violated dimension.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;weight_restriction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Posted or seasonal weight limit your gross weight exceeds. &lt;strong&gt;Critical&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rail_crossing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Public at-grade crossing. &lt;strong&gt;Critical&lt;/strong&gt; for placarded hazmat (49 CFR 392.10 mandatory stop); &lt;code&gt;info&lt;/code&gt; for high-volume otherwise.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;traffic_event&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Live incident, closure, or construction on the corridor.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;future_construction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Planned work zones the route passes through.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;special_event&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scheduled events affecting nearby roads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;weigh_station&lt;/code&gt; / &lt;code&gt;inspection_station&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Scale houses and inspection sites along the route.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alert&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Jurisdiction-issued advisories on the corridor.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;weather&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Conditions reported near the route.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each warning's &lt;code&gt;properties&lt;/code&gt; are type-specific: a &lt;code&gt;bridge_clearance&lt;/code&gt; reports the measured &lt;code&gt;clearance_m&lt;/code&gt; and your &lt;code&gt;truck_height_m&lt;/code&gt;. The data is only as fresh as the underlying feed — clearances come from inventories that refresh on the order of days, while &lt;code&gt;traffic_event&lt;/code&gt; warnings ride the live incident feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tuning the Enrichment
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;enrichment&lt;/code&gt; object controls how aggressively the corridor is scanned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;buffer_m&lt;/code&gt; — how far off the line to look, 10–1000 m (default 100).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clearance_pad_m&lt;/code&gt; — safety margin added to your height before a clearance flags, 0–1 m (default 0.15).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;min_severity&lt;/code&gt; — drop everything below &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, or &lt;code&gt;critical&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exclude_types&lt;/code&gt; — suppress warning types you don't care about.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skip_temporal_filter&lt;/code&gt; — include time-bounded items outside their active window.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_distance_m&lt;/code&gt; — cap how far along the route enrichment runs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include_features&lt;/code&gt; — opt into a separate &lt;code&gt;features[]&lt;/code&gt; channel of &lt;code&gt;truck_parking&lt;/code&gt;, &lt;code&gt;truck_rest_areas&lt;/code&gt;, and &lt;code&gt;rest_areas&lt;/code&gt; near the route.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Saved Routes, Re-Evaluation, and Monitoring
&lt;/h2&gt;

&lt;p&gt;Every routing call is auto-saved for &lt;strong&gt;30 minutes&lt;/strong&gt; under the &lt;code&gt;route_id&lt;/code&gt;. Refetch with &lt;code&gt;GET /api/v1/routing/route/saved/{id}&lt;/code&gt; inside that window and the warnings are &lt;em&gt;re-evaluated against current conditions&lt;/em&gt; — a closure that popped up since you planned shows up on the refetch. Refetches do &lt;strong&gt;not&lt;/strong&gt; consume routing quota.&lt;/p&gt;

&lt;p&gt;To keep a route past the 30-minute TTL, call &lt;code&gt;POST /api/v1/routing/route/saved/{id}/persist&lt;/code&gt;. Persisted routes count against your plan's saved-route slots, and they can be put under &lt;strong&gt;webhook monitoring&lt;/strong&gt; — so when a new critical warning lands on a route you've already dispatched, you get notified instead of polling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quota and Access
&lt;/h2&gt;

&lt;p&gt;Routing is a paid-plan feature, featured most heavily on &lt;strong&gt;Pro+&lt;/strong&gt;. The Free 14-day trial includes a small daily cap so you can try it. Quota is a monthly routing-call bucket; FIFO top-up packs add calls on top. Refetching a saved route is free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Different
&lt;/h2&gt;

&lt;p&gt;HERE and TomTom sell you &lt;em&gt;flow&lt;/em&gt; — how fast the road is moving. Road511 adds the &lt;em&gt;infrastructure&lt;/em&gt; — what your specific truck physically and legally can't do on that road. The routing endpoint puts both in one response: a drivable HERE geometry, plus a corridor scan against the live 511 data that flow APIs were never built to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://road511.com/docs.html#truck-routing" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>trucking</category>
      <category>go</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fuel Price API for Fleet Cost Planning</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Thu, 21 May 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/road511/fuel-price-api-for-fleet-cost-planning-1ag9</link>
      <guid>https://dev.to/road511/fuel-price-api-for-fleet-cost-planning-1ag9</guid>
      <description>&lt;p&gt;Fleet fuel costs are the second-largest operating expense after labor. Having state-level fuel price data in the same API as your truck routes, bridge clearances, and corridor restrictions means you can estimate total route costs in one integration.&lt;/p&gt;

&lt;p&gt;Road511 now serves weekly US state-level and monthly Canadian province-level fuel prices from government sources.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;US — EIA (Energy Information Administration)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Weekly retail prices for all 50 states&lt;/li&gt;
&lt;li&gt;Regular, midgrade, premium gasoline + diesel&lt;/li&gt;
&lt;li&gt;USD per gallon&lt;/li&gt;
&lt;li&gt;Updated every Monday&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Canada — Statistics Canada&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monthly retail prices for 12 provinces/territories&lt;/li&gt;
&lt;li&gt;Regular, premium gasoline + diesel&lt;/li&gt;
&lt;li&gt;CAD cents per litre&lt;/li&gt;
&lt;li&gt;Updated monthly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are official government data, free, and unrestricted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Latest Prices
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/fuel-prices?country=US&amp;amp;fuel_type=diesel&amp;amp;latest=true"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fuel_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"diesel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.892&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gallon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"period_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-07"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"frequency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weekly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eia"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fuel_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"diesel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.241&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gallon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"period_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-07"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"frequency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weekly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eia"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"has_more"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Historical Trends
&lt;/h2&gt;

&lt;p&gt;Track how prices change over time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/fuel-prices?jurisdiction=CA&amp;amp;fuel_type=diesel&amp;amp;from=2025-01-01&amp;amp;to=2026-04-10"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns every weekly data point for California diesel over the past 15 months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Border Queries
&lt;/h2&gt;

&lt;p&gt;Get both US and Canadian prices in one response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/fuel-prices?fuel_type=diesel&amp;amp;latest=true&amp;amp;limit=100"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: US prices are in USD/gallon, Canadian in CAD cents/litre. Convert as needed for your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combine with Corridor Data
&lt;/h2&gt;

&lt;p&gt;The real value is combining fuel prices with Road511's other data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plan the route&lt;/strong&gt; — check bridge clearances and weight restrictions via &lt;code&gt;/truck/corridor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estimate fuel cost&lt;/strong&gt; — get diesel prices for each state the route crosses via &lt;code&gt;/fuel-prices&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check conditions&lt;/strong&gt; — verify road conditions and active incidents via &lt;code&gt;/events&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All from one API, one key, one integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fleet fuel budgeting&lt;/strong&gt; — forecast costs based on state-level price trends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route cost optimization&lt;/strong&gt; — factor fuel price differences into route selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate calculations&lt;/strong&gt; — logistics platforms adjusting freight rates by fuel cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics&lt;/strong&gt; — correlate fuel prices with traffic patterns, delivery frequency&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.road511.com" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>trucking</category>
      <category>go</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Comparing Traffic Data APIs: Road511 vs HERE vs TomTom vs INRIX</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Tue, 19 May 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/road511/comparing-traffic-data-apis-road511-vs-here-vs-tomtom-vs-inrix-55cg</link>
      <guid>https://dev.to/road511/comparing-traffic-data-apis-road511-vs-here-vs-tomtom-vs-inrix-55cg</guid>
      <description>&lt;p&gt;If you’re building an app that needs traffic data, you have a few options. The big names — HERE, TomTom, INRIX — dominate the market. But they all sell the same thing: traffic flow from probe data (GPS traces from phones and connected vehicles).&lt;/p&gt;

&lt;p&gt;Road511 is different. It aggregates &lt;strong&gt;infrastructure&lt;/strong&gt; data from government 511 systems that the big providers don’t touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Each Provider Actually Offers
&lt;/h2&gt;

&lt;p&gt;Two notes before the table, because a comparison table is only as honest as its column definitions. The Road511 figures are &lt;strong&gt;active&lt;/strong&gt; row counts measured against our North American production database on &lt;strong&gt;14 August 2026&lt;/strong&gt; — not lifetime totals, which run considerably higher because retired hardware and expired records stay in the archive. And the other three columns mean one specific thing: &lt;em&gt;“sold as a queryable, source-attributed data feed you can pull.”&lt;/em&gt; Several of the “map attribute” entries below &lt;em&gt;are&lt;/em&gt; in HERE’s and TomTom’s map data — they are what makes their truck routing avoid a low bridge — but they are baked into a routing engine rather than exposed as an inventory you can query, list, and attribute to the agency that published it. That distinction is the whole point of this page, and it cuts both ways.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data Type&lt;/th&gt;
&lt;th&gt;Road511&lt;/th&gt;
&lt;th&gt;HERE&lt;/th&gt;
&lt;th&gt;TomTom&lt;/th&gt;
&lt;th&gt;INRIX&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Traffic flow (speed/congestion)&lt;/td&gt;
&lt;td&gt;Limited — travel times &amp;amp; segment speeds, select states&lt;/td&gt;
&lt;td&gt;Yes (core product)&lt;/td&gt;
&lt;td&gt;Yes (core product)&lt;/td&gt;
&lt;td&gt;Yes (core product)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic events/incidents&lt;/td&gt;
&lt;td&gt;61 jurisdictions reporting&lt;/td&gt;
&lt;td&gt;Yes (own sources)&lt;/td&gt;
&lt;td&gt;Yes (own sources)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live cameras (50,493)&lt;/td&gt;
&lt;td&gt;Yes, 59 jurisdictions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DMS sign messages (12,668)&lt;/td&gt;
&lt;td&gt;Yes, 45 jurisdictions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bridge clearances (662,529)&lt;/td&gt;
&lt;td&gt;Yes, 53 jurisdictions&lt;/td&gt;
&lt;td&gt;Map attribute for routing&lt;/td&gt;
&lt;td&gt;Map attribute for routing&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Truck routes (489,091 STAA segments)&lt;/td&gt;
&lt;td&gt;Yes, 52 jurisdictions&lt;/td&gt;
&lt;td&gt;Map attribute for routing&lt;/td&gt;
&lt;td&gt;Map attribute for routing&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weight restrictions (22,273)&lt;/td&gt;
&lt;td&gt;Yes, 6 jurisdictions&lt;/td&gt;
&lt;td&gt;Map attribute for routing&lt;/td&gt;
&lt;td&gt;Map attribute for routing&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring load limits&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Road conditions (8,119 segments)&lt;/td&gt;
&lt;td&gt;Yes, 20 jurisdictions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RWIS weather stations (3,877)&lt;/td&gt;
&lt;td&gt;Yes, 36 jurisdictions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rest areas &amp;amp; truck parking (4,220)&lt;/td&gt;
&lt;td&gt;Yes, 52 jurisdictions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EV charging (116,149 stations)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (own product)&lt;/td&gt;
&lt;td&gt;Yes (own product)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fuel prices (state-level)&lt;/td&gt;
&lt;td&gt;Yes (EIA + StatCan)&lt;/td&gt;
&lt;td&gt;Yes (own product)&lt;/td&gt;
&lt;td&gt;Varies by product&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event lifecycle history&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Varies by product&lt;/td&gt;
&lt;td&gt;Varies by product&lt;/td&gt;
&lt;td&gt;Yes (historical products)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GeoJSON native&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Varies by API&lt;/td&gt;
&lt;td&gt;Varies by API&lt;/td&gt;
&lt;td&gt;Varies by API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhook notifications&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Via HERE Platform&lt;/td&gt;
&lt;td&gt;Varies by product&lt;/td&gt;
&lt;td&gt;Varies by product&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Where a cell reads &lt;em&gt;“varies by product”&lt;/em&gt; we mean it literally: these are large vendors with wide catalogues that change, and we are not going to assert a flat “no” about a product line we do not buy. If a specific capability decides your build, check it on their own documentation rather than on ours — and hold us to the same standard, which is why every Road511 number above is a count you can reproduce against the live API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Entry Price&lt;/th&gt;
&lt;th&gt;What You Get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Road511&lt;/td&gt;
&lt;td&gt;$0 (14-day trial) / from $29/mo&lt;/td&gt;
&lt;td&gt;Full API access, high daily request volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HERE&lt;/td&gt;
&lt;td&gt;~$200/mo&lt;/td&gt;
&lt;td&gt;Traffic flow + incidents, limited calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TomTom&lt;/td&gt;
&lt;td&gt;~$150/mo&lt;/td&gt;
&lt;td&gt;Traffic flow + incidents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INRIX&lt;/td&gt;
&lt;td&gt;~$50K/year&lt;/td&gt;
&lt;td&gt;Enterprise only, negotiated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Road511 sits well below HERE/TomTom for comparable request volumes — and covers data categories they don’t sell as feeds. Treat the other three rows as &lt;strong&gt;order-of-magnitude only&lt;/strong&gt;: they are approximate entry list points, they were approximate when this post was written, and enterprise pricing at all three is negotiated rather than published. Do not plan a budget from this table — price your actual call volume with each vendor. Our live plan tiers and quotas are on the &lt;a href="https://road511.com/#pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;, where they are the real numbers rather than an estimate.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Which
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use HERE / TomTom if you need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time traffic flow (speed, congestion, travel times)&lt;/li&gt;
&lt;li&gt;Turn-by-turn routing with live traffic&lt;/li&gt;
&lt;li&gt;Global coverage (Europe, Asia, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Road511 if you need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Camera feeds, DMS messages, road conditions&lt;/li&gt;
&lt;li&gt;Bridge clearances, truck routes, weight restrictions&lt;/li&gt;
&lt;li&gt;Cross-state normalized 511 data&lt;/li&gt;
&lt;li&gt;Event lifecycle analytics (clearance times, trends)&lt;/li&gt;
&lt;li&gt;EV charging station data&lt;/li&gt;
&lt;li&gt;Fuel prices by state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use both if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re building a fleet management platform — HERE/TomTom for routing, Road511 for truck restrictions, bridge clearances, fuel prices, and camera verification.&lt;/li&gt;
&lt;li&gt;You’re building a comprehensive navigation app — traffic flow from one, infrastructure data from the other.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Real Differentiator
&lt;/h2&gt;

&lt;p&gt;HERE, TomTom, and INRIX compete with each other on the same data: traffic flow from probe traces. They spend hundreds of millions collecting it, and they are good at it. Nothing on this page is an argument that they aren’t.&lt;/p&gt;

&lt;p&gt;What Road511 aggregates is a different class of data from a different origin — government 511 systems rather than phone probes. As of 14 August 2026 that is &lt;strong&gt;50,493 live cameras, 662,529 bridge clearances, 489,091 STAA truck-route segments, 12,668 dynamic message signs, 8,119 road-condition segments and 3,877 roadside weather stations&lt;/strong&gt;, normalized across &lt;strong&gt;64 US states and Canadian provinces and territories&lt;/strong&gt;, plus federal datasets from FHWA, the National Bridge Inventory and the FRA. Those are active-record counts, and you can reproduce every one of them against the live API without a key: &lt;a href="https://api.road511.com/api/v1/stats" rel="noopener noreferrer"&gt;&lt;code&gt;/stats&lt;/code&gt;&lt;/a&gt; returns the per-type totals and &lt;a href="https://api.road511.com/api/v1/jurisdictions" rel="noopener noreferrer"&gt;&lt;code&gt;/jurisdictions&lt;/code&gt;&lt;/a&gt; the coverage list. Two things to know before you check, so that the numbers agreeing or disagreeing tells you something. The counts above are a dated reading and they move — cameras stood at 54,814 on 20 August, six days after this snapshot — so expect drift, not equality. And &lt;code&gt;/jurisdictions&lt;/code&gt; returns &lt;strong&gt;101&lt;/strong&gt; rows, not 64: the extra 37 are source rows rather than places you can filter by (30 WZDx feeds, plus CBP, CBSA, StatCan, NWS, Environment and Climate Change Canada, NRL and a continental OSM row). The 64 are the two-letter US and Canadian codes.&lt;/p&gt;

&lt;p&gt;The data is complementary, not competitive. If your product already uses HERE or TomTom for flow and routing, Road511 slots in beside it for everything those feeds leave out. We do not resell anyone’s probe data, and a flow provider is not something we replace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — events, features, bridge clearances, truck data, and the GeoJSON variants in one reference.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com/" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see the normalized 511 data across all 64 jurisdictions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — 14-day trial, no credit card.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://road511.com/blog/comparing-traffic-apis-here-tomtom-inrix.html?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=comparing-traffic-apis-here-tomtom-inrix&amp;amp;utm_content=dev-08" rel="noopener noreferrer"&gt;road511.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>go</category>
    </item>
  </channel>
</rss>
