OSOW Quote API
Price an oversize/overweight lane over HTTP: permits, escorts, and requirements, state by state.
Every paid plan · Last updated 2026-08-27 · OpenAPI 3.1 spec · llms.txt
Overview
One endpoint, POST /api/v1/quotes, prices an oversize/overweight lane: send origin, destination, and dimensions; get back per-state permit fees, escort counts, police / superload / survey / utility flags, and lane totals.
- Coverage: the lower 48 states. Canadian lanes are in the calculator but not on the API yet; they return
unsupported_regionrather than a guess. - Same numbers as the calculator: same engine, same state data, same rate logic. Rates default to your account’s saved Bid Builder config (one exception, noted in rates).
- Latency: typically 2 to 5 seconds. The lane is routed live and priced through every state it crosses; this is a quote engine, not a cache lookup. Parallel requests are fine.
Quickstart
A real 7-axle load, 134″ wide, 161″ tall, 134,075 lbs, Savannah GA to Birmingham AL. Four required fields: idempotency_key, origin, destination, dims.
curl -X POST https://osowloads.com/api/v1/quotes \
-H "Authorization: Bearer osow_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"idempotency_key": "lane-2041-rev1",
"origin": "Savannah, GA",
"destination": "Birmingham, AL",
"dims": {
"length_in": 960,
"width_in": 134,
"height_in": 161,
"weight_lbs": 134075,
"axles": 7
}
}'Abridged response (... marks omitted fields; the response reference lists everything):
{
"quote_id": "q_01M0SWMHBB36QW785RH1014XJE",
"engine": "here",
"route_mi": 470.7,
"total_permits": 100,
"total_escorts": 0,
"states": [
{ "abbr": "GA", "mi": 365.5, "permit": 30, "permit_status": "priced", "escRequired": "No", "escRequiredQty": "", "escTravelingQty": 0, ... },
{ "abbr": "AL", "mi": 104.4, "permit": 70, "permit_status": "priced", "escRequired": "No", "escRequiredQty": "", "escTravelingQty": 0, ... }
],
"assumptions": [
{ "field": "dims.trailer_length_in", "applied": 720,
"rule": "trailer_length_in not supplied: total length minus 240 in (20 ft), floor 0" },
{ "field": "rates", "applied": { "escort_dpm": 1.85, ... },
"rule": "unspecified rates fall back to the account Bid Builder config (exceptions: service_fee_per_state defaults to 0; esl_mode defaults to 1)" }
],
"warnings": [],
"meta": {
"data_version": "statesv3@2026-08-15",
"rate_card": { "escort_dpm": 1.85, ... },
"usage": { "charge": "allowance", "remaining_allowance": 199, "remaining_lot_credits": 0 }
}
}Authentication
Bearer key in the Authorization: Bearer osow_live_… header. Keys are minted from your dashboard (org owners, any paid plan) and shown once; we store only a hash, so a lost key means revoke and re-mint. Treat it like a password: server-side only, never in client code or a public repo.
Create a quote
Prices one lane and charges one call on success. Unknown fields are rejected with a 422 rather than ignored, so a typo fails loudly instead of silently skewing a quote. Bodies over 64 KB are rejected. When you omit an optional field that affects the math, the default we applied is reported back in assumptions[].
Request body
idempotency_keyload-4471-rev2. Sending the same key again returns the stored result free instead of charging twice. See Idempotency & retries.originmeta.resolved — check it if a city name exists in more than one state.destinationorigin.dimslength_in, width_in, height_in, weight_lbs.Fields below in the dims reference.
waypointsviasengineoptions.exclude_states works on HERE; options.route_preference on Google.Default: here. HERE applies truck restrictions; on HERE failure we fall back to Google with an engine_fallback warning.
ow_inputsassumptions[].Fields below in the ow_inputs reference. Only read when weight_lbs > 80,000.
optionsFields below in the options reference.
ratesservice_fee_per_state).Fields below in the rates reference.
client_refmetadataKeys max 40 chars; string values max 200 chars; whole object max 1 KB.
dims — load dimensions
length_inwidth_inheight_inweight_lbsaxles).axlesweight_lbs > 80,000; 422 without it.overhang_front_inDefault: 0.
overhang_rear_inDefault: 0.
trailer_length_inOmitted: total length minus 240 in (20 ft tractor), floor 0 — echoed in assumptions[].
kingpin_inkingpin_exceeds_legal warning.Default: 0 (not checked).
ow_inputs — overweight detail
Only read when weight_lbs > 80,000. A handful of states price overweight oninputs beyond gross weight; these fields are how you supply them. Every omitted field gets a lean, defensible default, echoed in assumptions[] — send the real numbers when you have them.
wa_excess_lbsOmitted: computed as weight_lbs − 80,000.
mt_excess_axle_lbsmn_groupstype: "tandem" | "tridem" | "quad"; weight 0–200,000 lbs. Omitted on 7+ axle MN lanes: lean default grouping with auto-distributed weights, echoed in assumptions[].
mn_drive_lbs / mn_trailer_lbsmn_groups explicitly (both must be present to take effect).mn_trailer_typeil_front_lbs / il_rear_lbsax_steer_kg / ax_drive_kg / ax_trailer_kgoptions — response add-ons and routing controls
return_polylinepolyline) for drawing the lane on a map.Default: false.
return_tooltipstooltips: the same hover explanations the calculator shows (escort, police, superload, survey, engineering, utility, alerts, oversize, general).Default: false.
alternativesalternate.Default: false. Still one call.
compare_mimeta.compare_mi — a sanity check that the two engines agree on the path.Default: false. Not billed extra.
avoid_usus_crossing_forced warning instead.Default: false.
route_preferenceGoogle only; ignored on HERE with a warning.
exclude_statesno_route if it can’t avoid them.HERE only; ignored on Google with a warning.
rates — rate-card override
rates is a partial override. Any field you omit falls back to your account’s saved Bid Builder configuration — so API quotes match your dashboard quotes — with two exceptions: service_fee_per_state defaults to 0, not your saved service fee, and esl_mode defaults to 1 (Conservative) regardless of your saved setting. The resolved values actually used are always returned in meta.rate_card.escort_dpmescort_day_baseescort_day_minhotel_per_nightmiles_per_dayesl_mode0 Raw (each state’s escort count exactly as that state reports it), 1 Conservative (fills interior dips — if escorts are needed before and after a state, they’re carried through it), 2 Full Carry (the peak escort count rides the whole lane). Default: 1 — always, even if your Bid Builder is set differently; only this request field changes it. Raw underprices escort labor on any lane with a gap state, so the API never assumes it.survey_dpmsurvey_minimumsurvey_report_feeservice_fee_per_stateThe one field that does NOT inherit from your Bid Builder config; omitted = 0.
Response
Lane totals at the top, then one block per state in travel order. The per-state block uses the same cell vocabulary as the calculator’s results table, so anyone who has used the calculator already reads it. See the worked lanes for complete real bodies.
| Field | What it tells you |
|---|---|
| quote_id | Unique id for this quote (q_ prefix, sortable by creation time). |
| idempotency_key / client_ref / metadata | Your inputs, echoed back untouched, so you can match responses to your own records. |
| engine | Which routing engine actually produced the route (“here” or “google”; can differ from the request if we had to fall back, flagged in warnings). |
| route_mi | Total routed miles for the lane. |
| total_permits | Sum of state permit fees (oversize + settled overweight, deduplicated). Null whenever ANY state is not status “priced” — those states are listed in meta.unpriced_states with a reason. Note: null does not mean free; see Billing. |
| total_escorts / total_survey / total_service | Escort cost, route-survey cost, and service fees for the lane, from the rate card in play (meta.rate_card). |
| any_super / super_qty, any_police / police_qty, any_survey / survey_qty, any_engr / engr_qty, any_util / util_qty | Lane-level rollups: “Yes”, “CBC” (case-by-case), or “No”, each paired with a quantity string like “2″ or ”1 +1 CBC“ (1 firm state, 1 case-by-case). |
| orig_tier_flag / orig_tier_note, dest_tier_flag / dest_tier_note | Road-tier check at the endpoints: ”yes“ when a lower road tier in the pickup or delivery state could make requirements WORSE than the main-route answer (more escorts, superload, police, survey, engineering, utility). The note names each delta, e.g. ”escorts 1→2″. Local roads at the dock are where quotes quietly go wrong. |
| states[] | One block per state in travel order: abbr, mi, permit fee + status (priced / agency_priced / not_permittable / unavailable), permit_label, permit_reason (a frozen machine code), plain-English permit_note, extras (small add-on charges like bridge fees), escRequired/escRequiredQty (what the state itself requires), escTravelingQty (the count the escort dollars were priced on: ESL-smoothed, so escorts carried through a gap state show here while escRequired stays No; CBC states price as 0 unless carried through), superload, police/policeQty, survey, engr/engrType (Automated / State Agency / Third-Party), util/utilType (Notify / Permission / Personnel / Bucket Truck), tooltips. |
| states[].tooltips | With options.return_tooltips: the same hover explanations the calculator shows, per state (escort, police, superload, survey, engineering, utility, alerts, oversize, general), each as {bullets, notes, legend}. Null when not requested. |
| polyline | Google-encoded route geometry for drawing the lane on a map. Only with options.return_polyline; null otherwise. |
| alternate | With options.alternatives: HERE’s alternate route fully priced, still one call. Same shape as the main quote minus ids, assumptions, and meta (it never nests another alternate). Null when no alternate exists or wasn’t requested. |
| assumptions[] | The defaults we applied that materially affect the math — trailer length, rate fallbacks, overweight lean defaults — each as {field, applied, rule}. Schema-level defaults that don’t move money (engine choice, false option flags) are not echoed. If an assumption matters, send the field. |
| warnings[] | Coded, machine-readable flags (e.g. a state with no data, a kingpin setting over a state’s legal limit, an engine fallback). |
| meta | data_version, rate_card (the resolved rates actually used), routed_at, provider info, the resolved origin/destination geocodes, unpriced_states, compare_mi (the other engine’s mileage when requested), and usage (what this call charged and what remains). |
Errors
Every error is { error: { code, message, request_id } } with a stable machine code. Errors are never charged. But note the flip side: a 200 where a state comes back not_permittable or agency_priced is a successful, billed answer — knowing a state will not permit the rig as described IS the quote. Only unavailable (our own data gap) makes the call free.
| HTTP | Code | Meaning |
|---|---|---|
| 401 | unauthorized / key_revoked | Missing, invalid, revoked, or expired key. |
| 402 | insufficient_balance | Allowance used and no live credits. Buy a pack or wait for the reset. |
| 403 | org_suspended | Organization access is off. Contact support. |
| 409 | idempotency_key_reused / request_in_progress | Same key, different body; or the original request is still running. |
| 422 | validation_failed / unknown_field | Bad or unrecognized input; details[] lists each offending path. |
| 422 | geocode_failed / no_route / unsupported_region | We could not resolve or route the lane (Canada/Mexico lanes are not on the API yet). |
| 423 | suspended_for_review | API access paused pending a manual review (unusual traffic). Contact support. |
| 429 | rate_limited_minute / rate_limited_day | Honor Retry-After; the daily cap resets 00:00 UTC. |
| 5xx | provider_unavailable / metering_unavailable / internal | Our side or an upstream. Retry with the SAME idempotency_key: if the original was charged you get the stored result free, otherwise it runs fresh. |
Idempotency & retries
Every POST includes an idempotency_key: a receipt number you make up (any string up to 128 chars). It is not your API key. It makes retries safe — you cannot double-bill yourself:
| Situation | What happens |
|---|---|
| Same key, same request again | The stored answer comes back free, with an Idempotent-Replay: true header. |
| Same key, different body | 409 idempotency_key_reused. Not charged. Use a new key for a changed request. |
| Timeout or 5xx | Retry with the same key. If the original was charged you get its stored result free; otherwise it runs fresh. Charged at most once either way. |
| 429 rate limited | Wait the seconds in Retry-After, then retry with the same key. The per-minute limit refills continuously, so short waits work. |
Billing & limits
Prepaid, no surprise bills. One successful quote = one call. Not charged: errors of any kind, replays of an already-answered request, and any quote where our own data or routing could not answer (a state with permit_status: “unavailable”). Charged: everything else that returns 200 — including lanes where a state is agency_priced or not_permittable, because that determination is the answer. Every response shows where you stand in meta.usage.
| Limit | Default | Notes |
|---|---|---|
| Included calls | Solo 30 · Team 100 · Enterprise 200 / month | Resets the 1st of the month (UTC) |
| Extra credits | 2 API calls per credit | Never expire, spent oldest-first after the allowance |
| Burst | 200 / minute | Rolling bucket; 429 + Retry-After when exceeded |
| Daily cap | 1,000 / day per org | Raised on request from the dashboard, usually same day |
| Body size | 64 KB | Larger bodies are rejected with a 422 |
Examples & resources
Three real request/response pairs from the production API — a four-field minimum, a typical quote with a rate card, and a fully specified request — so you can see what each level of input buys you before writing a line of code.
Charlotte, NC → Nashville, TN
Four fields. Everything else falls back to a documented default, and every default we applied comes back named in assumptions[]: the trailer length we assumed, the rate card we used.
Request
{
"idempotency_key": "devdemo-2026-08-24-l1",
"origin": "Charlotte, NC",
"destination": "Nashville, TN",
"dims": {
"length_in": 900,
"width_in": 106,
"height_in": 160,
"weight_lbs": 66188
}
}Response (200 OK)
{
"quote_id": "q_01M0TRVP17S28F9X27RXVDVM5W",
"idempotency_key": "devdemo-2026-08-24-l1",
"client_ref": null,
"metadata": null,
"engine": "here",
"route_mi": 423.4,
"total_permits": 32,
"total_escorts": 0,
"total_survey": 0,
"total_service": 0,
"any_super": "No",
"super_qty": "",
"any_police": "No",
"police_qty": "",
"any_survey": "No",
"survey_qty": "",
"any_engr": "No",
"engr_qty": "",
"any_util": "No",
"util_qty": "",
"orig_tier_flag": "no",
"orig_tier_note": "",
"dest_tier_flag": "no",
"dest_tier_note": "",
"states": [
{
"abbr": "NC",
"mi": 177.3,
"permit": 12,
"permit_status": "priced",
"permit_label": null,
"permit_reason": null,
"permit_note": null,
"extras": "",
"escRequired": "No",
"escRequiredQty": "",
"escTravelingQty": 0,
"superload": "No",
"police": "No",
"policeQty": "",
"survey": "No",
"engr": "No",
"engrType": "",
"util": "No",
"utilType": "",
"tooltips": null
},
{
"abbr": "TN",
"mi": 245.3,
"permit": 20,
"permit_status": "priced",
"permit_label": null,
"permit_reason": null,
"permit_note": null,
"extras": "",
"escRequired": "No",
"escRequiredQty": "",
"escTravelingQty": 0,
"superload": "No",
"police": "No",
"policeQty": "",
"survey": "No",
"engr": "No",
"engrType": "",
"util": "No",
"utilType": "",
"tooltips": null
}
],
"polyline": null,
"alternate": null,
"assumptions": [
{
"field": "dims.trailer_length_in",
"applied": 660,
"rule": "trailer_length_in not supplied: total length minus 240 in (20 ft), floor 0"
},
{
"field": "rates",
"applied": {
"escort_dpm": 2,
"escort_day_base": 125,
"escort_day_min": 550,
"hotel_per_night": 125,
"miles_per_day": 450,
"esl_mode": 1,
"survey_dpm": 2,
"survey_minimum": 550,
"survey_report_fee": 250,
"service_fee_per_state": 0
},
"rule": "unspecified rates fall back to the account Bid Builder config (service_fee_per_state defaults to 0)"
}
],
"warnings": [],
"meta": {
"data_version": "statesv3@2026-08-15",
"rate_card": {
"escort_dpm": 2,
"escort_day_base": 125,
"escort_day_min": 550,
"hotel_per_night": 125,
"miles_per_day": 450,
"esl_mode": 1,
"survey_dpm": 2,
"survey_minimum": 550,
"survey_report_fee": 250,
"service_fee_per_state": 0,
"out_of_route_percent": 0,
"escort_safety_net": 0
},
"routed_at": "2026-08-24T20:54:42.951Z",
"provider": {
"engine": "here",
"alternatives_offered": 1
},
"resolved": {
"origin": "Charlotte, NC",
"destination": "Nashville, TN"
},
"unpriced_states": [],
"compare_mi": null,
"usage": {
"charge": "allowance",
"remaining_allowance": 198,
"remaining_lot_credits": 20
}
}
}Captured from live production calls on 2026-08-24: real routes, real fees at that data version. Your responses will differ as state data updates, and meta.usage will show your own balance.
Recipe: lane list in a spreadsheet
The most common first integration: loop the rows, one POST per row, write back the fields you care about. Fire requests in parallel — they don’t queue behind each other. Put the lane and dims in the idempotency key (sheet-row-14-savannah-birmingham-960x134) so re-runs replay free but an edited row re-prices. A 200-row sheet fits inside one minute’s burst; the daily cap paces anything bigger.
Resources
- OpenAPI 3.1 spec — import into Postman or a code generator.
- llms.txt — this entire reference as one markdown page for AI agents (same text as the copy button).