Documentation
Quotes
Ask every courier enabled on your account what they would charge, concurrently, in one request.
/v1/quotes
Shipstack asks each enabled courier in parallel under a fixed latency
budget and merges the answers. Rates come back sorted cheapest first. A
courier that fails or times out is reported in failures,
it never fails the request.
Request
| Field | Type | Notes |
|---|---|---|
from | address | Required. Collection address. |
to | address | Required. The recipient, even when delivering to a pickup point. |
parcels | array | Required. At least one, at most 50. |
ship_date | string | RFC 3339. Defaults to today. |
currency | string | ISO 4217. Defaults to the account's currency. |
customs | object | Required for international consignments. |
options | object | Value-added services. Validated against carrier capabilities. |
deliver_to | object | A pickup point, when not delivering to the door. |
Address
| Field | Type | Notes |
|---|---|---|
name | string | Required. |
line1 | string | Required. |
city | string | Required. |
postcode | string | Required. Normalised for you. |
country | string | Required. ISO 3166-1 alpha-2. |
company, line2, line3, county | string | Optional. |
phone, email | string | Optional. Used by carriers for delivery notifications. |
residential | boolean | Affects pricing with some carriers. |
vat_number, eori | string | Optional. Used on international lanes. |
Parcel
| Field | Type | Notes |
|---|---|---|
weight_g | integer | Required. Grams, greater than zero. |
length_mm, width_mm, height_mm | integer | Required. Millimetres, all greater than zero. |
reference | string | Your own reference, printed on the label where supported. |
contents | array | Itemised contents. Required for customs. |
A domestic quote
curl -X POST https://api.shipstack.example/v1/quotes \
-H "Authorization: Bearer $SHIPSTACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": {
"name": "Acme Supplies", "line1": "1 Mill Lane",
"city": "Leeds", "postcode": "LS1 4DL", "country": "GB"
},
"to": {
"name": "Sam Reed", "line1": "44 Bright Street",
"city": "Bristol", "postcode": "BS1 5TR", "country": "GB",
"residential": true
},
"parcels": [
{ "weight_g": 1200, "length_mm": 300, "width_mm": 200,
"height_mm": 100, "reference": "ORDER-10432" }
],
"options": { "signature_required": true }
}'
Response
Each rate carries the carrier's own service_code, which is what
you quote back when buying the label. surcharges itemises what
makes up the total beyond the base rate.
{
"rates": [
{
"courier": "royalmail_clickanddrop",
"service_code": "TPS48",
"service_name": "Royal Mail Tracked 48 Signature",
"total": { "amount": 489, "currency": "GBP" },
"estimated_days": 2
},
{
"courier": "dpd",
"service_code": "1^12",
"service_name": "DPD Next Day by 12:00",
"total": { "amount": 899, "currency": "GBP" },
"estimated_days": 1,
"delivery_by": "2026-09-02T12:00:00Z",
"surcharges": [
{ "code": "FUEL", "name": "Fuel surcharge",
"amount": { "amount": 74, "currency": "GBP" } }
]
}
],
"failures": [
{ "courier": "ups", "reason": "upstream timeout" }
]
}
delivery_by appears only when the carrier gives a firm
commitment rather than an estimate. Express services do, economy services
usually do not.
Service options
Options are validated against what the chosen carrier actually supports before any network call, so an unsupported combination fails immediately rather than as an opaque carrier error.
| Option | Type | Notes |
|---|---|---|
signature_required | boolean | Someone must sign for the parcel. |
age_check | integer | Minimum recipient age, e.g. 18. |
insurance | money | Cover level for the consignment. |
saturday_delivery | boolean | Weekend delivery, where offered. |
cash_on_delivery | money | Amount to collect on delivery. |
safe_place | string | Where to leave the parcel if nobody is in. |
International and customs
Crossing a customs border requires a customs object and
itemised contents on every parcel. Without them the request is
rejected before it reaches a carrier.
{
"customs": {
"content_type": "merchandise",
"incoterm": "DAP",
"invoice_number": "INV-2026-0912",
"reason": "sale"
},
"parcels": [
{
"weight_g": 800, "length_mm": 250, "width_mm": 180, "height_mm": 60,
"contents": [
{
"description": "Cotton t-shirt",
"quantity": 2,
"value": { "amount": 1200, "currency": "GBP" },
"weight_g": 320,
"hs_code": "610910",
"origin_country": "GB",
"sku": "TS-01"
}
]
}
]
}
content_type is one of merchandise,
gift, sample, documents or
return. incoterm decides who pays duty:
DAP means the recipient pays, DDP means you do.
Pickup points
A locker or parcel shop is a destination, not an option.
Set deliver_to to route the parcel there, and keep
to as the recipient's own address, because carriers still use it for
notifications and returns.
{
"deliver_to": {
"id": "GB-12345",
"type": "locker",
"name": "Bright Street Locker"
}
}
Types are locker, shop, post_office
and depot. A carrier that does not serve the requested type,
or a parcel too large for the locker, is rejected before any network call.
Multi-piece consignments
Send several parcels in one parcels array to quote them as a
single consignment. Carriers that do not support multi-piece are skipped
rather than quoting a wrong price; see the
capability matrix for which do.