Time in Transit API for UPS, FedEx, USPS and DHL

A time in transit request asks a carrier when a shipment sent today (or on a date you choose) from one postal code to another would arrive, for each service it offers on that lane. It returns delivery dates, not prices. Use it for an "arrives by Thursday" promise at checkout, to pick the cheapest service that still makes a deadline, or to check a lane before you promise anything.

In RocketShipIt every carrier takes the same request. The action is timeintransit, the origin goes in ship_*, and the destination goes in to_*:

{
  "carrier": "UPS-REST",
  "action": "timeintransit",
  "params": {
    "key": "your-key-from-authenticate-request",
    "ship_city": "Whitehall",
    "ship_state": "MT",
    "ship_code": "59759",
    "ship_country": "US",
    "to_code": "90210",
    "to_country": "US",
    "pickup_date": "2026-01-15"
  }
}

Every carrier returns the same response shape: one entry per service, each with its delivery date.

{
  "data": {
    "errors": [],
    "services": [
      {
        "desc": "UPS Next Day Air Early",
        "service_code": "1DM",
        "is_guaranteed": true,
        "eta": 1768550400,
        "formatted_eta": "Fri January 16 by 08:00 am",
        "delivery_days": 1
      }
    ]
  }
}

The fields are listed under TimeInTransit. The raw carrier reply is in transactions[].response, the same as for every other action.

Do you need a separate call?#

Often you don't. If you are rating anyway, the rates already carry transit times for the major carriers. UPS-REST rates with request_option set to Shoptimeintransit by default, and FedEx-REST and DHL-REST return their estimates with each rate, so delivery_days and est_delivery_time arrive on every rate in one call (see Rating).

A separate timeintransit call is worth it when:

  • you want dates without prices, for example on a product page before there is a cart to rate
  • you want USPS delivery standards, which USPS serves from a separate API
  • you want UPS's guarantee and Saturday delivery flags per service without rating

One catch when you mix the two: UPS time-in-transit service codes are not rate service codes. Time in transit returns service level codes such as 1DM, 2DA and GND, while rating uses 14, 03. If you join the two responses, map them yourself, or take the transit time from the rate response and skip the mapping.

Carrier support#

Carrier carrier value Needs Returns
UPS UPS-REST origin and destination postal code and country; city for international date and time, business days, guarantee, Saturday delivery
FedEx FedEx-REST origin and destination postal code and country date, transit days
USPS USPS-REST origin and destination ZIP code date and time per mail class
DHL Express DHL origin and destination postal code and country date and time per product

The legacy UPS, FedEx and USPS (Web Tools) carriers also implement timeintransit, but those carrier APIs are being retired. See carrier API retirement dates before you build on them. DHL-REST and OnTrac-REST have no separate call; their estimates come back with each rate.

Choosing the ship date#

The ship date is the day the carrier gets the package, not the day the order is placed. It moves every estimate, so set it once you know it.

  • UPS-REST reads pickup_date in YYYY-MM-DD format and defaults to today. It does not read ship_date, and it passes pickup_date to UPS unchanged, so 20260115 is sent as it is rather than converted. UPS accepts dates up to 60 days ahead and 35 days back.
  • FedEx-REST reads pickup_date or ship_date, in YYYY-MM-DD, YYYYMMDD or RFC3339 format, and defaults to today.
  • USPS-REST sends no date. The estimate is for mail entered today.
  • DHL reads pickup_date (YYYY-MM-DD) and ready_time (HHMM). The ready time defaults to one hour from now, and DHL rejects dates more than about 10 days ahead.

UPS#

UPS returns the most detail: the service description, the UPS service level code, delivery date and time, delivery_days (business days in transit), is_guaranteed, and saturday_delivery when the service delivers on Saturday on that lane.

Things to know:

  • UPS filters its own list. By default it drops US domestic services that are slower than UPS Ground on the lane. If Ground arrives in two days, you won't see 3 Day Select. This is the same rule that hides services on rate requests.
  • International needs a city on both ends. If you leave to_city empty, RocketShipIt fills it in from its US ZIP and Canadian postal code tables. It never fills in ship_city, and it can't look up destinations outside the US or Canada. Send both cities.
  • Weight and contents value. RocketShipIt sends weight with weight_unit (LBS or KGS, up to 150 lb / 70 kg), defaulting to 10.5 LBS. It also sends a contents value from monetary_value, falling back to insured_value and then to 10.5, in currency (default USD). UPS only uses the value on international lanes, where it is required.
  • Every request is one non-document package. RocketShipIt always sends bill type 03 (non-document) with one package, whatever you pass in documents_only or num_of_packages.
  • Peak-season guarantees. When UPS suspends its service guarantees (for example, in December), the raw response carries guaranteeSuspended: true. RocketShipIt reports is_guaranteed for each service but doesn't surface this flag, so read it from transactions[].response if your promise depends on the guarantee.
  • An empty list with no error can mean UPS couldn't resolve the address and returned a candidate list instead. Check the raw response for destinationPickList or originPickList, then resend with the city and postal code from a candidate.

FedEx#

FedEx-REST calls FedEx's Retrieve Services and Transit Times API for FedEx Ground, FedEx Express and SmartPost together. Each service returns a FedEx service type (FEDEX_GROUND, PRIORITY_OVERNIGHT, SMART_POST and so on) as service_code, a readable desc, the commit date, and delivery_days, which is the upper end of FedEx's transit range. formatted_eta is a date only, such as Fri January 16, with no time.

Only the postal codes and countries go to FedEx; weight is fixed at 10 lb and isn't read from weight. For a quote that accounts for the real package, take the transit time from a getallrates call instead: FedEx rates return delivery_days and est_delivery_time for each service.

USPS#

USPS-REST calls the USPS Service Standards API with two ZIP codes and nothing else, so it is domestic only. It returns every mail class USPS has a standard for, including letters, flats and Media Mail, so filter the list to the classes you actually ship. service_code is the USPS mail class (PRIORITY_MAIL, USPS_GROUND_ADVANTAGE and so on), desc is the same name in title case, and formatted_eta is the scheduled delivery date and time as USPS writes it (2026-01-17T18:00:00). A mail class can appear more than once.

Two fields are only in the raw reply in transactions[].response:

  • delivery_days isn't set. USPS's own count is serviceStandard ("2", with serviceStandardMessage "2-Day").
  • is_guaranteed is always false, even for Priority Mail Express, which USPS does guarantee. Read delivery.guaranteedDelivery if you need it.
{
  "carrier": "USPS-REST",
  "action": "timeintransit",
  "params": {
    "key": "your-key-from-authenticate-request",
    "ship_code": "59759",
    "to_code": "90210"
  }
}

DHL Express#

The DHL carrier uses DHL Express's capability check with your username (site ID) and password. RocketShipIt marks the shipment dutiable when the countries differ; send customs_value and currency for international lanes. weight defaults to 5 LB. Each product comes back with DHL's local product name and code and a delivery date and time. delivery_days isn't set.

{
  "carrier": "DHL",
  "action": "timeintransit",
  "params": {
    "username": "your-site-id",
    "password": "your-password",
    "ship_code": "90210",
    "ship_country": "US",
    "to_code": "M1B5K7",
    "to_country": "CA",
    "pickup_date": "2026-01-15",
    "ready_time": "0900",
    "customs_value": 250,
    "currency": "USD"
  }
}

Reading the response#

  • eta is the carrier's local delivery time labelled as UTC. The carrier sends a wall-clock date and time with no zone, and RocketShipIt reads it as UTC. To display it, format eta in UTC (or use formatted_eta). Converting it to the destination's time zone shifts the delivery time by that zone's offset.
  • An unparseable date is not zero. If UPS, FedEx or DHL returns a service without a date RocketShipIt can read, eta is -62135596800 (year 1) and formatted_eta reads Mon January 1. Treat any eta at or below zero as unknown.
  • Business days, not calendar days. delivery_days from UPS and FedEx counts business days, so a two-day service shipped on Friday arrives on Tuesday.
  • Check errors before services. Carrier rejections, such as a bad postal code or a date outside the allowed window, come back in errors. An empty services list with no errors is the UPS candidate-list case above.

API Examples#