Skip to content
Last updated

Delivery Predictions

Delivery Predictions tells you when a shipment is likely to arrive, for any combination of carrier and service you ask about. The prediction comes from ShipStation's delivery prediction model, which is built on historical transit data, rather than from the transit time the carrier quotes.

The endpoint answers a question about a lane — one origin and one destination. You send that lane once, list every carrier and service combination you want predicted for it, and get one prediction per combination.

A prediction is not a rate, not a shipment, and not a commitment: use it to set delivery expectations, and use Rates when you need a price.

Requirements

  • The carrier_code and service_code of each combination you want predicted. These are the same service codes you use when rate shopping and creating labels.
  • The country and postal code of both ends of the lane.
  • All requests are authenticated with your API key in the api-key header. See Security & Authentication.

Predict Delivery Dates

POST /v2/delivery_predictions

Predict delivery dates for one or more carrier and service combinations on a lane.

Example Request & Response

curl -i -X POST \
  https://api.shipstation.com/v2/delivery_predictions \
  -H 'Content-Type: application/json' \
  -H 'api-key: YOUR_API_KEY_HERE' \
  -d '{
    "from_country_code": "US",
    "from_postal_code": "78701",
    "to_country_code": "US",
    "to_postal_code": "90210",
    "ship_date": "2026-07-01",
    "delivery_options": [
      {
        "carrier_details": {
          "carrier_code": "ups",
          "service_code": "ups_ground"
        }
      },
      {
        "carrier_details": {
          "carrier_code": "fedex",
          "service_code": "fedex_express_saver"
        }
      }
    ]
  }'
Response
application/json
{ "delivery_predictions": [ { … } ], "warnings": [ { … } ] }

ship_date is optional. When you omit it, today's date is used.

There is no cap you need to manage: if you ask about more combinations than the model accepts in a single call, the request is split up automatically and every combination is still answered for.

Each prediction in the response includes:

PropertyDescription
ship_dateThe ship date the prediction was calculated for — the one you sent, or today if you sent none.
delivery_dateThe predicted delivery date.
confidence_levelHow much historical data the prediction rests on. See Confidence levels.
carrier_detailsThe carrier_code and service_code the prediction belongs to.

Matching Predictions to What You Asked For

We don't guarantee the order predictions come back in, and they aren't echoed back verbatim — each one carries the carrier and service exactly as the model reports it. Match a prediction to a request entry on the carrier_code and service_code pair in carrier_details, not on position.

Listing the same combination twice does not produce two predictions; it is predicted once.

Confidence Levels

ValueMeaning
highThe prediction is based on historical data for this carrier service.
lowLittle or no historical data was available for this service, so the prediction is based on similar services. Treat it as a weaker signal.

Warnings

A combination you ask about is never in both arrays. One the model has no coverage for produces an entry in warnings instead of a prediction:

{
  "delivery_predictions": [],
  "warnings": [
    {
      "carrier_details": {
        "carrier_code": "fedex",
        "service_code": "fedex_express_saver"
      },
      "message": "Carrier and service combination is unsupported."
    }
  ]
}
No coverage is not an error

A response where none of the requested combinations is supported is still a 200 OK, with an empty delivery_predictions array and one warning per combination. Check delivery_predictions for content rather than relying on the status code alone.

Error Handling

Errors are returned with the standard error response body, which includes a request_id and an errors array. Each error carries an error_type, error_code, and message; validation errors also include field_name and field_value.

StatusWhen it occurserror_code
400 Bad RequestA required field is missing — from_country_code, from_postal_code, to_country_code, to_postal_code, or delivery_options — or an entry in delivery_options is missing its carrier_code or service_code.invalid_field_value
400 Bad Requestship_date is in the past.invalid_field_value
400 Bad RequestA country code or postal code cannot be resolved to a location.invalid_field_value
500 Internal Server ErrorAn unexpected error occurred while processing the request.varies

Delivery Predictions and Carrier Estimates

The Rates endpoints return the carrier's own transit estimates — delivery_days, estimated_delivery_date, and carrier_delivery_days. Those come from the carrier's published service commitments.

A delivery prediction is a different kind of answer to a similar question: it is derived from how the service has actually performed on comparable lanes. The two can disagree, and neither replaces the other. Rates are the place to go when you need the price alongside the estimate; this endpoint is the place to go when you want the prediction on its own, without quoting a shipment.

The same prediction is also attached to Rates, Checkout Rates and Labels responses as a delivery_prediction object, so you do not need a second call to get it alongside a rate or a purchased label:

WhereEndpoints
RatesPOST /v2/rates, POST /v2/rates/estimate, GET /v2/rates/{rate_id}, GET /v2/shipments/{shipment_id}/rates
Checkout RatesPOST /v2/checkout_rates/{config_id}/quotes, GET /v2/checkout_rates/quotes/{quote_id}. Calculating rates returns a prediction only when the request includes a ship_from address.
LabelsPOST /v2/labels and its variants (by shipment, rate or shipping rule), POST /v2/labels/{label_id}/return, GET /v2/labels, GET /v2/labels/{label_id}, GET /v2/labels/external_shipment_id/{external_shipment_id}

That embedded object carries delivery_date and confidence_level only — the carrier and service are already the context of the rate or label it hangs off, so they are not repeated. One difference from this endpoint's own response is worth knowing:

  • The object is omitted entirely when no prediction is available, rather than being reported as a warning. Labels responses have no other delivery estimate, so there is nothing to fall back on there; Rates responses still carry the carrier's own delivery_days, estimated_delivery_date, and carrier_delivery_days, which the prediction complements rather than replaces.

POST /v2/rates/bulk does not carry a prediction: its response contains no rates, only the identifiers to retrieve them with.