Sample codes > UPS live rates
UPS live rates
This example shows how to integrate UPS live carrier rates into JsRates using the UPS Rating API.
It demonstrates how to:
- Authenticate with UPS using OAuth 2.0 (
UPS_CLIENT_ID/UPS_CLIENT_SECRET) - Read product dimensions and weight from Shopify metafields via
enrichItemDetails - Build a UPS RateRequest with packages, weights, and addresses
- Request negotiated UPS rates and time-in-transit
- Convert UPS service codes (e.g.
01,02,03,07,65) into Shopify shipping rates - Return delivery dates (
min_delivery_date/max_delivery_date) for checkout
This sample is designed for stores that want real-time UPS pricing instead of flat-rate or rule-based shipping.
Prerequisites
1) Required UPS credentials (JsRates Secrets)
Create the following secrets in Settings → Secrets (see more information here):
UPS_CLIENT_IDUPS_CLIENT_SECRETUPS_SHIPPER_NUMBER(also known as shipper/account number)
See here on how to create UPS credentials: How to Generate UPS Credentials
2) Product dimensions metafields
This sample reads item dimensions from the custom variant metafield namespace (first 10 values). Make sure to use the correct namespace defined in your store.
Example keys per item (as strings or numbers):
lengthwidthheight
Note: This sample converts item weight from
DATA.items[*].gramsto pounds for UPS.
UPS API Reference
UPS live rates in this example are fetched using the UPS Rating API.
Official UPS API documentation:
UPS Rating API Reference
https://developer.ups.com/tag/Rating?loc=en_US#section/Reference
This documentation explains:
- Service codes (Ground, 2nd Day Air, Next Day Air, etc)
- Request / response schema
- Rate, negotiated rate, and time-in-transit fields
- Error handling and validation rules
If you need to support additional UPS services or fields (Saturday delivery, residential flags, etc), refer to the UPS API documentation above and update the request payload accordingly.
Sample code
Copy the following code and paste it to a blank calculateShippingRates.js module and save it.
import { enrichItemDetails } from "./modules.js"; // Enriches item data with metafields like dimensions
// 🔐 Generate a UPS OAuth token using Basic Authorization headers
async function getToken(env) {
const formData = {
grant_type: "client_credentials"
};
const url = "https://onlinetools.ups.com/security/v1/oauth/token";
const resp = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"x-merchant-id": env.UPS_SHIPPER_NUMBER, // Required UPS field
"Authorization": "Basic " + btoa(`${env.UPS_CLIENT_ID}:${env.UPS_CLIENT_SECRET}`) // Base64(client_id:client_secret)
},
body: new URLSearchParams(formData).toString()
});
const data = await resp.json();
return data.access_token;
}
// 🕒 Add business days (skipping weekends)
function addBusinessDays(numDays) {
const currentDate = new Date();
let daysAdded = 0;
while (daysAdded < numDays) {
currentDate.setDate(currentDate.getDate() + 1);
const day = currentDate.getDay();
if (day !== 0 && day !== 6) daysAdded++; // Skip Sunday (0) and Saturday (6)
}
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
const day = String(currentDate.getDate()).padStart(2, "0");
return `${year}${month}${day}`; // Format: YYYYMMDD
}
// 📆 Format UPS date/time into Shopify ISO format
function formatToShopifyMaxDeliveryDate(date, time) {
const year = date.substring(0, 4);
const month = date.substring(4, 6);
const day = date.substring(6, 8);
return `${year}-${month}-${day}`; // YYYY-MM-DD (Shopify-friendly)
}
// 🚚 Main shipping rate calculator
export async function calculateShippingRates(DATA, env) {
try {
// 1. Enrich item dimensions via metafields
DATA = await enrichItemDetails(DATA, [{
namespace: "custom",
size: 10
}]);
const token = await getToken(env); // 2. Get UPS OAuth token
console.log("token",token);
const url = `https://onlinetools.ups.com/api/rating/v2409/shop?additionalinfo=timeintransit`;
const destination = DATA.destination;
const origin = DATA.origin;
// 3. Prepare box/package data for each item
const boxes = DATA.items.map(item => {
const dims = item.metafields?.custom || {};
const weightLbs = (item?.grams || 100) / 453.592; // Convert grams to pounds
return {
totalWeightInPounds: weightLbs,
dimensions: {
length: Number(dims.length || 10),
width: Number(dims.width || 10),
height: Number(dims.height || 10)
}
};
});
// 4. Calculate total shipment weight
const totalWeight = boxes.reduce((sum, b) => sum + b.totalWeightInPounds, 0);
const ShipmentTotalWeight = {
UnitOfMeasurement: {
Code: "LBS",
Description: "Pounds"
},
Weight: totalWeight.toFixed(2)
};
// 5. Construct package list for UPS API
const packages = boxes.map(box => ({
PackagingType: {
Code: "02",
Description: "Package"
},
PackageWeight: {
UnitOfMeasurement: {
Code: "LBS",
Description: "Pounds"
},
Weight: box.totalWeightInPounds.toFixed(2)
},
Dimensions: {
UnitOfMeasurement: {
Code: "IN",
Description: "Inches"
},
Length: box.dimensions.length.toString(),
Width: box.dimensions.width.toString(),
Height: box.dimensions.height.toString()
}
}));
const pickupDate = addBusinessDays(2); // Assume 2 business days prep time
// 6. Construct UPS API request payload
const requestBody = {
RateRequest: {
Request: {
TransactionReference: {
CustomerContext: "CustomerContext"
}
},
PickupType: {
Code: "01", // Daily pickup
Description: "Daily Pickup"
},
CustomerClassification: {
Code: "00", // Rates Associated with Shipper Number
Description: "Rates Associated with Shipper Number"
},
Shipment: {
Shipper: {
Name: "Store",
ShipperNumber: env.UPS_SHIPPER_NUMBER,
Address: {
AddressLine: [origin.address1],
City: origin.city,
StateProvinceCode: origin.province,
PostalCode: origin.postal_code.replace(/\s/g, ""),
CountryCode: origin.country
}
},
ShipTo: {
Name: "ShipToName",
Address: {
AddressLine: [destination.address1],
City: destination.city,
StateProvinceCode: destination.province,
PostalCode: destination.postal_code.replace(/\s/g, ""),
CountryCode: destination.country
}
},
ShipFrom: {
Name: "Store",
Address: {
AddressLine: [origin.address1],
City: origin.city,
StateProvinceCode: origin.province,
PostalCode: origin.postal_code.replace(/\s/g, ""),
CountryCode: origin.country
}
},
PaymentDetails: {
ShipmentCharge: [{
Type: "01", // Bill to Shipper
BillShipper: {
AccountNumber: env.UPS_SHIPPER_NUMBER
}
}]
},
ShipmentRatingOptions: {
TPFCNegotiatedRatesIndicator: "Y",
NegotiatedRatesIndicator: "Y"
},
ShipmentTotalWeight,
NumOfPieces: boxes.length.toString(),
Package: packages,
RatingMethodRequestedIndicator: "Y",
DeliveryTimeInformation: {
PackageBillType: "03", // Non-Document
Pickup: {
Date: pickupDate
}
}
}
}
};
console.log("requestBody",requestBody);
// 7. Fetch UPS rates with transit info
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
transId: "transactionId123", // Arbitrary transaction ID
transactionSrc: "Shopify", // Source ID for tracking
Authorization: `Bearer ${token}`
},
body: JSON.stringify(requestBody)
});
const data = await response.json();
console.log("response data",data);
const ups_service_codes = ["03","01","02","07","54","65","11","08"]; // Added international codes
const service_names = ["UPS® Ground","UPS Next Day Air®","UPS 2nd Day Air®","UPS Worldwide Express®","UPS Worldwide Express Plus®","UPS Worldwide Saver®","UPS Standard®","UPS Worldwide Expedited®"];
const shopify_service_codes = ["UPS_Ground","UPS_Next_Day_Air","UPS_2nd_Day_Air","UPS_Worldwide_Express","UPS_Worldwide_Express_Plus","UPS_Worldwide_Saver","UPS_Standard","UPS_Worldwide_Expedited"];
const shipments = data?.RateResponse?.RatedShipment || [];
const filtered= shipments.filter(shipment => ups_service_codes.includes(shipment.Service.Code) );
// 8. Transform into Shopify rate objects
const rates = filtered.map(svc => {
const ups_code = svc?.Service?.Code;
const code_index = ups_service_codes.indexOf(ups_code);
const arrival = svc?.TimeInTransit?.ServiceSummary?.EstimatedArrival?.Arrival || {};
const deliveryDate = arrival.Date && arrival.Time ? formatToShopifyMaxDeliveryDate(arrival.Date, arrival.Time) : "";
let description = "";
const arrivalDays = svc?.GuaranteedDelivery || svc?.EstimatedArrival;
if (arrivalDays?.BusinessDaysInTransit) {
description += `Estimated to arrive by ${arrivalDays.BusinessDaysInTransit} business days`;
}
return {
service_name: service_names[code_index],
service_code: shopify_service_codes[code_index],
total_price: ( parseFloat(svc?.NegotiatedRateCharges?.TotalCharge?.MonetaryValue) * 100 ).toFixed(0),
currency: DATA.currency,
description,
min_delivery_date: deliveryDate,
max_delivery_date: deliveryDate
};
});
return {
rates
};
} catch (error) {
console.error("UPS Rate Error:", error.message);
return {
rates: []
};
}
}
Notes
-
Transit times:
additionalinfo=timeintransitis requested; if UPS does not return arrival info for a service, delivery dates will be empty for that rate. -
Units: UPS rate requests use LBS for weight and IN for dimensions in this example.
UPS Service Codes (Common)
| Code | Service name |
|---|---|
01 |
UPS Next Day Air |
02 |
UPS 2nd Day Air |
03 |
UPS Ground |
07 |
UPS Worldwide Express |
08 |
UPS Worldwide Expedited |
11 |
UPS Standard (Canada / Europe ground) |
12 |
UPS 3 Day Select |
13 |
UPS Next Day Air Saver |
14 |
UPS Next Day Air Early A.M. |
54 |
UPS Worldwide Express Plus |
59 |
UPS 2nd Day Air A.M. |
65 |
UPS Worldwide Saver |
82 |
UPS Today Standard |
83 |
UPS Today Dedicated Courier |
84 |
UPS Today Intercity |
85 |
UPS Today Express |
86 |
UPS Today Express Saver |
Note
UPS does not guarantee that every service code will be returned for every shipment.
The services returned depend on:
- Your UPS account & shipper number
- Origin & destination countries
- Package dimensions & weight
- Negotiated rates & enabled services
