Shipping Rates API
Manage the rates a site charges to ship an order. A site ships to two zones:
domestic, its default country, and international, every other country it
ships to. Each zone has its own rates, up to 10, such as "Ground" and "2-day
air". At checkout the buyer sees their zone's rates, each with its price and
estimated delivery dates, and the cheapest is chosen until they pick another.
An order with no physical items has no shipping and no choice.
A new site starts with one rate per zone called "Standard" (in the site's
language), so it can sell before you set up shipping.
Related resources:
- Sites for the countries a site ships to and its handling time
- Products and Product Variants
for weights - Orders for the rate an order shipped by
Endpoints
- List Shipping Rates -
GET /api/v1/sites/{site_id}/shipping-rates/ - Create Shipping Rate -
POST /api/v1/sites/{site_id}/shipping-rates/ - Get Shipping Rate -
GET /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/ - Update Shipping Rate -
PUT /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/ - Delete Shipping Rate -
DELETE /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/
Changing or deleting a rate never changes an order that already shipped by
it: an order keeps the rate's name and transit time as they were. A shopper's
cart picks up the change when they next load checkout, and a cart whose rate
was deleted gets the cheapest one left.
How a Rate Is Priced
base_price covers a parcel of up to 1 kg. Each further kilogram the parcel
starts, even by a gram, adds surcharge_percent of base_price, rounded to
the nearest minor unit, the way carriers bill by the started kilogram. A
surcharge_percent of 0 charges base_price whatever the weight.
The parcel weighs what its shipped items weigh: each variant's
weight_grams, or its product's when the variant sets none, times the
quantity. Items without a weight, digital products, and gift cards weigh
nothing.
When the order's items, after discounts and leaving out gift cards, cost
free_over or more, the rate ships free.
For a rate with a base_price of 500 and a surcharge_percent of 20:
| Parcel | Price |
|---|---|
| 500 g | 500 |
| 1 kg | 500 |
| 1.2 kg | 600 |
| 2 kg | 600 |
| 5 kg | 900 |
Shipping is taxed like the items wherever the store collects tax, so
choosing a rate quotes the order's tax again.
List Shipping Rates
Retrieve every shipping rate of a site, domestic first, each zone ordered by
position, then by when they were created. A site has at most 10 rates per
zone, so the list isn't paginated and has_more is always false.
Endpoint: GET /api/v1/sites/{site_id}/shipping-rates/
Permission Required: Read
Request
curl -X GET "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/" \
-H "Authorization: Bearer your_api_key_here"
Response
Status: 200 OK
{
"data": [
{
"id": "shr_d0b6fmv28q6vn14pesr1",
"zone": "domestic",
"name": "Standard",
"base_price": 500,
"surcharge_percent": 20,
"free_over": 7500,
"transit": {"min": 3, "max": 5},
"position": 0,
"created_at": "2023-01-01T10:00:00Z",
"updated_at": "2023-01-01T10:00:00Z"
},
{
"id": "shr_d0b6fmv28q6vn14pesr2",
"zone": "domestic",
"name": "2-day air",
"base_price": 1500,
"surcharge_percent": 20,
"free_over": null,
"transit": {"min": 1, "max": 2},
"position": 1,
"created_at": "2023-01-01T10:00:00Z",
"updated_at": "2023-01-01T10:00:00Z"
}
],
"has_more": false
}
Create Shipping Rate
Add a shipping rate to one of a site's zones.
Endpoint: POST /api/v1/sites/{site_id}/shipping-rates/
Permission Required: Write
Request Body
{
"zone": "domestic",
"name": "2-day air",
"base_price": 1500,
"surcharge_percent": 20,
"free_over": null,
"transit": {"min": 1, "max": 2},
"position": 1
}
| Field | Type | Required | Description |
|---|---|---|---|
zone |
string | Yes | domestic or international. It can't change later |
name |
string | Yes | 1 to 100 characters, unique within the zone |
base_price |
integer | No | Price for a parcel of up to 1 kg, in the store's smallest currency unit, zero or more. Defaults to 0 |
surcharge_percent |
integer | No | Share of base_price each started kilogram past the first adds, from 0 to 1000. Defaults to 20 |
free_over |
integer|null | No | Ships free when the items cost this much or more, after discounts and leaving out gift cards. Omit or null to never ship free |
transit |
object | Yes | Business days the carrier takes: an integer min and max, each from 0 to 60, with max at least min |
position |
integer | No | Sort order within the zone from 0 to 1000, lowest first. Leave it out to put the rate after the zone's last one |
Request
curl -X POST "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/" \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-12345" \
-d '{
"zone": "domestic",
"name": "2-day air",
"base_price": 1500,
"transit": {"min": 1, "max": 2}
}'
Response
Status: 201 Created
{
"id": "shr_d0b6fmv28q6vn14pesr2",
"zone": "domestic",
"name": "2-day air",
"base_price": 1500,
"surcharge_percent": 20,
"free_over": null,
"transit": {"min": 1, "max": 2},
"position": 1,
"created_at": "2023-01-01T10:00:00Z",
"updated_at": "2023-01-01T10:00:00Z"
}
Error Responses
Missing name
Status: 400 Bad Request
{
"error": "name is required",
"code": "MISSING_FIELD"
}
Invalid field, such as an unknown zone, a backward transit range, or a
negative price
Status: 400 Bad Request
{
"error": "Zone must be 'domestic' or 'international'",
"code": "INVALID_FIELD"
}
Zone full
Status: 400 Bad Request
{
"error": "A zone can have at most 10 shipping rates",
"code": "INVALID_FIELD"
}
Name taken in the zone
Status: 400 Bad Request
{
"error": "Another rate in the zone has this name",
"code": "INVALID_FIELD"
}
Get Shipping Rate
Retrieve one of a site's shipping rates.
Endpoint: GET /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/
Permission Required: Read
Request
curl -X GET "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/shr_d0b6fmv28q6vn14pesr2/" \
-H "Authorization: Bearer your_api_key_here"
Response
Status: 200 OK
Returns the Shipping Rate Object. A rate of another
site answers 404 Not Found.
Update Shipping Rate
Replace a shipping rate's fields. The body is the same as
Create Shipping Rate's, except that zone is
ignored: a rate stays in its zone. Fields you leave out take their defaults,
and leaving out position keeps the rate where it is.
Endpoint: PUT /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/
Permission Required: Write
Request
curl -X PUT "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/shr_d0b6fmv28q6vn14pesr2/" \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-67890" \
-d '{
"name": "2-day air",
"base_price": 1600,
"surcharge_percent": 25,
"transit": {"min": 1, "max": 2}
}'
Response
Status: 200 OK
Returns the updated Shipping Rate Object. Errors
are the same as Create Shipping Rate's.
Delete Shipping Rate
Delete a shipping rate. Orders that shipped by it keep its name and transit
time. If it was a zone's last rate, buyers in that zone can't check out
products that ship until you add another.
Endpoint: DELETE /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/
Permission Required: Write
Request
curl -X DELETE "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/shr_d0b6fmv28q6vn14pesr2/" \
-H "Authorization: Bearer your_api_key_here"
Response
Status: 204 No Content
Shipping Rate Object
| Field | Type | Description |
|---|---|---|
id |
string | Shipping rate public ID |
zone |
string | domestic, the site's default country, or international, every other country it ships to |
name |
string | Name buyers see at checkout, such as Ground |
base_price |
integer | Price for a parcel of up to 1 kg, in the store's smallest currency unit |
surcharge_percent |
integer | Share of base_price each started kilogram past the first adds. 0 charges base_price whatever the weight |
free_over |
integer|null | Items after discounts, leaving out gift cards, that cost this much or more ship free. null never ships free |
transit |
object | Business days the carrier takes, as min and max. With the site's handling time, it estimates delivery dates |
position |
integer | Sort order within the zone, lowest first |
created_at |
string | ISO 8601 timestamp |
updated_at |
string | ISO 8601 timestamp |