FedEx Freight API: LTL Rates, Bills of Lading, Pickups and Tracking

FedEx Freight became an independent less-than-truckload (LTL) carrier on June 1, 2026 and moved its APIs off developer.fedex.com onto its own FedEx Freight Developer Portal, with its own hosts, OAuth server and credentials. The fedex-freight carrier talks to those APIs. It rates and creates LTL shipments (FedEx Freight Priority and FedEx Freight Economy), returns the handling-unit labels and the Bill of Lading, schedules and cancels pickups and tracks PRO numbers.

The freight path built into fedex-rest (requests with a freight_account_number) calls the old FedEx Freight LTL endpoints on apis.fedex.com, which FedEx Freight is retiring along with FedEx Web Services. Switch those requests to this carrier; see Migrating from fedex-rest.

Credentials#

Create an integration on the FedEx Freight Developer Portal. It issues a Client ID and a Client Secret for the sandbox and, once the integration is validated, for production. Send them as:

Parameter FedEx Freight value
client_id Client ID of the integration
client_secret Client Secret of the integration
account_number Your FedEx Freight account number (the shipper account)

The same values can come from the environment as RS_FEDEX_FREIGHT_CLIENT_ID, RS_FEDEX_FREIGHT_CLIENT_SECRET and RS_FEDEX_FREIGHT_ACCOUNT_NUMBER.

RocketShipIt requests an OAuth token (client credentials grant, scope fxf-api) before each request. Tokens live for 60 minutes and cannot be refreshed, so to save a round trip call the authenticate action, cache data.access_token until data.expires_at, and pass it back as key. client_id is still required with key because every API call carries it in the x-client-id header. The secret and the token are redacted from meta.debug_information.

{
  "carrier": "FedEx-Freight",
  "action": "authenticate",
  "params": {
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "test": true
  }
}

Sandbox and production#

"test": true sends the request to the sandbox: the token comes from auth-qa.fedexfreight.com and the API call goes to api-preprod.ltl.tech through the fxfgw-preprod gateway. Without it the token comes from auth.fedexfreight.com and the call goes to api.ltl.tech through the fxfgw gateway. Sandbox and production credentials are different; the portal lists the sandbox test accounts (shipper and bill-to) for your integration.

Every FedEx Freight API is limited to 5 calls per second per integration.

Supported actions#

Action FedEx Freight endpoint
authenticate POST /am/oauth2/access_token on the OAuth host
getallrates POST /fxf-external-rate-auth0/{gateway}/rate/getRateQuote
submitshipment POST /fxf-external-ship-auth0/{gateway}/route/freight/shipment
createpickup POST /fxf-external-pickup-auth0/{gateway}/pickup/create
cancelpickup PUT /fxf-external-pickup-auth0/{gateway}/pickup/cancel
track POST /fxf-external-tracking-auth0/{gateway}/route/track/v1/trackingnumbers

Pickup availability, special service lookups, tracking by reference and tracking documents (BOL, proof of delivery, freight bill) are not supported yet.

Describing the freight#

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

  • weight - the handling unit's weight in weight_unit (LB or KG).
  • freight_class - the NMFC freight class as a number (50, 77.5, 100) or the FedEx enum (CLASS_050, CLASS_077_5, CLASS_100).
  • sub_packaging_type - PALLET, SKID, CRATE, DRUM, BOX, BUNDLE, CARTON, CASE, ROLL, BAG and the other FedEx sub packaging types. Rates default to PALLET and labels to SKID.
  • pieces - how many pieces are on the handling unit (default 1).
  • length, width, height in length_unit (IN or CM); optional for rates, recommended for labels and required for Freight Direct.
  • description - the commodity, printed on the Bill of Lading.
  • po_number - a purchase order number for the line item.

Shipment level:

  • service - FEDEX_FREIGHT_PRIORITY or FEDEX_FREIGHT_ECONOMY. Leave it out of getallrates to rate both.
  • lift_gate_required - lift gate at delivery (LIFTGATE_DELIVERY).
  • addons (or accessorials) - other special services, for example INSIDE_DELIVERY, LIMITED_ACCESS_DELIVERY, CALL_BEFORE_DELIVERY, DO_NOT_BREAK_DOWN_PALLETS, FREIGHT_GUARANTEE, LIFTGATE_PICKUP. See Parameters for the list.
  • residential - the delivery address is residential.
  • negotiated_rates - report your account rate in rate instead of the list rate. Both are always returned: the list rate in rate and the account rate in negotiated_rate.

Rates#

{
  "carrier": "FedEx-Freight",
  "action": "getallrates",
  "params": {
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "account_number": "YOUR_FREIGHT_ACCOUNT_NUMBER",
    "shipper": "RocketShipIt",
    "ship_addr1": "1202 Chalet Ln",
    "ship_city": "Harrison",
    "ship_state": "AR",
    "ship_code": "72601",
    "ship_country": "US",
    "to_addr1": "1234 Main St",
    "to_city": "Akron",
    "to_state": "OH",
    "to_code": "44333",
    "to_country": "US",
    "weight_unit": "LB",
    "length_unit": "IN",
    "packages": [
      { "weight": 200, "length": 48, "width": 40, "height": 36, "freight_class": "50", "sub_packaging_type": "PALLET", "pieces": 1, "description": "furniture" }
    ],
    "test": true
  }
}

Each rate has the service in service_code, the list rate in rate, your account rate in negotiated_rate, the delivery date in est_delivery_time and the transit days in est_delivery_days. Alerts FedEx Freight attaches to the quote are in errors with their type (NOTE, WARNING); only entries with type Error mean the request failed.

Creating a label and Bill of Lading#

submitshipment creates the shipment and returns:

  • tracking_number - the master PRO number, assigned to the first handling unit.
  • packages[] - one entry per handling unit with its own tracking_number (the first is the master, the rest are child numbers that reference it) and its handling-unit address label in label, in the image_type you asked for (PDF, PNG, ZPLII; thermal formats use label_stock_type STOCK_4X6).
  • documents[] - the Bill of Lading (code BILL_OF_LADING), always PDF. Every handling unit ships on this one BOL.
  • charges, billing_weight and billing_weight_units from the rate FedEx Freight quoted with the shipment.

Up to 40 handling units can be labelled in one request. Add reference_code and reference_value (PO_NUMBER, BILL_OF_LADING, SHIPPER_ID_NUMBER, CONSIGNEE_ID_NUMBER...) to print references on the BOL, and label.printed_origin to print a different origin on the labels for a blind shipment.

Billing#

By default the shipper's account pays and the billing address on the BOL is the shipper's. Override the billing address with the bill_* parameters (bill_company, bill_attention_name, bill_addr1, bill_city, bill_state, bill_code, bill_country, bill_phone).

To bill a third party, pass its account and address in billing:

"billing": [
  {
    "type": "transportation",
    "payment_type": "third_party",
    "account": "THIRD_PARTY_FREIGHT_ACCOUNT",
    "company": "Acme Inc.",
    "addr1": "2000 Freight LTL Testing",
    "city": "Harrison",
    "state": "AR",
    "postal_code": "72601",
    "country_code": "US"
  }
]

The third party becomes the payor and the alternate billing party on the BOL. The same billing entry works on getallrates to quote the third party's rates. The third-party account must be set up with FedEx Freight as a bill-to party for your shipper account; otherwise the shipment is rejected with error 653 ("Alternate billing account number contains invalid third-party reference"). The sandbox bill-to test account listed on the portal returned this error for the sandbox shipper account when this carrier was built.

Pickups#

createpickup schedules an LTL pickup at the pickup_* address. Give the day as pickup_date (YYYYMMDD), the time the freight is ready as ready_time (HHMM) and the dock close time as close_time (HHMM); the next business day and a 5 pm close are used when they are left out. List the handling units in packages and the destination in the to_* parameters. special_instructions goes to the driver. The response has the confirmation number in pickup_id and the servicing location in location.

cancelpickup takes that confirmation number as pickup_id and the day it was scheduled for as pickup_date (YYYY-MM-DD). Pickups cannot be cancelled once the driver has been dispatched.

Tracking#

track takes a master PRO number or any child handling-unit number in tracking_number. The sandbox has no tracking data for shipments created in the sandbox; those come back with TRACKING.TRACKINGNUMBER.NOTFOUND. Tracking the master returns every handling unit: each is an entry in packages with its sequence_number, weight and scan events (activity), and the shipment level fields (estimated_delivery, delivered_time, ship_date, origin, destination, reference_numbers, delivery_detail.received_by) come from the master. The status_type on each activity is the FedEx Freight status code (DL delivered, IT in transit, OD out for delivery, PU picked up...).

Migrating from fedex-rest#

Requests that used the freight path of fedex-rest need three changes:

  1. "carrier": "FedEx-Freight".
  2. New credentials: client_id and client_secret from the FedEx Freight Developer Portal, not the developer.fedex.com ones. key tokens are not interchangeable either.
  3. freight_account_number becomes account_number. The old name is still accepted as a synonym, so this change can wait.

The packages, billing, bill_*, addons, lift_gate_required, image_type and pickup parameters keep their meaning. Handling-unit labels and the BOL come back in the same places.

Errors#

FedEx Freight errors land in data.errors with the FedEx code and message. An expired or invalid token comes back as HTTP 401 with invalid_token; get a new one with authenticate. A rejected token request (wrong client id or secret, or production credentials against the sandbox) is reported as invalid_client. HTTP 429 means the 5 calls per second limit was exceeded.