How to Handle API Rate Limits
Overview
Testlify enforces rate limiting on all API requests to ensure fair resource usage. When you exceed your quota, the API returns 429 Too Many Requests. This guide explains the rate limit headers, how windows reset, and how to handle 429 errors automatically in your integration.
Before You Begin
- API access must be enabled on your Testlify account.
- You need a valid API key to authenticate requests.
- Basic familiarity with HTTP headers and response codes is helpful.
Rate Limit Response Headers
Every API response includes three headers. Monitor them to stay within your quota and avoid throttling.
- Retry-After — Time in milliseconds to wait before retrying after a 429 response.
- X-Ratelimit-Consumed — Number of API calls made in the current window. Increments with each request.
- X-Ratelimit-Remaining — Calls remaining in the current window. When this hits 0, all subsequent requests are blocked until the window resets.
Example Response Headers
Sample headers from an API response:
{
"retry-after": "23284",
"x-ratelimit-consumed": "34",
"x-ratelimit-remaining": "166"
}
- retry-after: 23284 — Wait 23,284 ms (about 23 seconds) before retrying.
- x-ratelimit-consumed: 34 — 34 requests used in the current window.
- x-ratelimit-remaining: 166 — 166 requests left before hitting the limit.
How Rate Limit Windows Work
The API tracks usage in a time-based window (per minute, hour, or day). With each request:
- X-Ratelimit-Consumed increments by 1.
- X-Ratelimit-Remaining decrements by 1.
When X-Ratelimit-Remaining reaches 0, the API returns 429 Too Many Requests. Check Retry-After to know how long to wait. Both headers reset at the start of the next window.
Handling 429 Errors Automatically
Use one of these JavaScript approaches to read the Retry-After header and pause before retrying.
Option 1: Manual Retry Loop with Axios
A while loop retries on 429 responses up to a configurable maximum. Always set a max retry count in production to prevent infinite loops.
const axios = require('axios');
async function callApiWithRateLimitHandling(url, headers = {}, maxRetries = 10) {
let attempts = 0;
while (attempts < maxRetries) {
try {
const response = await axios.get(url, { headers });
return response.data;
} catch (error) {
if (error.response && error.response.status === 429) {
const retryAfter = parseInt(error.response.headers['retry-after'], 10) || 1000;
console.warn(`Rate limit exceeded. Retrying after ${retryAfter} ms...`);
await new Promise(resolve => setTimeout(resolve, retryAfter));
attempts++;
} else {
throw error;
}
}
}
throw new Error(`Max retries (${maxRetries}) exceeded`);
}
// Usage
const url = 'https://api.testlify.com/ping';
const headers = { Authorization: 'Bearer YOUR_API_KEY' };
callApiWithRateLimitHandling(url, headers)
.then(data => console.log(data))
.catch(error => console.error('API call failed:', error));
Option 2: Automatic Retry with the axios-retry Library
The axios-retry library handles retries with minimal setup and stops automatically after the configured attempt count.
const axios = require('axios');
const axiosRetry = require('axios-retry');
const axiosInstance = axios.create();
axiosRetry(axiosInstance, {
retries: 3,
retryCondition: (error) => error.response && error.response.status === 429,
retryDelay: (retryCount, error) => {
const retryAfter = parseInt(error.response.headers['retry-after'], 10) || 1000;
console.warn(`Retry attempt ${retryCount}. Waiting for ${retryAfter} ms...`);
return retryAfter;
}
});
// Usage
const url = 'https://api.testlify.com/ping';
const headers = { Authorization: 'Bearer YOUR_API_KEY' };
axiosInstance.get(url, { headers })
.then(response => console.log(response.data))
.catch(error => console.error('API call failed:', error));
Tip: Choose Option 2 (axios-retry) if you already use Axios — it requires less boilerplate and caps retries automatically.
Best Practices
- Monitor X-Ratelimit-Remaining proactively — Slow down as it approaches 0 instead of waiting for a 429.
- Use Retry-After precisely — The value is in milliseconds. Pass it directly to
setTimeoutwithout dividing or rounding; retrying too early triggers another 429. - Implement exponential backoff — Increase delays between retries exponentially (1 s → 2 s → 4 s → 8 s) to reduce pressure on the API.
- Batch and optimize requests — Combine operations into fewer API calls and avoid polling in tight loops.
Quick Reference
- Watch X-Ratelimit-Remaining on every response — when it hits 0, a 429 is next.
- On a 429, read Retry-After (milliseconds) and wait that long before retrying.
- Use the Axios examples above for automatic retry handling.
- Cap manual retry loops with a maximum attempt count to avoid infinite loops.
Need help? Contact support.