Cotizaciones
Compara tarifas de todas las paqueterías en una sola llamada; el total cotizado es exactamente lo que pagas.
Las cotizaciones llegan solas: al crear un envío, la respuesta ya incluye rates[] con las tarifas de todas las paqueterías habilitadas para esa ruta. No hay una llamada separada de cotización que administrar.
Si ya sabes qué paquetería y servicio quieres, o solo quieres la más barata, agrega el objeto purchase al crear el envío. Ver Compra en una llamada.
El objeto tarifa
{
"id": "DHL_standard_a1b2c3",
"carrierCode": "DHL",
"serviceCode": "EXPRESS_WORLDWIDE",
"carrierName": "DHL Express",
"serviceName": "DHL Express Nacional",
"serviceLevel": "standard",
"totalPrice": 326.82,
"currency": "MXN",
"isInsured": false,
"estimatedDays": { "min": 1, "max": 2 },
"expiresAt": "2026-07-18T14:30:00.000Z",
"breakdown": {
"baseRate": 264.50,
"fuelSurcharge": 17.24,
"insuranceCost": 0.00,
"subtotal": 281.74,
"ivaRate": 0.16,
"ivaAmount": 45.08,
"total": 326.82
}
}
| Campo | Descripción |
|---|---|
id |
El rateId — pásalo a la compra de guía |
serviceCode |
Código nativo exacto del producto de la paquetería |
serviceLevel |
Nivel normalizado entre paqueterías (express, standard, economy, …) |
totalPrice |
El total a pagar, IVA incluido — coincide con breakdown.total |
isInsured |
true si la tarifa incluye seguro (creaste el envío con requestInsurance) |
estimatedDays |
Rango de días hábiles estimados de entrega |
expiresAt |
Vigencia de la tarifa (24 horas) |
breakdown |
Desglose transparente: tarifa base, sobrecargos, seguro, subtotal e IVA por separado |
Si creaste el envío con requestInsurance: true y un declaredValue, las tarifas regresan con la prima en breakdown.insuranceCost e isInsured: true. Comprar una tarifa asegurada crea una póliza real que puedes reclamar. Ver Seguro y reclamaciones.
El ciclo de vida de una cotización
POST /v1/shipments
→ crea el envío (DRAFT)
→ cotiza con todas las paqueterías habilitadas
→ devuelve el envío + rates[] (vigencia: 24 h)
↓
GET /v1/shipments/:id/rates ← consulta o refresca cuando quieras
↓
POST /v1/shipments/:id/label { rateId } ← compra al precio cotizado
Si las paqueterías tardan
La cotización tiene un presupuesto de 8 segundos. Si alguna paquetería es lenta, el envío se devuelve de inmediato con las tarifas en camino:
{
"success": true,
"data": {
"id": "clxq1w2e3r4t5y6u7i8o9p0a",
"status": "DRAFT",
"rates": [],
"ratesStatus": "pending",
"ratesExpiresAt": null,
"ratesPollUrl": "/v1/shipments/clxq1w2e3r4t5y6u7i8o9p0a/rates"
}
}
Consulta ratesPollUrl (por ejemplo cada 2 segundos) hasta que ratesStatus sea "ready".
Consulta o refresca las tarifas
GET /v1/shipments/:id/rates ← devuelve las tarifas vigentes
GET /v1/shipments/:id/rates?refresh=true ← recotiza con las paqueterías
Sin ?refresh=true obtienes las tarifas en caché mientras su vigencia de 24 horas no haya vencido. Con ?refresh=true se recotiza todo y la vigencia se reinicia.
Cuando una tarifa expira
Comprar con un rateId vencido devuelve 410 RATES_EXPIRED:
{
"success": false,
"error": {
"code": "RATES_EXPIRED",
"message": "Shipping rates have expired. Please refresh rates and select again.",
"details": {
"shipmentId": "clxq1w2e3r4t5y6u7i8o9p0a",
"hint": "GET /v1/shipments/clxq1w2e3r4t5y6u7i8o9p0a/rates"
}
}
}
Refresca, elige un nuevo rateId y vuelve a comprar. También verás este error si usas un rateId que pertenece a otro envío.
Cotiza sin crear un envío
Para widgets de checkout o estimaciones previas, usa el endpoint independiente:
Parámetros del cuerpo
originobjeto
El origen. Requiere al menos { postalCode }.
objetodestinationobjeto
El destino. Requiere al menos { postalCode }.
objetoparcelobjeto
El paquete: length, width, height (cm) y weight (kg).
objetocurl -X POST https://api.sendit.mx/v1/rates \
-H "X-API-Key: sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"origin": { "postalCode": "03940" },
"destination": { "postalCode": "45050" },
"parcel": { "weight": 1.5, "length": 20, "width": 15, "height": 10 }
}'
La respuesta trae rates[] (el mismo objeto tarifa de arriba) y su vigencia:
{
"success": true,
"data": {
"rates": [
{ "id": "DHL_standard_a1b2c3", "carrierCode": "DHL", "serviceCode": "EXPRESS_WORLDWIDE", "serviceLevel": "standard", "totalPrice": 88.40, "currency": "MXN", "isInsured": false }
],
"expiresAt": "2026-07-18T14:30:00.000Z"
}
}
Estas tarifas son solo informativas: no se pueden usar para comprar una guía. Para comprar, crea primero el envío. Puedes limitar a una paquetería (POST /v1/rates/carrier/DHL) u ordenar con ?sortBy=price o ?sortBy=speed.
Ordena las tarifas
const porPrecio = [...rates].sort((a, b) => a.totalPrice - b.totalPrice);
const porVelocidad = [...rates].sort(
(a, b) => a.estimatedDays.min - b.estimatedDays.min || a.totalPrice - b.totalPrice
);