FreightCake
Walkthroughs

Rate shop an LTL shipment

Quote a freight shipment across every carrier in your network, compare landed cost, and pick the winner.

This walkthrough takes a single shipment request — origin, destination, items — and returns a ranked list of carrier quotes. By the end you’ll be able to:

  • Submit a multi-item LTL rate request
  • Read the response envelope and per-carrier quote shape
  • Filter by service level, transit time, and accessorials
  • Pick a winning quote ID to feed into Create a shipment

The full request

import { fc } from './freightcake'

const result = await fc.quotes.create({
  origin: {
    city: 'Chicago',
    state: 'IL',
    zip: '60601',
  },
  destination: {
    city: 'Los Angeles',
    state: 'CA',
    zip: '90001',
  },
  items: [
    {
      description: 'Pallet of widgets',
      weight: 500, // lbs
      freight_class: '70',
      length: 48, // in
      width: 40,
      height: 48,
      quantity: 1,
    },
  ],
  accessorials: {
    destination_liftgate: true,
    residential_delivery: true,
  },
})

console.log(`${result.data.length} carriers quoted`)
for (const quote of result.data) {
  console.log(
    `${quote.carrier_name} — $${(quote.net_charge_cents / 100).toFixed(2)} — ${quote.delivery_estimate}`,
  )
}

Equivalent raw fetch

const res = await fetch('https://api.freightcake.com/api/v1/quotes', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.FREIGHTCAKE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    origin: { city: 'Chicago', state: 'IL', zip: '60601' },
    destination: { city: 'Los Angeles', state: 'CA', zip: '90001' },
    items: [
      {
        weight: 500,
        freight_class: '70',
        length: 48,
        width: 40,
        height: 48,
      },
    ],
    accessorials: {
      destination_liftgate: true,
      residential_delivery: true,
    },
  }),
})

if (!res.ok) {
  throw new Error(`Quote failed: ${res.status} ${await res.text()}`)
}
const result = await res.json()

Reading the response

A successful response is a list envelope of quote objects:

{
  "object": "list",
  "url": "/api/v1/quotes",
  "data": [
    {
      "object": "quote",
      "id": 1042,
      "carrier_name": "Sandbox Freight Co",
      "carrier_scac": "SBFC",
      "service_level": "Standard LTL",
      "net_charge_cents": 17500,
      "delivery_estimate": "3-5 business days",
      "transit_days": 3,
      "expires_at": "2026-05-22T00:00:00.000Z"
    }
  ],
  "has_more": false
}

Notable fields:

  • net_charge_cents is the quoted amount in cents and includes the carrier charges returned for the request.
  • expires_at defaults to 5 days after creation. Re-quote anything older than its expiration before booking.
  • transit_days comes from the carrier transit estimate and excludes pickup day and weekends.

Picking a winner

The simplest strategy — cheapest landed cost — is one line:

const winner = result.data
  .slice()
  .sort((a, b) => a.net_charge_cents - b.net_charge_cents)[0]

For production traffic, weight by carrier scorecard data:

const winner = result.data
  .map((q) => ({
    quote: q,
    score:
      q.net_charge_cents +
      // Penalize slow transit by ~$10 per extra day.
      (q.transit_days ?? 5) * 1000,
  }))
  .sort((a, b) => a.score - b.score)[0].quote

Pass winner.id straight into the next step.

Common gotchas

  • Freight class is required for LTL. If you don’t know the class, use the freight class calculator before requesting rates.
  • Residential addresses change the carrier list. A residential delivery filters out carriers that only run commercial freight. Always set residential_delivery if applicable — surprise reclasses get expensive.
  • Quotes expire. Trying to book against an expired quote returns quote_expired. Re-quote and book in one user session.

Next steps

On this page