Skip to content

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_region rather 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

POST/api/v1/quotes

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_key
string · 1–128 chars
required
A receipt number you make up for this request (not your API key), like load-4471-rev2. Sending the same key again returns the stored result free instead of charging twice. See Idempotency & retries.
origin
string or { lat, lng }
required
“City, ST”, a full address, or coordinates. The geocoded resolution is echoed in meta.resolved — check it if a city name exists in more than one state.
destination
string or { lat, lng }
required
Same formats as origin.
dims
object
required
Load dimensions and weight. Four required members: length_in, width_in, height_in, weight_lbs.

Fields below in the dims reference.

waypoints
array · max 3
optional
Intermediate stops, each a string address or coordinates (geocoded like origin/destination).
vias
array of { lat, lng } · max 5
optional
Route-shaping points the route must pass through — coordinates only, not stops.
engine
"here" | "google"
optional
Routing engine. options.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_inputs
object
optional
State-specific overweight detail (Minnesota axle groups, Washington/Montana excess weight, Illinois axle weights). Anything omitted uses a lean default, echoed in assumptions[].

Fields below in the ow_inputs reference. Only read when weight_lbs > 80,000.

options
object
optional
Response add-ons and routing controls. All flags default to false.

Fields below in the options reference.

rates
object
optional
Rate-card override for the escort / survey / service cost math. Omitted fields fall back to your account’s saved Bid Builder rates (one exception: service_fee_per_state).

Fields below in the rates reference.

client_ref
string · max 200
optional
Your own load or RFQ id, echoed back untouched.
metadata
object
optional
Free-form string/number pairs, echoed back untouched.

Keys max 40 chars; string values max 200 chars; whole object max 1 KB.

dims — load dimensions

length_in
number · 0–6,000
required
Overall length in inches, front bumper to rearmost point including overhang.
width_in
number · 1–1,200
required
Overall width in inches.
height_in
number · 1–600
required
Overall height in inches.
weight_lbs
number · 1–2,000,000
required
Gross vehicle weight in pounds. Over 80,000 lbs triggers overweight pricing (and requires axles).
axles
integer · 5–30
conditional
Total axle count. Required when weight_lbs > 80,000; 422 without it.
overhang_front_in
number · 0–600
optional
Front overhang in inches. A flat field, not part of an overhangs object.

Default: 0.

overhang_rear_in
number · 0–600
optional
Rear overhang in inches.

Default: 0.

trailer_length_in
number · 0–6,000
optional
Trailer length in inches. Some states rate on trailer length, so the default matters; send it when you know it.

Omitted: total length minus 240 in (20 ft tractor), floor 0 — echoed in assumptions[].

kingpin_in
number · 0–1,200
optional
Kingpin-to-rear-axle distance in inches. When set, states whose legal KPRA limit is exceeded get a kingpin_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_lbs
number · 0–2,000,000
optional
Washington: the excess pounds over legal gross that WSDOT’s overweight schedule prices on.

Omitted: computed as weight_lbs − 80,000.

mt_excess_axle_lbs
number · 0–2,000,000
optional
Montana: your declared excess axle weight in pounds over the Federal Bridge Chart allowance.
mn_groups
array · max 12 of { type, weight }
optional
Minnesota prices overweight per axle group. List each non-steer group with its weight; groups at or under legal weight cost $0 and can be left out.

type: "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_lbs
number · 0–200,000 each
optional
Minnesota: drive-axle and trailer-axle group weights, an alternative to listing mn_groups explicitly (both must be present to take effect).
mn_trailer_type
string · max 50
optional
Minnesota: trailer axle configuration (e.g. tandem, tridem) for the drive/trailer pair above.
il_front_lbs / il_rear_lbs
number · 0–100,000 each
optional
Illinois prices from a front/rear axle-weight matrix; these are those two weights.
ax_steer_kg / ax_drive_kg / ax_trailer_kg
number · 0–60,000 each
optional
Per-axle-group weights in kilograms for Canadian pricing. Accepted for forward compatibility; they have no effect today because the API prices lower-48 lanes only.

options — response add-ons and routing controls

return_polyline
boolean
optional
Include the encoded route geometry (polyline) for drawing the lane on a map.

Default: false.

return_tooltips
boolean
optional
Include per-state tooltips: the same hover explanations the calculator shows (escort, police, superload, survey, engineering, utility, alerts, oversize, general).

Default: false.

alternatives
boolean
optional
When HERE offers an alternate route, price it too and return it as alternate.

Default: false. Still one call.

compare_mi
boolean
optional
Also route the lane on the other engine and report its mileage in meta.compare_mi — a sanity check that the two engines agree on the path.

Default: false. Not billed extra.

avoid_us
boolean
optional
Prefer a routing that stays out of the US when one exists. Built for future cross-border lanes; on a lower-48 lane every route crosses the US, so you get a us_crossing_forced warning instead.

Default: false.

route_preference
integer · −100–100, steps of 5
optional
Route bias: negative prefers shorter, positive prefers faster.

Google only; ignored on HERE with a warning.

exclude_states
array · max 12 two-letter codes
optional
Hard wall: the route will not enter these states, or fails with no_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_dpm
number · 0–50
optional
Escort charge in dollars per route mile, per active escort.
escort_day_base
number · 0–5,000
optional
Base retainer per escort per day.
escort_day_min
number · 0–5,000
optional
Minimum an escort earns per day; applied as a true-up when mileage pay falls short.
hotel_per_night
number · 0–2,000
optional
Hotel charge per escort per overnight on multi-day lanes.
miles_per_day
number · 50–1,500
optional
Assumed miles covered per travel day; drives how many escort days and hotel nights a lane needs.
esl_mode
integer · 0, 1, or 2
optional
Escort smoothing across the lane: 0 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_dpm
number · 0–50
optional
Route-survey charge in dollars per mile, per state that requires a survey.
survey_minimum
number · 0–5,000
optional
Minimum survey charge per surveyed state.
survey_report_fee
number · 0–5,000
optional
Flat report fee per surveyed state.
service_fee_per_state
number · 0–500
optional
Flat service fee multiplied by the number of states the route crosses.

The 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.

FieldWhat it tells you
quote_idUnique id for this quote (q_ prefix, sortable by creation time).
idempotency_key / client_ref / metadataYour inputs, echoed back untouched, so you can match responses to your own records.
engineWhich routing engine actually produced the route (“here” or “google”; can differ from the request if we had to fall back, flagged in warnings).
route_miTotal routed miles for the lane.
total_permitsSum 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_serviceEscort 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_qtyLane-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_noteRoad-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[].tooltipsWith 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.
polylineGoogle-encoded route geometry for drawing the lane on a map. Only with options.return_polyline; null otherwise.
alternateWith 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).
metadata_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.

HTTPCodeMeaning
401unauthorized / key_revokedMissing, invalid, revoked, or expired key.
402insufficient_balanceAllowance used and no live credits. Buy a pack or wait for the reset.
403org_suspendedOrganization access is off. Contact support.
409idempotency_key_reused / request_in_progressSame key, different body; or the original request is still running.
422validation_failed / unknown_fieldBad or unrecognized input; details[] lists each offending path.
422geocode_failed / no_route / unsupported_regionWe could not resolve or route the lane (Canada/Mexico lanes are not on the API yet).
423suspended_for_reviewAPI access paused pending a manual review (unusual traffic). Contact support.
429rate_limited_minute / rate_limited_dayHonor Retry-After; the daily cap resets 00:00 UTC.
5xxprovider_unavailable / metering_unavailable / internalOur 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:

SituationWhat happens
Same key, same request againThe stored answer comes back free, with an Idempotent-Replay: true header.
Same key, different body409 idempotency_key_reused. Not charged. Use a new key for a changed request.
Timeout or 5xxRetry 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 limitedWait 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.

LimitDefaultNotes
Included callsSolo 30 · Team 100 · Enterprise 200 / monthResets the 1st of the month (UTC)
Extra credits2 API calls per creditNever expire, spent oldest-first after the allowance
Burst200 / minuteRolling bucket; 429 + Retry-After when exceeded
Daily cap1,000 / day per orgRaised on request from the dashboard, usually same day
Body size64 KBLarger 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).