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.
The wait is published. Both governments publish it, in fact — and that is exactly where it gets interesting, because they publish different halves of it. Here is the Blue Water Bridge between Port Huron, Michigan and Point Edward, Ontario, as our North American production database held it shortly after 07:00 UTC on 8 August 2026 — 3 a.m. in Michigan, the quietest hour of the day:
| Direction | Publisher | Commercial wait | Reported at |
|---|---|---|---|
| Into the US (northbound plaza, Port Huron) | U.S. Customs and Border Protection | 0 |
“At Midnight EST” |
| Into Canada (Point Edward) | Canada Border Services Agency | 25 minutes |
2026-08-08 02:22 EDT |
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 -- at all 29 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.
Both Halves, One Field, On the Route
Road511 polls both feeds every five minutes and normalizes them into the same border_crossings feature type. On a truck-aware route they arrive as warnings[] entries of type: "border_crossing" — the same array that already carries bridge clearances, work zones and weigh stations, from the same single call to POST /api/v1/routing/route.
Here are the two sides of that bridge exactly as the routing enrichment renders them, from the property blobs quoted above (abridged — distance_along_route_m and projected_arrival_time depend on your own route):
{
"type": "border_crossing",
"severity": "warning",
"title": "Blue Water Bridge",
"description": "Commercial border wait ~25 min.",
"status": "open",
"as_of": "2026-08-08T07:03:04Z",
"source": "CBSA",
"properties": {
"commercial_wait_minutes": 25,
"commercial_canada_bound": "25 minutes",
"commercial_us_bound": "--",
"location": "Point Edward, ON/Port Huron, MI",
"last_updated": "2026-08-08 02:22 EDT"
}
}
{
"type": "border_crossing",
"severity": "info",
"title": "Port Huron - Bluewater Bridge",
"description": "Commercial border wait ~0 min.",
"status": "open",
"as_of": "2026-08-08T07:10:43Z",
"source": "CBP",
"properties": {
"commercial_wait_minutes": 0,
"wait_trend": "steady",
"hours": "24 hrs/day",
"port_status": "Open",
"commercial": {
"delay_minutes": 0, "status": "no delay", "trend": "steady",
"lanes_open": 2, "max_lanes": 9,
"fast_status": "Lanes Closed", "update_time": "At Midnight EST"
},
"passenger": { "delay_minutes": 0, "lanes_open": 1, "max_lanes": 10 }
}
}
One field means the same thing on both: commercial_wait_minutes, 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.
Note that there are two clocks here, and they are different clocks. as_of is our 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 properties: CBSA’s last_updated (“2026-08-08 02:22 EDT” above) and CBP’s update_time (“At Midnight EST”), each in the agency’s own wording and timezone. A fresh as_of 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 reading is rather than how old the fetch is, read the agency field.
Severity is keyed on the commercial wait, because this is a truck API and the passenger queue is not your problem: info below 15 minutes, warning from 15, critical past 45 minutes or whenever the port is reported closed. That makes the whole thing filterable with the knob you already have — "min_severity": "warning" returns only the crossings on your route that are actually going to cost you time.
The direction caveat, stated up front
We do not yet resolve which side of the crossing your truck is on. When both directions are known, the warning carries the larger of the two — 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 commercial_wait_minutes 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.
Two Vocabularies for One Number
The two agencies do not merely publish different directions — they publish different kinds of thing. 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 No Delay (21 offices), four actual figures (3 minutes, 5 minutes, 12 minutes, 25 minutes), Not Applicable (3), and Temporarily closed (1).
Normalizing that is the boring part of the job and the part that has to be right:
-
No Delay→0. A real, published zero. -
25 minutes→25. -
--,N/A,Not Applicable, empty → no number at all. Not zero.
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 0 is how a dispatch board ends up confidently green in front of a two-hour queue. When nothing parseable is there, the warning stays info and says "Border crossing on route." — a crossing you should know about, with no wait claimed:
{
"type": "border_crossing",
"severity": "info",
"title": "Sumas",
"description": "Border crossing on route.",
"status": "open",
"as_of": "2026-08-08T07:05:42Z",
"source": "CBP"
}
“No Delay” With Two Lanes Open
Look again at the Port Huron block: "lanes_open": 2, "max_lanes": 9. 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: 2 of 13 commercial lanes open, no delay. Buffalo’s Peace Bridge: 4 of 8, no delay.
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, lanes_open against max_lanes is the more interesting series of the two.
Closed Is a State, Not an Absence
In that snapshot, 27 of the 81 CBP ports were reported closed — 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 6 am-10 pm or 8 am-4 pm). Laredo’s World Trade Bridge, a commercial-only crossing, was shut: it was 2 a.m. in Texas and its published hours are 7 am-Midnight.
A closed port comes back as critical, with the closure in the title:
{
"type": "border_crossing",
"severity": "critical",
"title": "Laredo - World Trade Bridge — closed",
"description": "Border crossing reported closed.",
"status": "closed",
"as_of": "2026-08-08T07:00:15Z",
"source": "CBP"
}
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.
How Much of It Is Actually Populated
Now the part most vendors leave out. In that same snapshot, of the 81 CBP ports, 17 carried a commercial delay figure. The other 64 were Update Pending (36), Lanes Closed (15) or N/A (13). On the CBSA side, 21 offices reported No Delay, three Not Applicable, one Temporarily closed, and four reported an actual number. FAST-lane figures — which we surface as fast_lane_wait_minutes and a derived fast_lane_savings_minutes when they are published — existed at exactly one port. (That last figure in particular is an artefact of the hour, and we come back to it below.)
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 — a border-wait API is mostly a “no figure right now” API, 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.
The Same Query at Ten in the Morning
“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 14 August 2026 at 13:57 UTC — 09:57 Eastern, mid-morning on a working Friday. The difference is large enough that you should not size your integration on the overnight figures:
| 3 a.m. ET, 8 Aug | 10 a.m. ET, 14 Aug | |
|---|---|---|
| CBP ports carrying a commercial delay figure | 17 of 81 | 35 of 81 |
| CBP ports reported closed | 27 | 3 |
| Highest commercial wait anywhere | 5 min | 30 min |
| Ports publishing a FAST figure | 1 | 11 |
| CBSA offices publishing a US-bound wait | 0 of 29 | 0 of 27 |
Two of those rows carry a lesson each. The population rate roughly doubles 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. The FAST count moved by a factor of eleven, 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 5 against a standard 30 — a genuine 25-minute saving, and exactly the case an enrolled carrier wants surfaced.
And the last row is the one that did not move. Every one of CBSA’s 27 offices still had -- 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.
The lane-capacity story sharpens in daylight too. Detroit’s Ambassador Bridge had gone from 2 of 13 commercial lanes to 5 of 13, 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 4 of 9 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 11 of its 19 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.
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.
The wider inventory works the same way, and it is the coverage number to plan against. As of 14 August 2026 we hold 197 active border crossings across North America; 108 of them — 81 CBP ports and 27 CBSA offices — sit behind a live wait feed. The remaining 89 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 info, never with an invented number.
Read that ratio before you design around this: 45% of the land border crossings we know about have no wait feed behind them at any hour, and no amount of waiting for daytime will change it — unlike the Update Pending 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.
What We Don’t Do
-
No history, no “typical Thursday.” We publish the live reading and a poll-over-poll
wait_trend(rising/falling/steady, 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. - No modelled waits. Every figure is the agency’s own, or there is no figure. No crowd-sourced estimate, no inference of a queue from silence.
- No bypass or clearance claims. FAST is surfaced when CBP publishes it. Whether your carrier is enrolled, and what that means at primary, is between you and the program.
The Same Field on the Other Side of the Atlantic
The schema is not North-America-shaped by accident. Our European deployment runs the identical border_crossing warning over Ukraine’s electronic queue at the EU external frontier, where commercial waits are measured in days rather than minutes — the same commercial_wait_minutes integer, rendered as “4 d 3 h” instead of “25 min”. If you run both continents, it’s one integration. That story is on the NAPSPAN blog.
Try It
-
API docs — the full
warnings[]reference, includingborder_crossingseverity bands and properties - Free API key — no credit card, 14-day trial
-
Truck routing with hazard warnings — how the whole corridor
warnings[]channel works - Data sources — every feed behind the API, with its licence
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.
Originally published on road511.com.
Top comments (0)