Skip to content
Last updated

Quote & Book Freight

Booking LTL freight is always a two-step process: request quotes, then book one of the offers you get back. This page covers both steps, along with the provider account lookup they both depend on. For the concepts behind handling units, freight class, accessorials, and hazmat, see Freight (LTL).

List Freight Provider Accounts

GET /v1/freight/provider_accounts

Freight is quoted and booked through a freight provider account connected to your account, and every request on these endpoints names the account it goes through. Start here to find the freight_provider_account_id to use.

curl -i -X GET \
  https://api.shipengine.com/v1/freight/provider_accounts \
  -H 'API-Key: YOUR_API_KEY_HERE'
{
  "freight_provider_accounts": [
    {
      "freight_provider_account_id": "se-4821",
      "freight_provider_name": "UNISHIPPERS",
      "nickname": "Unishippers - Midwest"
    }
  ]
}

Only active connections are returned, so an account with no freight provider connected — or one whose connection has been disabled — gets an empty array. freight_provider_name is the provider's code, the same value that comes back on quotes, shipments, and tracking. nickname is whatever the connection was named when it was set up, and is null when it has no name.

Get Freight Quotes

POST /v1/freight/quotes

Describe the freight and receive offers from the carriers available through your freight provider. Each offer is individually bookable.

curl -i -X POST \
  https://api.shipengine.com/v1/freight/quotes \
  -H 'API-Key: YOUR_API_KEY_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "freight_provider_account_id": "se-28529731",
    "shipment_id": "se-28529731",
    "ship_from": {
      "name": "Marcus Bell",
      "company_name": "Northgate Distribution",
      "address_line1": "4200 Industrial Pkwy",
      "address_line2": "Dock 12",
      "city_locality": "Grand Rapids",
      "state_province": "MI",
      "postal_code": "49512",
      "country_code": "US",
      "phone": "+1 616 555 0142",
      "email": "dock@northgate-dist.example",
      "location_type": "commercial"
    },
    "ship_to": {
      "name": "Marcus Bell",
      "company_name": "Northgate Distribution",
      "address_line1": "4200 Industrial Pkwy",
      "address_line2": "Dock 12",
      "city_locality": "Grand Rapids",
      "state_province": "MI",
      "postal_code": "49512",
      "country_code": "US",
      "phone": "+1 616 555 0142",
      "email": "dock@northgate-dist.example",
      "location_type": "commercial"
    },
    "shipment_date": "2026-04-17T00:00:00Z",
    "handling_units": [
      {
        "type": "pallet",
        "quantity": 2,
        "length": 48,
        "width": 40,
        "height": 52,
        "dimension_unit": "inch",
        "stackable": false,
        "commodities": [
          {
            "description": "Assembled oak dining chairs",
            "quantity": 24,
            "weight": 310,
            "weight_unit": "pound",
            "value": 4800,
            "packaging_type": "carton",
            "freight_class": "125",
            "nmfc_code": "80700-2",
            "hazardous_materials": {
              "identification_number_type": "un",
              "identification_number": "UN1263",
              "proper_shipping_name": "Paint",
              "hazard_class": "3",
              "subsidiary_hazard_classes": [
                "8"
              ],
              "packing_group": "iii",
              "emergency_contact_name": "Chemtrec",
              "emergency_contact_phone": "+1 800 424 9300",
              "emergency_response_reference": "CCN12345",
              "flashpoint_temperature": 73,
              "additional_details": "Keep upright. Do not stack."
            }
          }
        ]
      }
    ],
    "accessorials": {
      "liftgate_pickup": true,
      "inside_pickup": false,
      "carrier_terminal_pickup": false,
      "grocery_consolidation_pickup": false,
      "liftgate_delivery": true,
      "inside_delivery": false,
      "appointment_delivery": false,
      "notify_before_delivery": true,
      "hold_at_terminal": false,
      "grocery_consolidation_delivery": false,
      "sort_and_segregate": false,
      "protection_from_cold": false,
      "protection_from_heat": false,
      "tradeshow_pickup": {
        "name": "Midwest Home & Garden Expo",
        "booth_number": "B-1147"
      },
      "tradeshow_delivery": {
        "name": "Midwest Home & Garden Expo",
        "booth_number": "B-1147"
      }
    },
    "insurance": {
      "insured_value": 18500,
      "item_condition": "new",
      "commodity_category": "furniture",
      "marks_numbers": "NG-2026-0417"
    }
  }'

You can also try this request in the browser:

Loading...

Quoting Inline

Send the origin, destination, and handling units directly. Freight carriers need a contact name, company name, and phone number at both ends of the shipment, so those fields are required on ship_from and ship_to.

{
  "freight_provider_account_id": "se-4821",
  "shipment_date": "2026-04-17T00:00:00Z",
  "ship_from": {
    "name": "Marcus Bell",
    "company_name": "Northgate Distribution",
    "address_line1": "4200 Industrial Pkwy",
    "address_line2": "Dock 12",
    "city_locality": "Grand Rapids",
    "state_province": "MI",
    "postal_code": "49512",
    "country_code": "US",
    "phone": "+1 616 555 0142",
    "location_type": "commercial"
  },
  "ship_to": {
    "name": "Dana Reyes",
    "company_name": "Reyes Home Furnishings",
    "address_line1": "915 Commerce St",
    "city_locality": "Dallas",
    "state_province": "TX",
    "postal_code": "75202",
    "country_code": "US",
    "phone": "+1 214 555 0198",
    "location_type": "commercial"
  },
  "handling_units": [
    {
      "type": "pallet",
      "quantity": 2,
      "length": 48,
      "width": 40,
      "height": 52,
      "stackable": false,
      "commodities": [
        {
          "description": "Assembled oak dining chairs",
          "quantity": 24,
          "weight": 310,
          "freight_class": "125",
          "packaging_type": "carton"
        }
      ]
    }
  ],
  "accessorials": {
    "liftgate_delivery": true,
    "notify_before_delivery": true
  }
}

Quoting an Existing Shipment

Provide shipment_id instead of addresses. The origin is read from the shipment's ship-from warehouse and the destination from the order's ship-to address.

{
  "freight_provider_account_id": "se-4821",
  "shipment_id": "se-28529731",
  "handling_units": [
    {
      "type": "pallet",
      "quantity": 2,
      "length": 48,
      "width": 40,
      "height": 52,
      "commodities": [
        { "quantity": 24, "weight": 310, "freight_class": "125" }
      ]
    }
  ],
  "accessorials": { "liftgate_delivery": true }
}

Sending ship_from or ship_to alongside shipment_id is rejected. The shipment must have a ship-from warehouse and a destination address, otherwise the request returns a 400 Bad Request.

The handling_units, accessorials, and insurance you send here are saved as the shipment's freight configuration. Quote the same shipment again with only shipment_id and the saved configuration is reused:

{
  "freight_provider_account_id": "se-4821",
  "shipment_id": "se-28529731"
}

This is what makes re-quoting cheap — adjust one thing, or nothing at all, and ask again.

Reading the Offers

{
  "quotes": [
    {
      "freight_provider_account_id": "se-4821",
      "freight_provider_name": "UNISHIPPERS",
      "quote_request_id": "7f1c9a52-3d84-4f0e-9b3a-2c6d5e4f8a10",
      "offer_id": "0f2b41d8-6a17-4c9e-8f52-b71d3e9c4a68",
      "carrier_name": "FedEx Freight Economy",
      "carrier_scac": "FXFE",
      "service_name": "Standard LTL",
      "service_type": "Direct",
      "total_charges": 842.37,
      "transit_days": 3,
      "estimated_delivery_date": "2026-04-22T00:00:00Z",
      "is_guaranteed": false,
      "origin_terminal_code": "GRR",
      "destination_terminal_code": "DFW",
      "quote_expiration_date": "2026-04-16T23:59:59Z",
      "max_liability_new": 25,
      "max_liability_used": 10
    }
  ]
}

Four fields are what you carry into the booking request:

FieldWhy it matters
quote_request_idIdentifies the quote request that produced the offers. The same value appears on every offer in the response.
offer_idIdentifies the specific offer you are booking.
carrier_scacThe Standard Carrier Alpha Code of the carrier that would move the freight.
quote_expiration_dateWhen the offer stops being bookable.

The rest of the offer is there to help you choose. service_type tells you whether the origin terminal serves the lane itself (Direct) or hands the freight to another carrier (Interline) — interline moves involve an extra transfer. is_guaranteed distinguishes a guaranteed transit time from an estimate, and max_liability_new / max_liability_used show the carrier's liability cap per pound, which is usually the argument for adding insurance.

An empty or absent quotes array means no carrier available through that connection can serve the lane as described.

Book a Freight Shipment

POST /v1/freight/shipments

Book one of the offers. This dispatches the shipment with the carrier and generates the Bill of Lading.

curl -i -X POST \
  https://api.shipengine.com/v1/freight/shipments \
  -H 'API-Key: YOUR_API_KEY_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "freight_provider_account_id": "se-28529731",
    "shipment_id": "se-28529731",
    "quote_request_id": "7f1c9a52-3d84-4f0e-9b3a-2c6d5e4f8a10",
    "offer_id": "0f2b41d8-6a17-4c9e-8f52-b71d3e9c4a68",
    "quote_expiration_date": "2026-04-16T23:59:59Z",
    "carrier_scac": "FXFE",
    "ship_from": {
      "name": "Marcus Bell",
      "company_name": "Northgate Distribution",
      "address_line1": "4200 Industrial Pkwy",
      "address_line2": "Dock 12",
      "city_locality": "Grand Rapids",
      "state_province": "MI",
      "postal_code": "49512",
      "country_code": "US",
      "phone": "+1 616 555 0142",
      "email": "dock@northgate-dist.example",
      "location_type": "commercial"
    },
    "ship_to": {
      "name": "Marcus Bell",
      "company_name": "Northgate Distribution",
      "address_line1": "4200 Industrial Pkwy",
      "address_line2": "Dock 12",
      "city_locality": "Grand Rapids",
      "state_province": "MI",
      "postal_code": "49512",
      "country_code": "US",
      "phone": "+1 616 555 0142",
      "email": "dock@northgate-dist.example",
      "location_type": "commercial"
    },
    "handling_units": [
      {
        "type": "pallet",
        "quantity": 2,
        "length": 48,
        "width": 40,
        "height": 52,
        "dimension_unit": "inch",
        "stackable": false,
        "commodities": [
          {
            "description": "Assembled oak dining chairs",
            "quantity": 24,
            "weight": 310,
            "weight_unit": "pound",
            "value": 4800,
            "packaging_type": "carton",
            "freight_class": "125",
            "nmfc_code": "80700-2",
            "hazardous_materials": {
              "identification_number_type": "un",
              "identification_number": "UN1263",
              "proper_shipping_name": "Paint",
              "hazard_class": "3",
              "subsidiary_hazard_classes": [
                "8"
              ],
              "packing_group": "iii",
              "emergency_contact_name": "Chemtrec",
              "emergency_contact_phone": "+1 800 424 9300",
              "emergency_response_reference": "CCN12345",
              "flashpoint_temperature": 73,
              "additional_details": "Keep upright. Do not stack."
            }
          }
        ]
      }
    ],
    "pickup_details": {
      "pickup_date": "2026-04-17T00:00:00Z",
      "ready_time": "09:00",
      "close_time": "16:30",
      "is_self_scheduled": false,
      "location_type": "commercial"
    },
    "references": [
      {
        "type": "purchase_order",
        "value": "PO-84213"
      }
    ],
    "pickup_instructions": "Check in with the guard at gate 3 before backing into dock 12.",
    "delivery_instructions": "Delivery appointments accepted between 08:00 and 11:00 only.",
    "handling_instructions": "Do not double stack. Load with forks from the long side."
  }'

You can also try this request in the browser:

Loading...

Booking an Existing Shipment

When the quote was linked to a shipment_id, book with that same shipment_id. The origin, destination, and handling units all come from the shipment and its saved freight configuration, so ship_from, ship_to, and handling_units must be omitted.

{
  "freight_provider_account_id": "se-4821",
  "shipment_id": "se-28529731",
  "quote_request_id": "7f1c9a52-3d84-4f0e-9b3a-2c6d5e4f8a10",
  "offer_id": "0f2b41d8-6a17-4c9e-8f52-b71d3e9c4a68",
  "carrier_scac": "FXFE",
  "quote_expiration_date": "2026-04-16T23:59:59Z",
  "pickup_details": {
    "pickup_date": "2026-04-17T00:00:00Z",
    "ready_time": "09:00",
    "close_time": "16:30",
    "is_self_scheduled": false,
    "location_type": "commercial"
  },
  "references": [
    { "type": "purchase_order", "value": "PO-84213" }
  ],
  "pickup_instructions": "Check in with the guard at gate 3 before backing into dock 12.",
  "delivery_instructions": "Delivery appointments accepted between 08:00 and 11:00 only."
}

The shipment must already have a freight quote. Booking a shipment_id with no saved freight configuration returns a 400 Bad Request asking you to quote first, and a shipment whose freight plan was cancelled cannot be booked at all.

Booking Inline

Omit shipment_id and provide ship_from, ship_to, and handling_units — the same objects you sent when quoting. A shipment record is created for the booking and marked as shipped.

Pickup Details

pickup_details is required in both modes. ready_time and close_time are 24-hour HH:mm values local to the origin, and they define the window a driver can arrive in.

Keep the window realistic. A one-hour window is likely to be missed, and a missed pickup usually means the freight moves a day later. Set is_self_scheduled to true if you arrange the pickup with the carrier yourself; ShipStation API then does not request one on your behalf.

References and Instructions

references are printed on the Bill of Lading and are how your consignee and your own AP team recognize the shipment. type is a free-form label such as purchase_order, sales_order, or reference_1. Which reference types a carrier can actually print varies by carrier, and so does the length a carrier keeps — the API does not length-check value, but freight providers commonly truncate references to around 35 characters, so keep them short.

The three instruction fields reach different people: pickup_instructions and delivery_instructions go to the drivers at each end, while handling_instructions travels with the freight.

The Booking Response

{
  "freight_shipment_id": "se-98765432",
  "freight_provider_account_id": "se-4821",
  "freight_provider_name": "UNISHIPPERS",
  "status": "booked",
  "product_transaction_id": "7f1c9a52-3d84-4f0e-9b3a-2c6d5e4f8a10",
  "pickup_transaction_id": "3ac81f60-92b7-4de1-8f04-5b9a7c2e6d13",
  "bol_number": "BOL-20260417-4821",
  "pro_number": null,
  "confirmation_number": "FXFE-PU-884215",
  "documents": [
    { "document_id": null, "type": "BILL_OF_LADING", "url": null },
    { "document_id": null, "type": "PALLET_LABEL", "url": null }
  ]
}

Store the freight_shipment_id — it is how you retrieve, track, and cancel the shipment.

bol_number identifies the shipment on its paperwork and can be tracked immediately. pro_number is the carrier's own tracking number and is often assigned only after pickup, so it is usually null here. confirmation_number is the pickup confirmation from the carrier. product_transaction_id is the provider's own identifier for the booked shipment — it is the same value you sent as quote_request_id.

The documents array tells you what was generated, not where to get it: url and document_id are always null in the booking response. To download the documents, call List freight shipment documents.

Document type casing

The booking response reports document types in the freight provider's own format, in uppercase (BILL_OF_LADING). The document and shipment retrieval endpoints report them in lowercase (bill_of_lading) and include download URLs. Compare document types case-insensitively.

Expired Quotes

LTL offers are short-lived — usually the same day. Booking an offer whose quote_expiration_date has passed returns:

{
  "request_id": "4b8e2f4c-1f6f-4b1a-9c4a-8f5e6d7c8b9a",
  "errors": [
    {
      "error_source": "shipengine",
      "error_type": "system",
      "error_code": "unspecified",
      "message": "The quote has expired. Please request a new quote."
    }
  ]
}

This is the shape ShipStation API uses for every pre-flight rejection on these endpoints — error_type is system and error_code is unspecified, and the specifics are in message. The other rejections that arrive this way are a shipment_id with no freight configuration, a shipment_id whose freight plan was cancelled, and a shipment_id missing its ship-from warehouse or destination address. Match on message only for logging, never for control flow.

There is no way to extend an offer. Request fresh quotes and book from those. If your workflow involves human approval between quoting and booking, re-quote at the moment of approval rather than booking a stored offer.

Errors

StatusCause
400Validation failed, the freight provider connection is not active, the quote expired, the linked shipment has no freight configuration or a cancelled one, or the provider rejected the request.
404The shipment_id does not identify a shipment on your account.

Every rejection falls into one of four shapes. Branch on error_code together with error_source, never on message.

error_source / error_type / error_codeWhat it means
shipengine / validation or business_rules / field_value_required or unspecifiedThe request failed field validation, either because a required field was missing (handling_units is required and must contain at least one item) or because a value broke a rule — an unrecognized location_type, a freight_class outside the accepted list, a ready_time that is not HH:mm, a dimension that is not greater than zero, or ship_from sent alongside shipment_id. field_name names the offending field, and message states the rule. Handle both codes: they are the same class of client-side mistake.
shipengine / system / unspecifiedThe request is well-formed but cannot proceed: the quote expired, the linked shipment has no freight configuration, its freight plan was cancelled, or it is missing its ship-from warehouse or destination address. The specifics are only in message.
carrier / business_rules / unspecifiedThe freight provider or carrier refused the request. message carries the provider's own wording behind a fixed prefix.

Provider and carrier rejections — a lane the carrier does not serve, a freight class it will not accept, an inactive provider connection — all come back as 400 responses in this shape:

{
  "request_id": "4b8e2f4c-1f6f-4b1a-9c4a-8f5e6d7c8b9a",
  "errors": [
    {
      "error_source": "carrier",
      "error_type": "business_rules",
      "error_code": "unspecified",
      "carrier_id": "se-4821",
      "carrier_code": "unknown",
      "carrier_name": "unknown",
      "message": "A shipping carrier error occurred: No rates available for the requested lane."
    }
  ]
}

message is the provider's own wording behind the fixed prefix A shipping carrier error occurred: . That wording is the most specific information available, so log it — but strip the prefix before showing it to a user.

The carrier fields do not mean what they look like

On freight errors, carrier_id is your freight_provider_account_id, not a carrier ID you can use anywhere else. carrier_code and carrier_name are always unknown, because a freight offer does not map to a parcel carrier record. Ignore all three; use carrier_scac from the offer to identify the carrier.