ShipPlus Open API
Multi-carrier shipping aggregation. Integrate once to rate, label, and track across UniUni, Stallion, Canada Post, UPS and more.
Authentication
ShipPlus issues a keyID + secretToken for your account (billed at your contract rate). Pass the secretToken in a header on every request — any of the following works:
X-API-Key: <secretToken>
X-Secret-Token: <secretToken>
Authorization: Bearer <secretToken>
Authorization: Basic base64(keyID:secretToken)The secretToken is sensitive — keep it safe. If leaked, contact ShipPlus to revoke and reissue.
Errors
Non-2xx responses use a consistent shape:
{ "error": { "code": "invalid_request", "message": "..." } }unauthorized (401) · invalid_request (400) · invalid_rate_id (400) · rating_failed (502) · label_failed (502) · not_found (404) · account_suspended (402) · credit_limit_exceeded (402) · insufficient_balance (402) · request_in_progress (409) · idempotency_key_reused (422)
1. Rates
Get available carrier services and prices (sorted ascending).
Request
{
"from": { "city": "Toronto", "province": "ON", "postal_code": "M5V3A8", "country": "CA" },
"to": { "city": "Burnaby", "province": "BC", "postal_code": "V5H4M1", "country": "CA" },
"parcels": [
{ "weight": 1.0, "weight_unit": "kg",
"length": 20, "width": 15, "height": 10, "dimension_unit": "cm" }
]
}weight_unit: kg/g/lb/oz (default kg) · dimension_unit: cm/in (default cm) · include city + province for full carrier coverage (some carriers e.g. UniUni require the destination city/province)
Response
{
"rates": [
{
"rate_id": "eyJwIjoic3RhbGxpb24i...",
"carrier": "Stallion",
"carrier_code": "stallion",
"service": "fleet-optics-ground",
"service_name": "Fleet Optics Ground",
"price": 14.48,
"currency": "CAD",
"transit_days": 4
}
]
}rate_id is an opaque token — pass it back verbatim when creating a shipment (short-lived; re-quote if expired).
2. Create shipment
Create a shipment and buy a label using a rate_id. from / to require full addresses.
Strongly recommended: send an Idempotency-Key header (a unique string, e.g. a UUID). Retrying with the same key after a timeout is safe — the same key buys a label and bills only once, and returns the original result.
Headers
X-API-Key: <your secretToken>
Idempotency-Key: 3f9a1c2e-... (recommended)Request
{
"from": { "name": "Warehouse A", "phone": "6470000000", "street1": "50 Wellesley St E",
"city": "Toronto", "province": "ON", "postal_code": "M5V3A8", "country": "CA" },
"to": { "name": "Jane Li", "phone": "6041234567", "street1": "4700 Kingsway",
"city": "Burnaby", "province": "BC", "postal_code": "V5H4M1", "country": "CA" },
"parcels": [ { "weight": 1.0, "weight_unit": "kg", "length": 20, "width": 15, "height": 10 } ],
"rate_id": "eyJwIjoic3RhbGxpb24i..."
}Response (201)
{
"shipment": {
"id": "SP...",
"tracking_number": "...",
"carrier": "stallion",
"service": "fleet-optics-ground",
"label_url": "https://.../label.pdf",
"cost": 14.48,
"currency": "CAD",
"status": "created"
}
}3. Tracking
Response
{
"tracking_number": "...",
"status": "in_transit",
"events": [
{ "status": "in_transit", "timestamp": "2026-07-07T10:00:00Z",
"location": "Toronto, ON", "description": "..." }
]
}4. Account & usage
View your billing account: credit limit / balance, status, and this period's shipment count and amount. If credit is exhausted or the account is suspended, the create-shipment call returns 402.
{
"account": {
"company_name": "...", "billing_mode": "postpaid",
"currency": "CAD", "status": "active",
"credit_limit": 500.00, "current_due": 42.15, "balance": 0
},
"current_period": { "period": "2026-07", "shipments": 6, "amount": 42.15 }
}List billed line items for a period (defaults to the current month).
{
"period": "2026-07", "count": 6, "total": 42.15, "currency": "CAD",
"records": [
{ "shipment_id": "...", "carrier": "uniuni", "service": "uniuni-standard",
"amount": 7.83, "currency": "CAD", "invoiced": false,
"created_at": "2026-07-14T15:00:00Z" }
]
}Billing: postpaid accrues against a credit limit and is invoiced monthly; prepaid is deducted from your balance in real time.
Integration flow
- 1Call /api/v1/rates, pick a rate_id
- 2Call /api/v1/shipments with rate_id to get tracking_number + label_url
- 3Print the label and ship
- 4Poll /api/v1/tracking/{tracking_number}