TruckTabs API · v1

Quotes

A quote is priced by the same engine as trucktabs.com. It works out the states on the route, adds oversize/overweight permits where the load is over a state's legal limits, and returns every fee.

Create a quote

POSThttps://trucktabs.com/api/v1/quotes

Needs a write key. Every quote becomes a draft order on our side, which is what lets the carrier pay from resume_url, so send an Idempotency-Key if you might retry.

Request body

FieldTypeRequiredNotes
permit_typesarrayYesAny of trip, fuel, osow. Oversize/overweight is added by us in each state where the load needs it, so you rarely ask for it.
route.originplaceYes*Where the trip starts. See Places below.
route.destinationplaceYes*Where the trip ends.
route.statesarrayYes*Instead of origin and destination: the states crossed, in order, each { "state": "OR", "miles": 210 }. Use it when your system already has the miles. The miles are yours: we price what you send, in whole miles (at least 1 a state).
route.stopsarrayNoNot supported yet. A request with stops is refused with UNSUPPORTED rather than priced wrong.
vehicle.vehicle_typecodeYesFor example tractor_trailer. See vehicle types.
vehicle.trailer_typecodeSemisFor example step_deck. See trailer types.
vehicle.trailer_lengthmeasureSemisThe trailer length. At least 10 ft.
vehicle.power_unit_axlesintegerSemisAxles on the tractor or truck.
vehicle.trailer_axlesintegerSemisAxles on the trailer.
vehicle.total_axlesintegerSingle unitsEvery vehicle type other than tractor_trailer and truck_trailer.
vehicle.combined_lengthmeasureSingle unitsThe vehicle's overall length. At least 10 ft.
vehicle.deck_heightmeasureNoOpen decks: how high the load sits off the road. Only with load.cargo_height (refused with load.height); left out, we use a typical deck for the trailer type (60 in flatbed, 42 in step deck, 24 in lowboy, RGN and double drop). Where there is no typical deck, such as Power Only, it is listed in details.missing.
vehicle.empty_weight_lbsintegerNoThe truck and trailer, empty. Only with load.weight_lbs (refused with gross_weight_lbs); left out, we use a typical figure for the trailer type, or list it in details.missing where there is none (single vehicles, a pickup pulling a trailer).
load.gross_weight_lbsintegerYes*Gross weight in pounds, with the truck and trailer. Or send load.weight_lbs instead. A gross well below what the trailer type weighs empty is refused as the load's weight alone.
load.weight_lbsintegerYes*The load on its own. The gross is this plus vehicle.empty_weight_lbs. Send this or gross_weight_lbs, not both.
load.lengthmeasureSometimesLength, width and height are required when the load could be oversize (for example an open trailer or an osow request). A missing one is listed in details.missing.
load.widthmeasureSometimesDecides whether a state needs an oversize permit.
load.heightmeasureSometimesOverall height from the road, at least 24 in, trailer included. Legal height differs by state, so this can change the answer at a state line. Or send load.cargo_height instead.
load.cargo_heightmeasureSometimesThe load on its own. The overall height is this plus vehicle.deck_height (or, on an enclosed trailer, the trailer's height; on a single vehicle it is taken as the overall height). Send this or load.height, not both. A piece's own height sent as load.height makes a tall load look legal.
load.categorycodeNoSee load categories.
load.descriptionstringNoUp to 500 characters, for example "Excavator".
external_refstringNoYour own reference, up to 64 characters. Echoed back.
metadataobjectNoUp to 10 keys of 1 to 40 characters. Values are strings of up to 200 characters, numbers, booleans or null. Echoed back, never read by us.

* Send route.origin and route.destination, or route.states, not both. A measure is { "value": 48, "unit": "ft" } with a unit of ft or in and at most 2 decimal places. A value far too big or too small for its field, such as a length over 500 feet or a 13-inch height, is refused as a likely unit slip. Codes also accept the display names and common aliases shown in the reference, in any case.

Places

A place is an object in one of these forms. To choose between similar names before quoting, use GET /places.

FieldTypeRequiredNotes
city + statestringNo{ "city": "Portland", "state": "OR" }. Only a match in that state counts. Add postal_code to pick between places of the same name in that state.
postal_codestringNo{ "postal_code": "97201" }. Only a place with that ZIP code counts. If none has it, the quote is refused with PLACE_NOT_FOUND.
lat + lng + statenumberNo{ "lat": 45.52, "lng": -122.68, "state": "OR" }. Used as given, once we have checked the point with Mapbox. Coordinates far outside the United States are refused with INVALID_INPUT. A point that isn't inside that state, or isn't in any US state, is refused with PLACE_MISMATCH.
querystringNo{ "query": "Portland, OR" }. Free text. A state code or ZIP code at the end narrows it. If it matches places in more than one state and nothing narrows it, the quote is refused with the candidates.

Example

curl
curl https://trucktabs.com/api/v1/quotes \
  -H "Authorization: Bearer $TRUCKTABS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d @quote.json
quote.json
{
  "permit_types": ["trip", "fuel"],
  "route": {
    "origin": { "city": "Portland", "state": "OR", "postal_code": "97201" },
    "destination": { "city": "Boise", "state": "ID" }
  },
  "vehicle": {
    "vehicle_type": "tractor_trailer",
    "trailer_type": "step_deck",
    "trailer_length": { "value": 48, "unit": "ft" },
    "power_unit_axles": 3,
    "trailer_axles": 2
  },
  "load": {
    "category": "construction_farm_equipment",
    "gross_weight_lbs": 92000,
    "length": { "value": 38, "unit": "ft" },
    "width": { "value": 10, "unit": "ft" },
    "height": { "value": 13, "unit": "ft" },
    "description": "Excavator"
  },
  "external_ref": "LOAD-284113",
  "metadata": { "shipment_id": "284113" }
}

Amounts, descriptions and miles in these examples are illustrative, not current rates.

201 Created · status quoted
{
  "id": "5b0f6c1e-8a7d-4c1e-9f3a-2d6b7c8e9f01",
  "reference": "48213",
  "status": "quoted",
  "currency": "USD",
  "subtotal": 424,
  "processing_fee": 21.2,
  "total": 445.2,
  "states": [
    {
      "state": "OR",
      "state_name": "Oregon",
      "miles": 211,
      "permits": ["trip", "fuel", "osow"],
      "amount": 159,
      "osow_auto_applied": true,
      "osow_not_needed": false,
      "osow_reasons": ["width"],
      "lines": [
        { "kind": "state_fee", "permit": "trip", "description": "Oregon Trip Permit", "amount": 43 },
        { "kind": "state_fee", "permit": "fuel", "description": "Oregon Fuel Permit", "amount": 56 },
        { "kind": "state_fee", "permit": "osow", "description": "Oregon OS/OW Permit", "amount": 60 }
      ]
    },
    {
      "state": "ID",
      "state_name": "Idaho",
      "miles": 219,
      "permits": ["trip", "fuel", "osow"],
      "amount": 105,
      "osow_auto_applied": true,
      "osow_not_needed": false,
      "osow_reasons": ["width"],
      "lines": [ … ]
    }
  ],
  "fees": [
    { "kind": "service_fee", "description": "TruckTabs service fee — OR, ID", "amount": 160 },
    { "kind": "processing_fee", "description": "Processing fee (5%)", "amount": 21.2 }
  ],
  "oversize_notice": {
    "title": "Oversize/overweight permits added",
    "message": "This load is over the legal limits in Oregon and Idaho, so an OS/OW permit is included in each."
  },
  "advisories": [],
  "route": {
    "origin": { "label": "Portland, Oregon 97201", "city": "Portland", "state": "OR", "postal_code": "97201", "lat": 45.5152, "lng": -122.6784 },
    "destination": { "label": "Boise, Idaho", "city": "Boise", "state": "ID", "postal_code": null, "lat": 43.615, "lng": -116.2023 },
    "mileage_source": "route"
  },
  "repriced": false,
  "external_ref": "LOAD-284113",
  "metadata": { "shipment_id": "284113" },
  "created_at": "2026-09-15T17:42:09.311Z",
  "resume_url": "https://trucktabs.com/permits?resume=…"
}

A quote our team must look at first comes back as needs_review. That happens for unusual dimensions, very large amounts, or a state we can't price automatically. It has no total, and every amount in the breakdown is null. The carrier can still open resume_url, and nothing is charged until the team confirms the price.

201 Created · status needs_review
{
  "id": "…",
  "reference": "48214",
  "status": "needs_review",
  "currency": "USD",
  "estimated_total": 1873.4,
  "review_message": "Orders this size get a second look before we charge the card. Submit it and we'll confirm the total with you.",
  "states": [
    {
      "state": "OR",
      "amount": null,
      "lines": [ { "kind": "state_fee", "permit": "osow", "description": "Oregon OS/OW Permit", "amount": null }, … ],
      …
    }
  ],
  "fees": [ { "kind": "service_fee", "description": "TruckTabs service fee — OR, ID", "amount": null }, … ],
  …
}

A legal_load means you asked only for osow and the load is within every state's legal limits, so there is nothing to buy. The carrier can still add OS/OW from the resume link if they want it anyway.

201 Created · status legal_load
{
  "id": "…",
  "reference": "48215",
  "status": "legal_load",
  "currency": "USD",
  "total": 0,
  "states": [
    { "state": "OR", "permits": [], "amount": 0, "osow_not_needed": true, "lines": [], … }
  ],
  "fees": [],
  …
}

Response fields

FieldTypeRequiredNotes
idstringAlwaysThe quote id, for GET /quotes/{id}.
referencestringAlwaysOur order number. Short enough for a charge description, and what our team will ask for.
statusstringAlwaysquoted, needs_review or legal_load. On a later read, also ordered or canceled.
currencystringAlwaysUSD.
subtotalnumberquotedState permit fees and service fees.
processing_feenumberquotedCharged once per order.
totalnumberquoted, legal_loadWhat the carrier pays. Never present on needs_review. 0 on legal_load.
estimated_totalnumberneeds_reviewPresent only when every state priced. An estimate: the team confirms the price before anything is charged.
review_messagestringneeds_reviewWhy a person needs to look at it, in a sentence you can show the carrier.
states[]arrayAlwaysPer state: state, state_name, miles, permits, amount (its permit fees, the sum of its lines), osow_auto_applied, osow_not_needed, osow_reasons and lines. With osow asked for, we decide each state: over a limit, OS/OW is added and osow_reasons says which (width, height, length, overhang, weight, or unknown when we have no limits on file); within every limit, osow_not_needed is true and nothing is charged there.
load_checkedobjectNoWhat each state was checked against: overall_height_in and gross_weight_lbs, with the parts they came from (deck_height_in, trailer_height_in for an enclosed trailer whose load rides inside, cargo_height_in, empty_weight_lbs, load_weight_lbs; null when you sent the overall figures). Show it to the carrier: their own deck and empty weight can change the answer. The whole block is null once the quote has been re-priced, and absent on quotes made before 2026-10-23.
fees[]arrayAlwaysOrder-level lines: service fees and the processing fee. The states' amounts plus these add up to total.
amounts on needs_reviewNoEvery amount in states, lines and fees is null, so nothing adds up to a price the team hasn't confirmed.
oversize_noticeobjectNotitle and message when we added oversize/overweight permits because the load is over a state's limits, or because a state's limits aren't on file (title “Limits not on file”); otherwise null. Since 2026-10-23 it is set on quotes that asked for oversize/overweight too.
advisories[]arrayAlwaysPlain sentences worth showing the carrier.
routeobjectAlwaysThe places we priced, and mileage_source: route, straight_line (no drivable route was found; confirmed by hand) or provided (you sent route.states).
repricedbooleanAlwaysfalse when created. On a later read, true if the carrier changed the trip since. See Get a quote.
resume_urlstringNoA link where the carrier reviews the quote, adds their details and pays. It works like a password to this order: give it only to the carrier. Null once the order is placed or canceled.
external_ref, metadataNoWhat you sent.
created_atstringAlwaysISO 8601, UTC.

The response also has a Location header pointing at the quote.

Errors

StatusCodeWhen
422INVALID_INPUTA value is wrong (details.invalid) or missing (details.missing). Both lists are complete. This includes a value too big or small for its field, which is usually a unit slip.
422UNSUPPORTEDSomething we don't quote yet, such as route.stops.
422PLACE_AMBIGUOUSA place matches more than one state. details.candidates lists them; send the one you mean with its state, or its lat, lng and state.
422PLACE_NOT_FOUNDWe couldn't find a place, or no place has the postal_code you sent. details.field says which.
422PLACE_MISMATCHCoordinates you sent aren't in the state you sent with them. details.field says which.
422NO_STATESThe route doesn't cross any US state we permit.
422INCOMPLETEThe quote couldn't be priced with what was sent. The message says what to add.
409IDEMPOTENCY_MISMATCHThe Idempotency-Key already created a quote from a different body.
409IDEMPOTENCY_IN_PROGRESSThe first request with this key is still running. Retry after a moment.
429RATE_LIMITEDToo many writes or quotes this minute. Wait Retry-After seconds.
429QUOTE_LIMIT_REACHEDYour daily allowance is used. Every quote that reaches pricing counts. It resets at midnight UTC.
502 / 503ROUTING_FAILED, ROUTING_UNAVAILABLEWe couldn't look up places or miles. Retry, or send route.states with miles.
500 / 503INTERNAL, QUOTE_FAILED, SAVE_FAILED, UNAVAILABLEOur problem. Retry with the same Idempotency-Key and you get one quote back. A failure during pricing can leave an unfinished draft on our side, which counts toward the daily allowance. If it persists, send us the request_id.
422 · PLACE_AMBIGUOUS
{
  "error": {
    "code": "PLACE_AMBIGUOUS",
    "message": "The origin matches places in more than one state. Add the state.",
    "details": {
      "field": "route.origin",
      "candidates": [
        { "label": "Portland, Oregon", "city": "Portland", "state": "OR", "postal_code": null, "lat": 45.5152, "lng": -122.6784 },
        { "label": "Portland, Maine", "city": "Portland", "state": "ME", "postal_code": null, "lat": 43.6591, "lng": -70.2568 }
      ]
    }
  },
  "request_id": "req_Qm3v8Kx1Tn5bW2pL9sR4"
}

Get a quote

GEThttps://trucktabs.com/api/v1/quotes/{id}

Returns a quote your account created, in the same shape as when it was created, with status brought up to date. Nothing is priced again. A quote that belongs to another account is 404 NOT_FOUND.

  • ordered: the carrier completed the order, or our team took it on. ordered_at is set and resume_url is null.
  • canceled: the order was canceled. canceled_at is set and resume_url is null.
  • Otherwise the original status, with a working resume_url.

The carrier can change the trip after opening resume_url, and our team can take an order over. If either moved the price or put the order on hold, repriced is true. The per-state breakdown is still the one you were quoted, with every amount set to null. total is the order's current price, and it is left out while the order is held for review.

curl
curl https://trucktabs.com/api/v1/quotes/5b0f6c1e-8a7d-4c1e-9f3a-2d6b7c8e9f01 \
  -H "Authorization: Bearer $TRUCKTABS_API_KEY"