A rate request fans out to every eligible carrier connection and returns a rate quote: an immutable, retrievable, expiring snapshot of what each service costs for this exact shipment.

1. The rate_quote object

Rate fields that need explaining

2. Get live rates

Request

A cross-border request that rates FedEx with an indirect signature and UPS with its defaults:

Response

200 with a rate_quote. Rates are sorted by total_in_account_currency ascending (so mixed-currency quotes sort correctly), then by delivery.days. Partial failure is not failure. If at least one connection was attempted, the response is 200 and the failed connections appear in errors[]. If every connection fails, the response is still 200 with an empty rates[] and a populated errors[]. A non-2xx status is reserved for problems with the request itself (400), credentials (401, 403), or the implementer (5xx). This is the single most important difference from carrier-native APIs, where one carrier’s outage would otherwise take down checkout. The fan-out, with one carrier missing the timeout_ms deadline:

Streaming (optional capability rates.stream)

POST /v1/rates with Accept: text/event-stream returns Server-Sent Events as carriers respond:
This suits checkout pages that render the first rates in about 300 ms rather than waiting for the slowest carrier. The same fan-out over Server-Sent Events:

3. Retrieve a rate quote

Returns the quote, including after expiry, for audit. Rates are retained for at least 30 days. Expired rates can be read but not purchased.

4. Expiry and caching

  • expires_at is set by the implementer, no shorter than 15 minutes and SHOULD be 30 minutes. A carrier that publishes a shorter validity (Amazon Shipping’s requestToken) caps it.
  • Implementers MAY cache carrier responses. A cached rate MUST carry the cache age in messages[] (code: "rate_cached") and MUST NOT outlive the carrier’s own validity.
  • Rates are bound to their request. Purchasing rate_... buys exactly the contacts (including bill_to), billing, handling_units, order, customs declaration and rate.carrier_configs in the quote. To change anything, re-rate.
A quote’s life from rating to purchase:

5. Rate mismatch at purchase

Carriers sometimes re-price at label time (dimensional reweighs, surcharges recalculated at ship time). On purchase:
  • If the label’s actual total is equal or lower, the purchase succeeds and the shipment records the actual amount.
  • If it is higher by no more than purchase.max_increase (default 0), the purchase succeeds, and the shipment carries a rate_adjusted message.
  • Otherwise the implementer MUST void the just-created label and return 422 carrier_error / rate_mismatch with error.details[0] = { quoted, actual }. The client re-rates or raises max_increase.
The decision the implementer makes once the carrier returns the label’s actual total:

6. What rating MUST NOT do

  • It MUST NOT create a shipment, reserve a tracking number, or charge money.
  • It MUST NOT emit webhook events. Quotes are high-volume and noisy.
  • It MUST NOT require customs_declaration for domestic shipments, or dimensions for flat-rate packaging.
  • It MUST NOT return a rate for a service that cannot carry an item’s dangerous_goods.