Rate Limits
Understand how rate limiting works and how to optimize your API usage.
Overview
Rate limits protect our API from abuse and ensure fair usage for all users. Limits are applied per API key and vary based on your plan.
Monthly API limits
API request limits reset on the 1st of each month at midnight UTC. QR code limits are based on your current plan allocation.
Limits by Plan
Each plan has different rate limits and quotas:
Free
API requests per month
100
Static QR codes
5
Dynamic QR codes
1
- All QR types
- Custom colors
- Basic analytics (7 days)
Pro
API requests per month
1,000
Static QR codes
30
Dynamic QR codes
10
- All QR types
- Logo upload
- Analytics (30 days)
- Scan limits
Business
API requests per month
10,000
Static QR codes
Unlimited
Dynamic QR codes
100
- Everything in Pro
- Advanced analytics (1 year)
- Team collaboration
- Bulk operations
Rate Limit Headers
Every API response includes headers to help you track your usage:
| Header | Description | Example |
|---|---|---|
| X-RateLimit-Limit | Max requests per month for your plan | 1000 |
| X-RateLimit-Remaining | Requests remaining this month | 847 |
| X-RateLimit-Reset | Unix timestamp when limits reset (1st of next month) | 1735689600 |
| Retry-After | Seconds to wait (only on 429 responses) | 3600 |
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1735689600Handling Rate Limits
When you exceed the rate limit, you'll receive a 429 response. Here's how to handle it:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 30 seconds.",
"details": {
"retryAfter": 30
}
}
}Implementing Exponential Backoff
Use exponential backoff with jitter for the most resilient implementation:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.ok) {
return response.json();
}
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const waitTime = retryAfter
? parseInt(retryAfter, 10) * 1000
: Math.min(1000 * Math.pow(2, attempt) + Math.random() * 1000, 60000);
console.log(`Rate limited. Waiting ${waitTime}ms before retry...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
// Non-retryable error
const error = await response.json();
throw new Error(error.error?.message || 'Request failed');
}
throw new Error('Max retries exceeded');
}
// Usage
const qrCode = await fetchWithRetry('https://quality-qr.app/api/v1/qr', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ type: 'url', content: 'https://example.com' }),
});Best Practices
Monitor your usage
Check the X-RateLimit-Remaining header to avoid hitting limits unexpectedly.
Use bulk endpoints
When generating multiple QR codes, use the bulk endpoint to create up to 100 codes in a single request.
Cache responses
Cache generated QR codes on your end to avoid regenerating the same codes repeatedly.
Implement request queuing
Queue requests client-side and process them at a rate below your limit for smoother performance.
Avoid request bursts
Spread requests evenly over time instead of sending many at once. Bursting can trigger rate limits even if you're under the daily quota.
Need Higher Limits?
Upgrade to a higher plan for increased rate limits, or contact us for custom enterprise limits.