@pzd060/speedy-sdk
v0.1.0
Published
TypeScript SDK for the Speedy Bulgaria courier API (api.speedy.bg). Zero runtime dependencies, dual ESM/CJS, fully typed.
Maintainers
Readme
@pzd060/speedy-sdk
A small, typed TypeScript SDK for Speedy — the largest courier in Bulgaria — covering the public REST API at https://api.speedy.bg/v1.
- Zero runtime dependencies (uses
globalThis.fetch, Node ≥ 18) - Dual ESM / CJS, full
.d.ts - Maps cleanly to Speedy's URL paths:
client.contract,client.location,client.calculate(...) - Throws
SpeedyApiErrorwith Speedy's full error envelope (context,id,code)
Status: v0.1.0 covers contract, location, and calculate endpoints — enough to validate addresses and quote a shipment. Shipment creation, label printing, cancellation, and tracking land in 0.2.x.
Install
pnpm add @pzd060/speedy-sdk
# or
npm i @pzd060/speedy-sdkAuthentication
Speedy doesn't issue API keys. Every request carries a userName + password in the JSON body — these are the same credentials your team uses on services.speedy.bg. Get a test account by emailing [email protected].
Usage
import { SpeedyClient } from "@pzd060/speedy-sdk"
const speedy = new SpeedyClient({
username: process.env.SPEEDY_USERNAME!,
password: process.env.SPEEDY_PASSWORD!,
language: "EN", // optional, default "EN"
})
// 1. Validate the credentials and list your contract clients
const clients = await speedy.contract.list()
const senderClientId = clients[0].clientId
// 2. Resolve a destination (Sofia, Кпинка street)
const [sofia] = await speedy.location.findSites({ countryId: 100, name: "София" })
const [street] = await speedy.location.findStreets({ siteId: sofia.id, name: "Къпинка" })
// 3. Or list Sofia offices for office-pickup mode
const offices = await speedy.location.findOffices({ siteId: sofia.id })
// 4. Quote a shipment
const quote = await speedy.calculate({
sender: { clientId: senderClientId, dropoffOfficeId: 55 }, // drop at Yambol office
recipient: {
privatePerson: true,
addressLocation: { siteId: sofia.id, streetId: street.id, streetNo: "20А" },
},
service: { serviceIds: [505], autoAdjustPickupDate: true },
content: { parcelsCount: 1, totalWeight: 1.0, contents: "books", package: "BOX" },
payment: { courierServicePayer: "RECIPIENT" },
})
console.log(quote.calculations[0].price.total) // e.g. 4.99 EURError handling
import { SpeedyClient, SpeedyApiError } from "@pzd060/speedy-sdk"
try {
await speedy.calculate({ /* … missing required fields … */ } as any)
} catch (err) {
if (err instanceof SpeedyApiError) {
console.error(err.code, err.context, err.message, err.id)
} else throw err
}Development
pnpm install
pnpm typecheck
pnpm build
pnpm test # unit tests; integration tests are skipped without creds
# Run integration tests against the live API (uses your demo account):
SPEEDY_USERNAME=... SPEEDY_PASSWORD=... pnpm testRoadmap
| Version | Endpoints |
|---|---|
| 0.1.x ✅ | /client/contract/, /location/site/, /location/street/, /location/office/, /calculate/ |
| 0.2.x | /shipment/, /shipment/{id}/cancel/, /print/ (PDF labels) |
| 0.3.x | /track/, /services/, /services/destination/ |
| 0.4.x | International services, returns, COD payouts |
Contributing
Issues and PRs welcome. The whole API surface is small enough that you can read src/ end-to-end in a few minutes.
License
MIT — see LICENSE.
