Documentation
Everything you need to integrate the rateship SDK into your Node backend.
Everything you need to integrate the rateship SDK into your Node backend.
v2 is a complete rewrite. v1 was an HTTP client for a hosted RateShip service; v2 talks to EasyPost / Shippo / ShipEngine directly from your backend. There's no hosted API anymore, so every v1 method has a new shape.
// v1
const rateship = new RateShip({ apiKey: "rs_live_..." });
// v2
import { RateShip, easypost, shippo, shipengine } from "rateship";
const client = new RateShip({
providers: [
easypost({ apiKey: process.env.EASYPOST_KEY! }),
shippo({ apiKey: process.env.SHIPPO_KEY! }),
shipengine({ apiKey: process.env.SHIPENGINE_KEY! }),
],
});// v1
const { rates, errors } = await rateship.rates.get({
from_address: { ..., phone: "..." },
to_address: { ..., phone: "..." },
weight: 2.5,
weight_unit: "lbs",
length: 12,
width: 8,
height: 6,
package_count: 1,
});
// v2
const { rates, errors } = await client.getRates({
from: { ... }, // renamed from from_address
to: { ... }, // renamed from to_address
parcel: { // weight + dims extracted into parcel
weight: 2.5,
weight_unit: "lb", // "lbs" -> "lb" (matches provider APIs)
length: 12,
width: 8,
height: 6,
distance_unit: "in", // NEW
},
// package_count removed, single parcel at v2.0 (multi-parcel is v2.1+)
});// v1, pass full shipment again, plus rate_id
const label = await rateship.labels.purchase({
provider: "shippo",
rate_id: "5e228fbf...",
carrier: "USPS",
service: "Ground Advantage",
price_cents: 965,
from_address: { ... },
to_address: { ... },
weight: 2.5,
weight_unit: "lbs",
length: 10,
width: 8,
height: 4,
package_count: 1,
});
// v2, pass the NormalizedRate back, SDK reads provider identifiers from rate.raw
const label = await client.createLabel(rates[0]);v1 had hosted webhook registration (rateship.webhooks.create / list / delete / update). v2 drops all of that, there's no hosted service to register against. Register webhooks directly in each provider's dashboard, receive them on a URL you own, and use client.webhooks.verify(...) to validate the HMAC signature and normalize the payload.
phone is now optional (it was required in v1). Adapters throw VALIDATION_ERROR if a specific carrier needs it.country is now required and locked to the literal "US" at the type level. v2.0.0 is US-domestic only; widening in v2.1+ is additive and won't break v2.0 code.street2 and email are new optional fields.RateShipError.status (HTTP status) is removed, use .code instead.RateShipError: .provider and .cause.WebhookVerificationError for HMAC mismatches.ProviderError.error field (inside getRates().errors[]) renamed to .message for consistency with the thrown class.rateship.labels.list() , no hosted label history. Persist labels yourself if you need a history view.rateship.webhooks.create / list / delete / update , no hosted webhook registration. Use provider dashboards.Back to the Getting Started guide.
v2 is a complete rewrite. v1 was an HTTP client for a hosted RateShip service; v2 talks to EasyPost / Shippo / ShipEngine directly from your backend. There's no hosted API anymore, so every v1 method has a new shape.
// v1
const rateship = new RateShip({ apiKey: "rs_live_..." });
// v2
import { RateShip, easypost, shippo, shipengine } from "rateship";
const client = new RateShip({
providers: [
easypost({ apiKey: process.env.EASYPOST_KEY! }),
shippo({ apiKey: process.env.SHIPPO_KEY! }),
shipengine({ apiKey: process.env.SHIPENGINE_KEY! }),
],
});// v1
const { rates, errors } = await rateship.rates.get({
from_address: { ..., phone: "..." },
to_address: { ..., phone: "..." },
weight: 2.5,
weight_unit: "lbs",
length: 12,
width: 8,
height: 6,
package_count: 1,
});
// v2
const { rates, errors } = await client.getRates({
from: { ... }, // renamed from from_address
to: { ... }, // renamed from to_address
parcel: { // weight + dims extracted into parcel
weight: 2.5,
weight_unit: "lb", // "lbs" -> "lb" (matches provider APIs)
length: 12,
width: 8,
height: 6,
distance_unit: "in", // NEW
},
// package_count removed, single parcel at v2.0 (multi-parcel is v2.1+)
});// v1, pass full shipment again, plus rate_id
const label = await rateship.labels.purchase({
provider: "shippo",
rate_id: "5e228fbf...",
carrier: "USPS",
service: "Ground Advantage",
price_cents: 965,
from_address: { ... },
to_address: { ... },
weight: 2.5,
weight_unit: "lbs",
length: 10,
width: 8,
height: 4,
package_count: 1,
});
// v2, pass the NormalizedRate back, SDK reads provider identifiers from rate.raw
const label = await client.createLabel(rates[0]);v1 had hosted webhook registration (rateship.webhooks.create / list / delete / update). v2 drops all of that, there's no hosted service to register against. Register webhooks directly in each provider's dashboard, receive them on a URL you own, and use client.webhooks.verify(...) to validate the HMAC signature and normalize the payload.
phone is now optional (it was required in v1). Adapters throw VALIDATION_ERROR if a specific carrier needs it.country is now required and locked to the literal "US" at the type level. v2.0.0 is US-domestic only; widening in v2.1+ is additive and won't break v2.0 code.street2 and email are new optional fields.RateShipError.status (HTTP status) is removed, use .code instead.RateShipError: .provider and .cause.WebhookVerificationError for HMAC mismatches.ProviderError.error field (inside getRates().errors[]) renamed to .message for consistency with the thrown class.rateship.labels.list() , no hosted label history. Persist labels yourself if you need a history view.rateship.webhooks.create / list / delete / update , no hosted webhook registration. Use provider dashboards.Back to the Getting Started guide.