Rate Limits & Best Practices
Understand API rate limits and choose the right integration pattern
Rate Limits & Best Practices
Rate Limits by Plan
Every API key has per-minute and per-month request quotas:
| Plan | Requests/min | Requests/month | Price |
|---|---|---|---|
| Free | 1 | 5 | $0 |
| Starter | 30 | 10,000 | $15/mo |
| Pro | 60 | 30,000 | $29/mo |
| Business | 200 | 300,000 | $199/mo |
All plans also have a server-protection limit of 10 requests/second per API key.
When you exceed a limit, the API returns HTTP 429 Too Many Requests. Check these response headers to monitor your usage:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1706200000
Choosing the Right Integration Pattern
/v2/track — On-demand lookups
Use this for one-off, user-initiated queries. For example:
- A customer checks their order status on your website
- A support agent looks up a tracking number
- A one-time bulk import of tracking data
Each call counts toward your request quota.
/v2/webhooks/register with recurring: true — Continuous monitoring (Recommended)
Use this for keeping your database in sync with delivery status. For example:
- Updating order records when status changes
- Sending notifications to customers on delivery
- Building a tracking dashboard with live status
Why webhooks are better for monitoring:
| Approach | Requests for 1,000 parcels |
|---|---|
Polling /v2/track every 30 min | ~48,000/day |
| Webhook subscription | 1 per parcel |
Webhook subscriptions count only 1 request per tracking number — regardless of how many status updates occur during delivery. Register once, and WhereParcel monitors the parcel until delivery at no additional cost.
// Register once — receive updates automatically
await fetch('https://api.whereparcel.com/v2/webhooks/register', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}:${SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
trackingItems: [
{ carrier: 'us.usps', trackingNumber: '9400111899562537866361' }
],
recurring: true,
webhookEndpointId: 'your-endpoint-id',
}),
});
Handling Rate Limit Errors
When you receive HTTP 429, use exponential backoff:
async function trackWithRetry(carrier, trackingNumber, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch('https://api.whereparcel.com/v2/track', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}:${SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
trackingItems: [{ carrier, trackingNumber }],
}),
});
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
return await response.json();
}
}
Tips to Reduce API Usage
- Use webhooks instead of polling — saves 95%+ of requests
- Cache results — delivered parcels won’t change status
- Batch requests —
/v2/tracksupports up to 5 items per call - Stop tracking delivered parcels — no need to keep querying