FedEx FreightFedEx Freight

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 portaldeveloper.fedex.comdeveloper.fedexfreight.com
IdentityFedEx OAuth, /oauth/tokenPing AIC, /am/oauth2/access_token
API hostapis.fedex.comapi.ltl.tech
Rates LTL?NoYes

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.com and 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-no header. 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-api

Sandbox 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/getRateQuote

The 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

  1. 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.
  2. 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.
  3. The published OpenAPI spec is the other company’s. Its paths 404 on this gateway, and its response schema is wrong in three places: ratedShipmentDetails is an array, not an object; freightTransitLocationDetail is an object, not an array; and commit has 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.
  4. 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.

← All carriers