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 inweight_unit(LBorKG).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,BAGand the other FedEx sub packaging types. Rates default toPALLETand labels toSKID.pieces- how many pieces are on the handling unit (default 1).length,width,heightinlength_unit(INorCM); 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_PRIORITYorFEDEX_FREIGHT_ECONOMY. Leave it out ofgetallratesto rate both.lift_gate_required- lift gate at delivery (LIFTGATE_DELIVERY).addons(oraccessorials) - other special services, for exampleINSIDE_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 inrateinstead of the list rate. Both are always returned: the list rate inrateand the account rate innegotiated_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 owntracking_number(the first is the master, the rest are child numbers that reference it) and its handling-unit address label inlabel, in theimage_typeyou asked for (PDF,PNG,ZPLII; thermal formats uselabel_stock_typeSTOCK_4X6).documents[]- the Bill of Lading (codeBILL_OF_LADING), always PDF. Every handling unit ships on this one BOL.charges,billing_weightandbilling_weight_unitsfrom 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:
"carrier": "FedEx-Freight".- New credentials:
client_idandclient_secretfrom the FedEx Freight Developer Portal, not the developer.fedex.com ones.keytokens are not interchangeable either. freight_account_numberbecomesaccount_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.