FedEx Freight LTL rates, without the nine weeks
FedEx Freight became its own company in June 2026 and took LTL rating with it. Most of what is written about the FedEx rate API — including pages still on developer.fedex.com — now describes the wrong company. Here is what actually works, from an integration that quotes live.
- Auth
- OAuth 2.0 client credentials (Ping AIC)
- Format
- REST / JSON
- Access
- Self-serve sandbox · production needs an account
- In Oread
- Live — connect and rate
FedEx Freight is not FedEx
On 1 June 2026 FedEx Freight Holding Company span out as an independent public company. LTL went with it. This is the one fact everything else follows from, and getting it wrong costs days.
| FedEx Corp (parcel) | FedEx Freight | |
|---|---|---|
| Developer portal | developer.fedex.com | developer.fedexfreight.com |
| Identity | FedEx OAuth, /oauth/token | Ping AIC, /am/oauth2/access_token |
| API host | apis.fedex.com | api.ltl.tech |
| Rates LTL? | No | Yes |
developer.fedex.com still publishes Freight LTL documentation and still routes /rate/v1/freight/rates/quotes. It answers 403 FORBIDDEN.ERROR rather than 404, which reads like a permissions problem and is not one. Those pages are stamped “Version V0, last updated January 16, 2021” and are legacy left behind by the business that left.
www.fedexfreight.com is a third thing again — the customer portal, with no API section. Its own quoting tool calls api.ltl.tech with a token tied to a browser session, which is no use from a server.
What you need
- A registration at developer.fedexfreight.com. Separate from both
developer.fedex.comand the customer portal; existing logins do not carry over, and it is an approval queue rather than an instant signup. - A client id and secret, issued per project. Regenerating them changes the client id and is safe, but it fixes nothing — if calls are failing, the credentials are rarely why.
- A shipper account number, sent as the
x-account-noheader. Rates are account-specific, so this is what makes a quote yours rather than base tariff.
Sandbox accounts are provisioned with the project — 510087020 shipper, 510051408 bill-to, both at the Harrison, Arkansas address that appears in the spec’s own sample payload. Your production account does not exist in sandbox.
Getting a production freight account is the slow part: roughly nine weeks in our case, including a screening call. Ask explicitly for a standard freight-enabled account — FedEx’s own documentation notes that “LTL Freight only” and “Bill to LTL Freight” accounts cannot be added to the developer portal at all, and finding that out afterwards means opening a second account.
Authentication
OAuth 2.0 client credentials, against Ping Advanced Identity Cloud rather than anything FedEx-branded.
POST {base}/am/oauth2/access_token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=…
&client_secret=…
&scope=fxf-apiSandbox is https://auth-qa.fedexfreight.com, production https://auth.fedexfreight.com.
scope=fxf-api is required. Omit it and the request is refused while looking structurally correct — no field is missing, nothing is malformed, and the error does not name the scope. It is the hardest kind of failure to diagnose and the easiest to avoid.
One more: the documentation says tokens live 900 seconds. The observed expires_in is 3599. Read the value off the response rather than trusting the prose.
The endpoint, and the segment that moves
sandbox POST https://api-preprod.ltl.tech/fxf-external-rate-auth0/fxfgw-preprod/rate/getRateQuote
production POST https://api.ltl.tech/fxf-external-rate-auth0/fxfgw/rate/getRateQuoteThe gateway segment is environment-suffixed: fxfgw-preprod in sandbox, plain fxfgw in production. Nothing else in the URL hints at it. The production path is the one you see if you watch the website’s own network traffic, so copying it into sandbox silently 404s. Fourteen probes failed on exactly this before the pattern was visible.
The -auth0 segment is in the path in both environments despite the Ping token. It is not a hint that another auth flavour exists, and there is no sibling route for client-credentials callers.
Beyond Authorization: Bearer, send Content-Type: application/json and x-account-no. The website also sends x-locale and x-version; requests succeed without them.
Four traps worth knowing before you start
- A 403 here means the wrong path, not the wrong permissions. The production path returned
403 {"error":"Invalid Client"}for hours, then began returning 404 with an empty body, with nothing changing on our side. Read as an entitlement problem it sends you to account association, project setup and credential regeneration — all dead ends. Confirm the URL first. - A valid token proves nothing about routing. Auth worked for hours while no endpoint was reachable. Verify the two separately or you will conflate them.
- The published OpenAPI spec is the other company’s. Its paths 404 on this gateway, and its response schema is wrong in three places:
ratedShipmentDetailsis an array, not an object;freightTransitLocationDetailis an object, not an array; andcommithas properties the spec omits entirely. The two container types are backwards in opposite directions, so code generated from the spec compiles cleanly and fails on first contact. Type from real responses. - Sandbox only rates from its own address. Any origin other than the sandbox shipper’s registered one is refused with
400 SHIPPER.ADDRESS.MISMATCH. The message names the shipper but reads like a bad-address problem, sending you to the address you supplied rather than the account behind it. Destination is unconstrained. It means a realistic multi-origin file cannot be exercised end to end against sandbox.
One thing that will bite you in the data
serviceType is not a unique key. A single response returned six quotes across two service types, differing only by commit.guaranteedType — GUARANTEED_MORNING, absent, and GUARANTEED_CLOSE_OF_BUSINESS. Key a quote on carrier, service level and guarantee together, or several distinct offerings collapse into one and your customer sees identical-looking rows at different prices.
Money arrives as JSON floats, e.g. 2608.31. Round to integer cents rather than truncating: 2608.31 * 100 is 260830.999… in binary floating point, and truncation loses a cent on a surprising share of quotes.