You’re dealing with three interconnected challenges: API rate limiting fundamentals, client-side throttling implementation, and exponential backoff with jitter. Let me address each systematically.
For API rate limiting, HubSpot enforces limits at multiple levels: per-second burst limits, per-10-second rolling windows, and daily quotas. When you hit a limit, the API returns HTTP 429 with a Retry-After header indicating when you can retry. The key insight: client-side calls from multiple users count against your portal’s shared rate limit, so concurrent redemptions quickly exhaust your quota. You need both immediate retry handling and longer-term rate management.
For client-side throttling, implement a request queue with controlled concurrency:
const requestQueue = [];
let activeRequests = 0;
const MAX_CONCURRENT = 3;
function queueAPICall(fn) {
return new Promise((resolve, reject) => {
requestQueue.push({ fn, resolve, reject });
processQueue();
});
}
For exponential backoff, here’s a production-ready implementation:
async function redeemWithBackoff(contactId, points, maxRetries = 5) {
let delay = 1000;
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(`/api/redeem`, {
method: 'POST',
body: JSON.stringify({ contactId, points })
});
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
delay = retryAfter ? retryAfter * 1000 : delay * 2;
await new Promise(r => setTimeout(r, delay * (0.5 + Math.random())));
continue;
}
return await response.json();
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}
The jitter (0.5 + Math.random()) prevents all clients from retrying simultaneously after a rate limit. This is crucial for distributed systems.
However, I strongly echo the earlier advice: move to a server-side architecture. Client-side API calls for write operations create security, rate limiting, and reliability issues. Your server can implement proper queuing, batch processing, and rate limit management that’s impossible to achieve reliably from client code. Consider it a priority architectural improvement rather than an optional refactor.
In the interim, add optimistic UI updates - show the redemption as successful immediately, then reconcile with the API response asynchronously. This improves perceived performance and user experience even when dealing with rate limit delays.