Get instant shipping rate quotes before creating an order. Calculate delivery costs based on service level, package dimensions, weight, and destination. Perfect for displaying shipping options to your customers.
The POST /v1/rates/quote endpoint calculates delivery rates without creating an order.
Use this to show customers shipping options and prices before checkout.
Returns rate quote with price, estimated delivery date, and transit time. Requires HMAC authentication.
The rate quote request requires the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
service_level |
string | Yes | One of: standard, express, same_day |
pickup |
object | Yes | Pickup location (name, phone, email, address, time_window) |
dropoff |
object | Yes | Delivery location (name, phone, email, address, time_window) |
parcels |
array | Yes | Array of parcels with dimensions, weight, and value |
partner_order_id or idempotency_key for rate quotes.
import hmac
import hashlib
import time
import json
import requests
def get_rate_quote():
# Configuration
public_key = "pk_test_pivohub"
secret_key = "sk_test_secret_pivohub"
base_url = "http://localhost:5000"
# Quote request data
quote_data = {
"service_level": "standard",
"pickup": {
"name": "Warehouse Montreal",
"phone": "+1-514-555-0001",
"email": "warehouse@company.com",
"address": {
"line1": "123 Main Street",
"city": "Montréal",
"province": "QC",
"postal_code": "H1A1A1",
"country": "CA"
},
"time_window": {
"start": "2025-11-08T09:00:00-05:00",
"end": "2025-11-08T12:00:00-05:00"
}
},
"dropoff": {
"name": "John Doe",
"phone": "+1-438-555-0002",
"email": "john.doe@customer.com",
"address": {
"line1": "789 Oak Avenue",
"city": "Montréal",
"province": "QC",
"postal_code": "H2L1P1",
"country": "CA"
},
"time_window": {
"start": "2025-11-08T13:00:00-05:00",
"end": "2025-11-08T17:00:00-05:00"
}
},
"parcels": [
{
"parcel_id": "PKG-001",
"description": "Electronics - Laptop",
"length_cm": 40,
"width_cm": 30,
"height_cm": 25,
"weight_kg": 5.5,
"value_cents": 150000,
"fragile": True
}
]
}
# Prepare request
method = "POST"
path = "/v1/rates/quote"
body = json.dumps(quote_data, separators=(',', ':'))
timestamp = str(int(time.time()))
# Generate signature
string_to_sign = f"{timestamp}\n{method}\n{path}\n{body}"
signature = hmac.new(
secret_key.encode('utf-8'),
string_to_sign.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Make request
headers = {
'Content-Type': 'application/json',
'X-Api-Key': public_key,
'X-Timestamp': timestamp,
'X-Signature': f'v1={signature}'
}
response = requests.post(
f"{base_url}{path}",
headers=headers,
data=body
)
print(f"Status: {response.status_code}")
print(f"Response: {json.dumps(response.json(), indent=2)}")
return response.json()
if __name__ == "__main__":
get_rate_quote()
const crypto = require('crypto');
const axios = require('axios');
async function getRateQuote() {
// Configuration
const publicKey = 'pk_test_pivohub';
const secretKey = 'sk_test_secret_pivohub';
const baseUrl = 'http://localhost:5000';
// Quote request data
const quoteData = {
service_level: 'standard',
pickup: {
name: 'Warehouse Montreal',
phone: '+1-514-555-0001',
email: 'warehouse@company.com',
address: {
line1: '123 Main Street',
city: 'Montréal',
province: 'QC',
postal_code: 'H1A1A1',
country: 'CA'
},
time_window: {
start: '2025-11-08T09:00:00-05:00',
end: '2025-11-08T12:00:00-05:00'
}
},
dropoff: {
name: 'John Doe',
phone: '+1-438-555-0002',
email: 'john.doe@customer.com',
address: {
line1: '789 Oak Avenue',
city: 'Montréal',
province: 'QC',
postal_code: 'H2L1P1',
country: 'CA'
},
time_window: {
start: '2025-11-08T13:00:00-05:00',
end: '2025-11-08T17:00:00-05:00'
}
},
parcels: [{
parcel_id: 'PKG-001',
description: 'Electronics - Laptop',
length_cm: 40,
width_cm: 30,
height_cm: 25,
weight_kg: 5.5,
value_cents: 150000,
fragile: true
}]
};
// Prepare request
const method = 'POST';
const path = '/v1/rates/quote';
const body = JSON.stringify(quoteData);
const timestamp = Math.floor(Date.now() / 1000).toString();
// Generate signature
const stringToSign = `${timestamp}\n${method}\n${path}\n${body}`;
const signature = crypto
.createHmac('sha256', secretKey)
.update(stringToSign)
.digest('hex');
// Make request
const headers = {
'Content-Type': 'application/json',
'X-Api-Key': publicKey,
'X-Timestamp': timestamp,
'X-Signature': `v1=${signature}`
};
try {
const response = await axios.post(
`${baseUrl}${path}`,
quoteData,
{ headers }
);
console.log('Status:', response.status);
console.log('Rate Quote:', JSON.stringify(response.data, null, 2));
return response.data;
} catch (error) {
console.error('Error:', error.response?.data || error.message);
throw error;
}
}
getRateQuote();
#!/bin/bash
# Configuration
PUBLIC_KEY="pk_test_pivohub"
SECRET_KEY="sk_test_secret_pivohub"
BASE_URL="http://localhost:5000"
TIMESTAMP=$(date +%s)
# Quote request data
QUOTE_DATA='{
"service_level": "standard",
"pickup": {
"name": "Warehouse Montreal",
"phone": "+1-514-555-0001",
"email": "warehouse@company.com",
"address": {
"line1": "123 Main Street",
"city": "Montréal",
"province": "QC",
"postal_code": "H1A1A1",
"country": "CA"
},
"time_window": {
"start": "2025-11-08T09:00:00-05:00",
"end": "2025-11-08T12:00:00-05:00"
}
},
"dropoff": {
"name": "John Doe",
"phone": "+1-438-555-0002",
"email": "john.doe@customer.com",
"address": {
"line1": "789 Oak Avenue",
"city": "Montréal",
"province": "QC",
"postal_code": "H2L1P1",
"country": "CA"
},
"time_window": {
"start": "2025-11-08T13:00:00-05:00",
"end": "2025-11-08T17:00:00-05:00"
}
},
"parcels": [{
"parcel_id": "PKG-001",
"description": "Electronics - Laptop",
"length_cm": 40,
"width_cm": 30,
"height_cm": 25,
"weight_kg": 5.5,
"value_cents": 150000,
"fragile": true
}]
}'
# Generate signature
STRING_TO_SIGN="${TIMESTAMP}\nPOST\n/v1/rates/quote\n${QUOTE_DATA}"
SIGNATURE=$(echo -n "$STRING_TO_SIGN" | openssl dgst -sha256 -hmac "$SECRET_KEY" | sed 's/^.* //')
SIGNATURE_HEADER="v1=${SIGNATURE}"
# Make request
curl -X POST "${BASE_URL}/v1/rates/quote" \
-H "Content-Type: application/json" \
-H "X-Api-Key: ${PUBLIC_KEY}" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Signature: ${SIGNATURE_HEADER}" \
-d "${QUOTE_DATA}"
On success (200 OK), you'll receive the rate quote:
{
"service_level": "standard",
"rate_cents": 2500,
"currency": "CAD",
"estimated_delivery": "2025-11-11T17:00:00Z",
"transit_days": 3
}
| Field | Type | Description |
|---|---|---|
service_level |
string | The service level requested (standard, express, same_day) |
rate_cents |
integer | Delivery rate in cents (e.g., 2500 = $25.00 CAD) |
currency |
string | Currency code (CAD or USD) |
estimated_delivery |
string | Estimated delivery date/time (ISO 8601 format) |
transit_days |
integer | Number of business days for delivery |
| Service Level | Transit Time | Use Case |
|---|---|---|
standard |
3-5 business days | Regular deliveries, cost-effective |
express |
1-2 business days | Faster delivery, higher priority |
same_day |
Same day | Urgent deliveries, premium pricing |
import asyncio
import aiohttp
async def get_all_quotes(pickup, dropoff, parcels):
"""Get rate quotes for all service levels"""
service_levels = ['standard', 'express', 'same_day']
quotes = {}
async with aiohttp.ClientSession() as session:
tasks = []
for level in service_levels:
task = get_quote_async(session, level, pickup, dropoff, parcels)
tasks.append(task)
results = await asyncio.gather(*tasks)
for level, quote in zip(service_levels, results):
quotes[level] = quote
return quotes
# Display to customer:
# Standard: $25.00 - 3 days
# Express: $37.50 - 1 day
# Same Day: $50.00 - Today
POST /v1/orders to create the orderorder_id to track delivery status