Estes Express Lines API: LTL Rates, Bills of Lading, Pickups, Tracking and Documents

Estes Express Lines (Estes, SCAC EXLA) publishes one REST API suite on cloudapi.estes-express.com, and the estes carrier talks to it. It quotes LTL shipments across every Estes service level, estimates transit time, files the electronic Bill of Lading and returns the labels and BOL as PDFs, requests, checks and cancels pickups, tracks shipments by PRO or by reference, and fetches shipping documents (the BOL, delivery receipt, invoice...).

Credentials#

Every Estes call needs an API key and a MyEstes login:

Parameter Estes value
key Your Estes API key, sent in the apikey header
username Your MyEstes username
password Your MyEstes password
account_number The Estes account the shipment is priced and billed against

The same values can come from the environment as RS_ESTES_KEY, RS_ESTES_USERNAME, RS_ESTES_PASSWORD and RS_ESTES_ACCOUNT_NUMBER.

Getting the key takes three steps, all on Estes' side:

  1. Email [email protected] with your developer name, email, title and company. Estes sends two emails with a client id and client secret, one for the test environment and one for production.

  2. Generate the key once per environment with the client id and secret as Basic auth. The response carries the API key and a new client secret; keep both.

    curl -X POST https://cloudapi.estes-express.com/v1/api-key -u CLIENT_ID:CLIENT_SECRET
  3. Sign up for a MyEstes account if you do not have one.

RocketShipIt exchanges the login and the key for a bearer token before each request; Estes binds the token to the login that fetched it. The authenticate action returns that token in data.access_token with data.expires_in (about ten hours) if you want to see it. The password, the API key and the token are redacted from meta.debug_information.

{
  "carrier": "Estes",
  "action": "authenticate",
  "params": {
    "key": "YOUR_ESTES_API_KEY",
    "username": "YOUR_MYESTES_USERNAME",
    "password": "YOUR_MYESTES_PASSWORD"
  }
}

Transportation management systems serving several Estes customers need each customer's MyEstes login; one key covers them all.

Test and production#

Requests go to cloudapi.estes-express.com. "test": true sends them to Estes' UAT host uat-cloudapi.estes-express.com, which takes its own API key and client credentials. Estes lists the UAT host for the Bill of Lading and pickup APIs; rates, transit times, tracking and documents are documented against production only, so expect those to need a production key. The BOL API's own isTest flag is never sent as true; the host decides.

Supported actions#

Action Estes endpoint
authenticate POST /authenticate
getallrates POST /v1/rate-quotes
timeintransit POST /v1/route-guides
submitshipment POST /v1/bol
track GET /v1/shipments/history
createpickup POST /v1/pickup-requests
pickupstatus GET /v1/pickup-requests/{requestNumber}
cancelpickup DELETE /v1/pickup-requests/{requestNumber}
gettrackingdocuments GET /v1/search/images/{document-type}

Estes has no endpoint to cancel a filed BOL, so voidshipment is not supported; contact Estes to void a PRO. Invoices and event subscriptions are not supported yet.

Describing the freight#

Each entry in packages is one handling unit (a skid, pallet, crate, drum...). Set on each package:

  • weight - the handling unit's weight in weight_unit (LB or KG). Estes rates in pounds; kilograms are converted.
  • freight_class - the NMFC class as a number: 50, 55, 60, 65, 70, 77.5, 85, 92.5, 100, 110, 125, 150, 175, 200, 250, 300, 400 or 500.
  • packaging_type - the handling unit type. SKID when left out. PALLET, CRATE, DRUM, BOX, BUNDLE, CARTON, CASE, BAG, BALE, BARREL, ROLL, REEL, TOTE, PAIL, PIECE and the rest are translated to Estes' codes for whichever API the action uses (the rating and pickup APIs say SK and DR, the BOL API says SKD and DRM).
  • sub_packaging_type - the packaging of the pieces on the handling unit (BOX, CARTON...). Estes does not accept a skid or pallet as the pieces' packaging on the BOL, so a skid of unspecified pieces ships as PCS.
  • pieces - how many pieces are on the handling unit (default 1).
  • length, width, height in length_unit (IN or CM; centimeters are converted). Required for the time critical, volume and exclusive use levels and recommended for every rate.
  • description - the commodity, printed on the BOL.
  • po_number - a purchase order number printed on the BOL.
  • hazmat - a hazmat block with one chemical_records entry giving id_number (UN1263), proper_shipping_name, class_division_number and packaging_group, plus emergency_contact and emergency_phone. The HAZ accessorial is added for you. See Hazmat.

Shipment level:

  • service - leave it out of getallrates to quote every service level. To narrow it, one or more of LTL, LTLTC (time critical guaranteed), VTL (volume LTL and truckload), VTLTC, ERG (Estes retail guarantee) and EU (exclusive use), comma separated. TIME_CRITICAL, GUARANTEED, VOLUME and EXCLUSIVE_USE are accepted too.
  • accessorials (or addons) - Estes accessorial codes. Each API has its own list; see Accessorials.
  • lift_gate_required - a lift gate at delivery.
  • ship_date (or pickup_date) - the day the freight ships (2026-12-15 or 20261215). Rates are priced for that day; the BOL prints it; the pickup books it. Today when left out.
  • ready_time - on rates, the ship time (08:00); on pickups, the start of the pickup window.
  • insured_value - asks for full value coverage of that amount (whole dollars or cents, 100 to 150000) on rates and on the BOL.
  • ship_contact, ship_phone, ship_email - the person requesting the quote. Estes requires them for the time critical, volume and exclusive use levels.
  • special_instructions - printed on the BOL, or sent to the driver with a pickup.

Country codes can be the two letter US, CA, MX or the three letter USA, CAN, MEX; each API gets the form it wants. Phone numbers can carry any punctuation; Estes gets the ten digits.

Billing#

By default the account in account_number is the shipper, paying prepaid. A billing entry of type transportation changes who pays:

"billing": [
  {
    "type": "transportation",
    "payment_type": "third_party",
    "account": "THIRD_PARTY_ESTES_ACCOUNT",
    "company": "Pays For Freight LLC",
    "name": "Pat Payer",
    "phone": "2145550000",
    "addr1": "456 Other St",
    "city": "Dallas",
    "state": "TX",
    "postal_code": "75201",
    "country_code": "US"
  }
]
  • payment_type third_party prices the quote against that account and bills it as the third party, prepaid. On the BOL the entry's address is the bill-to party; on a pickup it is the third party.
  • payment_type recipient makes the consignee pay collect, against account (or your account_number).

The same billing works on getallrates, submitshipment and createpickup. Without an address on the entry, the BOL's bill-to party is the bill_* parameters (bill_company, bill_name, bill_addr1, bill_city, bill_state, bill_code, bill_country, bill_phone) when set, else the party that pays.

Rates#

{
  "carrier": "Estes",
  "action": "getallrates",
  "params": {
    "key": "YOUR_ESTES_API_KEY",
    "username": "YOUR_MYESTES_USERNAME",
    "password": "YOUR_MYESTES_PASSWORD",
    "account_number": "YOUR_ACCOUNT_NUMBER",
    "shipper": "RocketShipIt",
    "ship_contact": "John Doe",
    "ship_phone": "8705551234",
    "ship_email": "[email protected]",
    "ship_city": "Harrison",
    "ship_state": "AR",
    "ship_code": "72601",
    "ship_country": "US",
    "to_city": "Akron",
    "to_state": "OH",
    "to_code": "44333",
    "to_country": "US",
    "ship_date": "2026-12-15",
    "weight_unit": "LB",
    "length_unit": "IN",
    "accessorials": ["LGATE"],
    "packages": [
      { "weight": 200, "length": 48, "width": 40, "height": 36, "freight_class": "50", "packaging_type": "SKID", "description": "furniture" }
    ]
  }
}

The city, state, postal code and country of both ends are required for a quote. One rate comes back per service level Estes could price:

  • service_code - LTL, LTLTC, VTL, VTLTC, ERG or EU, derived from Estes' service level name, which is in desc ("Estes LTL Standard Transit"). guaranteed is true on the time critical and retail guarantee levels.
  • rate - Estes' total charges in USD.
  • rate_detail - the breakdown: one entry per line item by class (class_50), one per rated accessorial by its code (LGATE), and the other charge items by Estes' description ("Fuel Surcharge"). An accessorial Estes reports as "Included" has no amount and is left out.
  • est_delivery_days and est_delivery_time - Estes' transit days and the delivery date and time it expects.
  • rate_id - Estes' quote id. Pass it back as rate_id on submitshipment to price the BOL on that quote.
  • billing_weight - the rated weight, handling units and tare included.
  • package_type - Estes' rate type ("Customer Pricing").

A level Estes could not price is reported in errors with its name ("Volume LTL: ...") and left out of rates; when no level could be priced Estes answers HTTP 422 and every reason lands in errors. Entries with type Note are Estes' alerts (add dimensions...), not failures.

Transit time#

timeintransit asks Estes' route guide for the service days between ship_code and to_code (city, state and country help). One entry comes back with delivery_days, the origin and destination terminals in desc, and formatted_eta as the ship date plus that many weekdays. Estes does not count weekends; holidays are not accounted for, so treat the date as an estimate.

Creating a label and Bill of Lading#

submitshipment files the electronic BOL (the Digital LTL Council eBOL 2.0.1) and returns:

  • tracking_number and shipment_id - the PRO number Estes assigned.
  • packages[] - one entry per handling unit, each with the PRO as its tracking_number and Estes' shipment confirmation number under alternative_tracking_ids. The labels are one PDF holding every handling unit's label; it is on the first package's label and under documents as shipping_labels.
  • documents[] - the Bill of Lading (code bill_of_lading) as a PDF and the labels (code shipping_labels).

Estes produces labels as PDF only; image_type is ignored. label_stock_type picks the layout: left out or zebra gives one 4x6 label per page; avery (3-1/3 x 4, six per sheet), avery2 (8.5 x 5), avery4 (3.5 x 5) or letter a sheet of labels. One label per handling unit is requested.

Add reference_value (and reference_value2, reference_value3) for BOL numbers and po_number on the shipment or the packages for purchase orders; all print on the BOL. rate_id from getallrates is the quote the BOL is priced on. tracking_number on submitshipment is a PRO you pre-assigned (Estes' Reserve PRO program); leave it out and Estes assigns one. notify entries with an email get Estes' shipment movement emails.

Filing the BOL does not request the pickup; call createpickup with the PRO in shipment_id. Estes' warnings ("Invalid characters were removed") come back in errors with type Warning; the BOL is still filed.

Pickups#

createpickup requests a pickup at the pickup_* address (pickup_company_name, pickup_contact_name, pickup_phone, pickup_email, pickup_addr1, pickup_city, pickup_state, pickup_code, pickup_country), or at the ship_* address when those are blank, for freight going to the to_* address. Give the day as pickup_date, the window as ready_time and close_time (0800, 08:00 or 4:30 PM; Estes reads them as Eastern time; 0800 to 1700 when left out). List the handling units in packages, or just pickup_quantity and pickup_total_weight when you do not have them. Put the PRO from submitshipment in shipment_id to link the BOL to the pickup; reference_value and po_number go along as BOL and PO references. special_instructions goes to the driver. pickup_type is Estes' request action when the default live load (LL) is wrong: HL hook loaded, HE hook empty, SE spot empty, AP appointment.

The response has Estes' pickup request number in pickup_id (and in reference_numbers with code PUR). pickupstatus with that pickup_id returns the request's status (Accepted, Scheduled, Completed...), the shipper contact and the PROs picked up on it. cancelpickup with the pickup_id deletes the request; special_instructions is the cancel comment. Validation failures come back in errors with the field name as the code (pickupDate).

Parcel pickups work the same way on other carriers; see Schedule a Pickup.

Tracking#

track takes the PRO in tracking_number. To track by another reference, set reference_type to PO, BOL, PUR (pickup request number), LDN (load number), EXL (EXLA id) or INTERLINE (interline PRO) and put that value in tracking_number; a reference can match several PROs, and each comes back as one entry in packages. Estes tracks the shipment as a whole, so the events are in activity and repeated on the first package. Each event has Estes' status in status_code (Picked Up, In Transit, Out for Delivery, Delivered...), Estes' detail in description ("Arrived at destination terminal"), the terminal in location and a coarse status_type (PICKUP, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, APPOINTMENT, EXCEPTION). The shipment level fields give estimated_delivery (Estes' window), delivered_time, pickup_date, delivery_detail.received_by (who signed) and the reference numbers Estes holds (BOL, PO, pickup request, interline PROs). The shipper's and consignee's places are shown when the login is party to the shipment; otherwise origin and destination are Estes' terminals, and the charges are left out.

Documents#

gettrackingdocuments fetches images for the PRO in tracking_number. document_type names the document, or several comma separated: BOL (default), BOLSUP, DR (delivery receipt), DELIMG (delivery image), PUIMG (pickup image), INVOICE, WR and WNR (weight and research), LUMPER, LOA, CCI, B7512, CAMOTH, TCSUPP. Each document comes back under documents with Estes' content type (application/pdf, image/tiff...), the base64 data in media and the image URL and creation time in metadata. Only the latest version is fetched. A consignee who is not party to the account can pass to_code (the consignee ZIP) to see the party-to documents. Invoices and some other types are only shown to the payer.

Accessorials#

Estes' APIs use different code lists. accessorials takes the list for the action, and the rating codes are translated for submitshipment (and the other way round) so a rated payload ships unchanged.

Service getallrates, createpickup submitshipment
Lift gate at delivery / pickup LGATE / LGATEP LFTD / LFTP
Residential delivery / pickup HD / HPU RES / REP
Inside delivery / pickup INS / INP IDL / IPU
Appointment at delivery APT APTD
Notify before delivery NCM MNC
Protect from freezing FRZ PSC
Single shipment SSM SS
Construction site CONST / CONSTP CONST / CONSTP
Limited access PRISDL, SCHLDL, FARMDL, ... LTDAD / LTDAP (need the access type)
Guaranteed service use service LTLTC GTD_AM, GTD_NOON, GTD_PM, TCS
Full value coverage insured_value FVC, added with insured_value
Hazardous material HAZ, added with hazmat HAZ, added with hazmat

Old Dominion's codes (HYD, RDC, IDC...) are translated too, so a payload written for od-rest rates and ships on Estes. The full lists are in Estes' API developer portal.

Errors#

Estes errors land in data.errors with Estes' code and message, prefixed with the service level they apply to on rates. HTTP 401 means the login or the key was rejected; a rejected login is reported as "Estes authentication failed: Failed authentication: Invalid credentials". The BOL API reports failures in its messageStatus block; they are mapped to errors with Estes' codes (EBG0010 an invalid BOL date, and so on), one entry per message. Pickup validation failures carry the field name as the code.