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.
- The
carrier_codeandservice_codeof 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-keyheader. See Security & Authentication.
POST /v2/delivery_predictions
Predict delivery dates for one or more carrier and service combinations on a lane.
- Productionhttps://api.shipstation.com/v2/delivery_predictions
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
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"
}
}
]
}'{ "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:
| Property | Description |
|---|---|
ship_date | The ship date the prediction was calculated for — the one you sent, or today if you sent none. |
delivery_date | The predicted delivery date. |
confidence_level | How much historical data the prediction rests on. See Confidence levels. |
carrier_details | The carrier_code and service_code the prediction belongs to. |
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.
| Value | Meaning |
|---|---|
high | The prediction is based on historical data for this carrier service. |
low | Little or no historical data was available for this service, so the prediction is based on similar services. Treat it as a weaker signal. |
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."
}
]
}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.
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.
| Status | When it occurs | error_code |
|---|---|---|
400 Bad Request | A 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 Request | ship_date is in the past. | invalid_field_value |
400 Bad Request | A country code or postal code cannot be resolved to a location. | invalid_field_value |
500 Internal Server Error | An unexpected error occurred while processing the request. | varies |
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:
| Where | Endpoints |
|---|---|
| Rates | POST /v2/rates, POST /v2/rates/estimate, GET /v2/rates/{rate_id}, GET /v2/shipments/{shipment_id}/rates |
| Checkout Rates | POST /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. |
| Labels | POST /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, andcarrier_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.