Adding parcel tracking to your Node.js application doesn’t have to be complicated. In this tutorial, you’ll build a working tracking system using the WhereParcel SDK — from installation to real-time webhook notifications.
Prerequisites
- Node.js 18+ (the SDK uses native
fetch) - A WhereParcel API key (get one free)
Step 1: Install the SDK
npm install whereparcel
The package has zero dependencies — it only uses Node.js built-in APIs.
Step 2: Initialize the Client
import { WhereParcel } from 'whereparcel';
const wp = new WhereParcel(
process.env.WHEREPARCEL_API_KEY!,
process.env.WHEREPARCEL_SECRET_KEY!
);
Store your credentials in environment variables. Never hardcode API keys in your source code.
Step 3: Track a Parcel
The simplest use case — track a single parcel by carrier code and tracking number:
const response = await wp.track([
{ carrier: 'us.usps', trackingNumber: '9400111899562537866361' },
]);
console.log(response.summary);
// { total: 1, success: 1, failed: 0, usageIncremented: 1 }
for (const result of response.results) {
if (result.status === 'success') {
console.log(`Status: ${result.data.deliveryStatus}`);
// Status: in_transit
}
}
Understanding Delivery Statuses
WhereParcel normalizes all carrier statuses into these values:
| Status | Description |
|---|---|
pending | Label created, not yet picked up |
in_transit | Package is in transit |
out_for_delivery | Out for delivery |
delivered | Successfully delivered |
failed | Delivery attempt failed |
returned | Returned to sender |
cancelled | Shipment cancelled |
unknown | Status could not be determined |
Step 4: Track Multiple Parcels
For e-commerce platforms tracking hundreds of orders, use bulk tracking (up to 5 per request):
const response = await wp.track([
{ carrier: 'us.usps', trackingNumber: '9400111899562537866361' },
{ carrier: 'us.fedex', trackingNumber: '123456789012' },
{ carrier: 'us.ups', trackingNumber: '1Z999AA10123456784' },
]);
console.log(response.summary);
// { total: 3, success: 3, failed: 0, usageIncremented: 3 }
for (const result of response.results) {
if (result.status === 'success') {
console.log(`${result.carrier}: ${result.data.deliveryStatus}`);
}
}
Step 5: Find the Right Carrier Code
Not sure which carrier code to use? The SDK can help:
// List all 500+ carriers
const allCarriers = await wp.getCarriers();
// List US carriers only
const usCarriers = await wp.getCarriersByCountry('us');
// ['us.fedex', 'us.ups', 'us.usps', ...]
// List supported countries
const countries = await wp.getCountries();
// ['kr', 'us', 'jp', 'cn', 'de', ...]
Carrier codes follow the format {country}.{carrier} — for example, us.fedex, kr.cj, jp.yamato.
Step 6: Set Up Webhooks
Instead of polling, let WhereParcel notify you when a package status changes:
6a. Create a Webhook Endpoint
const endpoint = await wp.createWebhookEndpoint({
name: 'My App - Production',
url: 'https://myapp.com/api/webhooks/tracking',
});
// Save endpoint.secret to verify webhook signatures later
console.log(endpoint.endpointId); // 'endpoint_abc123'
console.log(endpoint.secret); // 'whsec_...'
6b. Register Parcels for Monitoring
await wp.registerWebhook({
trackingItems: [
{ carrier: 'us.usps', trackingNumber: '9400111899562537866361' },
{ carrier: 'us.fedex', trackingNumber: '123456789012' },
],
recurring: true,
webhookEndpointId: endpoint.endpointId,
});
With recurring: true, WhereParcel will keep monitoring until the package is delivered, sending you updates at each status change.
6c. Handle Webhook Events
In your Express/Fastify/Next.js app, add a webhook handler:
app.post('/api/webhooks/tracking', (req, res) => {
const { carrier, trackingNumber, deliveryStatus, events } = req.body;
console.log(`${carrier} ${trackingNumber}: ${deliveryStatus}`);
// Update your database, notify the customer, etc.
res.status(200).json({ received: true });
});
Error Handling Best Practices
Always handle errors gracefully:
import {
WhereParcel,
AuthenticationError,
RateLimitError,
NotFoundError,
} from 'whereparcel';
try {
const result = await wp.track([{ carrier: 'us.usps', trackingNumber: '...' }]);
} catch (error) {
if (error instanceof AuthenticationError) {
// Invalid API key — check your credentials
console.error('Authentication failed');
} else if (error instanceof RateLimitError) {
// Too many requests — back off and retry
const retryAfter = error.retryAfter ?? 60;
console.error(`Rate limited. Retry in ${retryAfter}s`);
} else if (error instanceof NotFoundError) {
// Carrier or endpoint not found
console.error('Not found');
}
}
Complete Example: Express Tracking API
Here’s a minimal Express server that wraps WhereParcel:
import express from 'express';
import { WhereParcel } from 'whereparcel';
const app = express();
const wp = new WhereParcel(
process.env.WHEREPARCEL_API_KEY!,
process.env.WHEREPARCEL_SECRET_KEY!
);
app.get('/track/:carrier/:trackingNumber', async (req, res) => {
const { carrier, trackingNumber } = req.params;
const result = await wp.track([{ carrier, trackingNumber }]);
res.json(result);
});
app.listen(3000, () => console.log('Tracking server running on :3000'));
Test it:
curl http://localhost:3000/track/us.usps/9400111899562537866361
Next Steps
- API Documentation — Full API reference
- API Playground — Test API calls in your browser
- npm package — SDK source and docs
- Carrier List — Browse all 500+ supported carriers
Have questions? Visit our community page.