API Rate Limits and Quotas
Comprehensive guide to Trading Card API rate limits, usage quotas, and best practices for efficient API usage.
📊 Quick Overview
The Trading Card API implements fair usage policies to ensure reliable service for all users:
Tier | Requests/Hour | Requests/Month | Burst Limit |
---|---|---|---|
Free | 1,000 | 10,000 | 100/minute |
Premium | 10,000 | 100,000 | 500/minute |
Enterprise | Custom | Custom | Custom |
During beta, all users receive Free tier limits. Premium and Enterprise tiers will be available at general availability.
🚦 Rate Limiting Details
Request Rate Limits
Hourly Limits
- Free Tier: 1,000 requests per hour
- Premium Tier: 10,000 requests per hour (Coming Soon)
- Enterprise Tier: Custom limits based on agreement
Burst Limits
- Free Tier: 100 requests per minute (short bursts allowed)
- Premium Tier: 500 requests per minute
- Enterprise Tier: Configurable burst capacity
Daily and Monthly Quotas
- Free Tier: 10,000 requests per month
- Premium Tier: 100,000 requests per month
- Enterprise Tier: Custom monthly allowances
Rate Limit Enforcement
Rate limits are enforced using a sliding window approach:
- Limits reset continuously rather than at fixed intervals
- Recent request history determines current rate limit status
- Fair distribution of requests across time periods
📋 Rate Limit Headers
Every API response includes rate limiting information in the headers:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1693526400
X-RateLimit-Window: 3600
Retry-After: 1800
Header Descriptions
Header | Description | Example |
---|---|---|
X-RateLimit-Limit | Maximum requests allowed per period | 1000 |
X-RateLimit-Remaining | Requests remaining in current period | 847 |
X-RateLimit-Reset | Unix timestamp when limits reset | 1693526400 |
X-RateLimit-Window | Rate limit window in seconds | 3600 (1 hour) |
Retry-After | Seconds to wait before retry (when rate limited) | 1800 |
⚠️ Rate Limit Responses
When rate limits are exceeded, the API returns a 429 Too Many Requests
response:
{
"errors": [
{
"status": "429",
"title": "Rate Limit Exceeded",
"detail": "API rate limit exceeded. Please wait before making additional requests.",
"meta": {
"rate_limit": {
"limit": 1000,
"remaining": 0,
"reset": 1693526400,
"retry_after": 1800
}
}
}
]
}
💻 Code Examples
JavaScript (Node.js)
const axios = require('axios');
class TradingCardAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseURL = 'https://api.tradingcardapi.com';
}
async makeRequest(endpoint, options = {}) {
try {
const response = await axios({
url: `${this.baseURL}${endpoint}`,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Accept': 'application/vnd.api+json',
...options.headers
},
...options
});
// Log rate limit status
this.logRateLimit(response.headers);
return response.data;
} catch (error) {
if (error.response?.status === 429) {
return this.handleRateLimit(error.response, endpoint, options);
}
throw error;
}
}
logRateLimit(headers) {
const limit = headers['x-ratelimit-limit'];
const remaining = headers['x-ratelimit-remaining'];
const reset = headers['x-ratelimit-reset'];
console.log(`Rate Limit: ${remaining}/${limit} requests remaining`);
console.log(`Resets at: ${new Date(reset * 1000).toISOString()}`);
}
async handleRateLimit(response, endpoint, options) {
const retryAfter = parseInt(response.headers['retry-after']) || 60;
console.warn(`Rate limited. Retrying after ${retryAfter} seconds...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return this.makeRequest(endpoint, options);
}
}
// Usage
const api = new TradingCardAPI('your-api-key');
// Get cards with automatic rate limit handling
const cards = await api.makeRequest('/cards', {
params: { 'page[limit]': 25 }
});
Python
import requests
import time
from datetime import datetime
class TradingCardAPI:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = 'https://api.tradingcardapi.com'
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Accept': 'application/vnd.api+json'
})
def make_request(self, endpoint, **kwargs):
try:
response = self.session.get(f'{self.base_url}{endpoint}', **kwargs)
# Log rate limit status
self._log_rate_limit(response.headers)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
return self._handle_rate_limit(e.response, endpoint, **kwargs)
raise
def _log_rate_limit(self, headers):
limit = headers.get('X-RateLimit-Limit')
remaining = headers.get('X-RateLimit-Remaining')
reset = headers.get('X-RateLimit-Reset')
if all([limit, remaining, reset]):
reset_time = datetime.fromtimestamp(int(reset))
print(f"Rate Limit: {remaining}/{limit} requests remaining")
print(f"Resets at: {reset_time.isoformat()}")
def _handle_rate_limit(self, response, endpoint, **kwargs):
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Retrying after {retry_after} seconds...")
time.sleep(retry_after)
return self.make_request(endpoint, **kwargs)
# Usage
api = TradingCardAPI('your-api-key')
# Get cards with automatic rate limit handling
cards = api.make_request('/cards', params={'page[limit]': 25})
PHP
<?php
class TradingCardAPI
{
private $apiKey;
private $baseUrl = 'https://api.tradingcardapi.com';
public function __construct($apiKey)
{
$this->apiKey = $apiKey;
}
public function makeRequest($endpoint, $params = [])
{
$url = $this->baseUrl . $endpoint;
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => [
'Authorization: Bearer ' . $this->apiKey,
'Accept: application/vnd.api+json'
]
]
]);
$response = file_get_contents($url, false, $context);
if ($response === false) {
$statusLine = $http_response_header[0];
if (strpos($statusLine, '429') !== false) {
return $this->handleRateLimit($http_response_header, $endpoint, $params);
}
throw new Exception('API request failed');
}
$this->logRateLimit($http_response_header);
return json_decode($response, true);
}
private function logRateLimit($headers)
{
$rateHeaders = [];
foreach ($headers as $header) {
if (strpos($header, 'X-RateLimit-') === 0) {
list($key, $value) = explode(': ', $header, 2);
$rateHeaders[$key] = $value;
}
}
if (isset($rateHeaders['X-RateLimit-Remaining'], $rateHeaders['X-RateLimit-Limit'])) {
$remaining = $rateHeaders['X-RateLimit-Remaining'];
$limit = $rateHeaders['X-RateLimit-Limit'];
echo "Rate Limit: {$remaining}/{$limit} requests remaining\n";
}
}
private function handleRateLimit($headers, $endpoint, $params)
{
$retryAfter = 60; // Default
foreach ($headers as $header) {
if (strpos($header, 'Retry-After:') === 0) {
$retryAfter = (int) trim(substr($header, 12));
break;
}
}
echo "Rate limited. Retrying after {$retryAfter} seconds...\n";
sleep($retryAfter);
return $this->makeRequest($endpoint, $params);
}
}
// Usage
$api = new TradingCardAPI('your-api-key');
// Get cards with automatic rate limit handling
$cards = $api->makeRequest('/cards', ['page[limit]' => 25]);
?>
🎯 Best Practices
1. Monitor Rate Limit Headers
Always check rate limit headers in responses to avoid hitting limits:
function checkRateLimit(response) {
const remaining = parseInt(response.headers['x-ratelimit-remaining']);
const limit = parseInt(response.headers['x-ratelimit-limit']);
if (remaining < limit * 0.1) { // Less than 10% remaining
console.warn('Approaching rate limit. Consider slowing down requests.');
}
}
2. Implement Exponential Backoff
When rate limited, use exponential backoff for retries:
async function exponentialBackoff(attempt, maxAttempts = 5) {
if (attempt >= maxAttempts) {
throw new Error('Max retry attempts exceeded');
}
const delay = Math.min(1000 * Math.pow(2, attempt), 30000); // Max 30 seconds
await new Promise(resolve => setTimeout(resolve, delay));
}
3. Cache Responses Intelligently
Reduce API calls by caching responses when appropriate:
class APICache {
constructor(ttl = 300000) { // 5 minutes default
this.cache = new Map();
this.ttl = ttl;
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() > item.expires) {
this.cache.delete(key);
return null;
}
return item.data;
}
set(key, data) {
this.cache.set(key, {
data,
expires: Date.now() + this.ttl
});
}
}
4. Batch Requests When Possible
Use includes to fetch related data in single requests:
// ❌ Multiple requests
const card = await api.get('/cards/123');
const set = await api.get(`/sets/${card.relationships.set.data.id}`);
const player = await api.get(`/players/${card.relationships.oncard.data[0].id}`);
// ✅ Single request with includes
const cardWithRelations = await api.get('/cards/123?include=set,oncard');
5. Use Appropriate Page Sizes
Balance between fewer requests and manageable response sizes:
// ❌ Too many small requests
for (let page = 1; page <= 100; page++) {
await api.get(`/cards?page[number]=${page}&page[limit]=10`);
}
// ✅ Fewer larger requests
for (let page = 1; page <= 10; page++) {
await api.get(`/cards?page[number]=${page}&page[limit]=100`);
}
📜 Usage Policies
Fair Use Guidelines
Acceptable Use
- Building trading card applications and tools
- Personal collection management
- Educational and research purposes
- Commercial applications with appropriate tier
Prohibited Uses
- Excessive automated scraping without proper rate limiting
- Reselling raw API data without value addition
- Bypassing rate limits through multiple accounts
- Using the API for spam or malicious purposes
Commercial vs Personal Use
Personal Use (Free Tier)
- Individual developers and hobbyists
- Non-commercial projects and experimentation
- Educational use and learning
Commercial Use (Premium/Enterprise)
- Production applications serving users
- Revenue-generating services
- High-volume integrations
- Mission-critical applications requiring SLA
🔧 Troubleshooting
Common Rate Limiting Issues
Issue: Constant 429 Errors
// Problem: Not respecting Retry-After header
setTimeout(() => retry(), 1000); // ❌ Fixed delay
// Solution: Use Retry-After header value
const retryAfter = response.headers['retry-after'];
setTimeout(() => retry(), retryAfter * 1000); // ✅ Proper delay
Issue: Hitting Burst Limits
// Problem: Sending requests too quickly
Promise.all(urls.map(url => api.get(url))); // ❌ All at once
// Solution: Control concurrency
async function limitConcurrency(urls, limit = 5) {
const results = [];
for (let i = 0; i < urls.length; i += limit) {
const batch = urls.slice(i, i + limit);
const batchResults = await Promise.all(batch.map(url => api.get(url)));
results.push(...batchResults);
// Small delay between batches
if (i + limit < urls.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return results;
}
Issue: Unexpected Rate Limit Resets
- Rate limits use sliding windows, not fixed periods
- Previous hour's requests affect current limit status
- Monitor
X-RateLimit-Reset
for next window availability
Error Handling Checklist
- Check for 429 status codes in all API calls
- Parse and respect
Retry-After
header values - Implement exponential backoff for failed requests
- Log rate limit headers for monitoring
- Set up alerts for approaching rate limits
- Cache responses to reduce API calls
- Use batch operations when available
📞 Need Higher Limits?
Requesting Limit Increases
If your application requires higher limits:
- Contact Support: Email [email protected]
- Provide Details:
- Current usage patterns
- Expected future needs
- Application description
- Business case for increase
- Upgrade Path: Consider Premium or Enterprise tiers
Enterprise Solutions
For high-volume applications, we offer:
- Custom Rate Limits: Tailored to your needs
- Dedicated Infrastructure: Isolated resources
- SLA Guarantees: Uptime and performance commitments
- Priority Support: Direct access to our engineering team
- Custom Integrations: Specialized endpoints for your use case
📊 Monitoring Your Usage
Usage Tracking Tips
- Log Rate Limit Headers: Track your usage patterns
- Set Up Alerts: Warn when approaching limits
- Monitor Response Times: Detect performance issues
- Track Error Rates: Identify problematic endpoints
Sample Monitoring Code
class UsageMonitor {
constructor() {
this.requests = [];
this.errors = [];
}
recordRequest(endpoint, headers) {
this.requests.push({
endpoint,
timestamp: Date.now(),
remaining: parseInt(headers['x-ratelimit-remaining']),
limit: parseInt(headers['x-ratelimit-limit'])
});
}
recordError(endpoint, status, error) {
this.errors.push({
endpoint,
status,
error: error.message,
timestamp: Date.now()
});
}
getUsageStats() {
const now = Date.now();
const lastHour = now - 3600000;
const recentRequests = this.requests.filter(r => r.timestamp > lastHour);
const recentErrors = this.errors.filter(e => e.timestamp > lastHour);
return {
requestsLastHour: recentRequests.length,
errorsLastHour: recentErrors.length,
errorRate: recentErrors.length / recentRequests.length,
mostRecentLimit: recentRequests[recentRequests.length - 1]?.remaining
};
}
}
🔄 Rate Limit Changes
Notification Process
We'll notify users of rate limit changes through:
- Email announcements to registered users
- API changelog documentation updates
- GitHub releases for SDK updates
- Blog posts for major changes
Backwards Compatibility
- Rate limit increases: Applied immediately
- Rate limit decreases: 30-day advance notice
- New rate limit tiers: Opt-in basis
- Emergency changes: Immediate with explanation
Quick Reference
Metric | Free Tier | Premium Tier | Enterprise |
---|---|---|---|
Requests/Hour | 1,000 | 10,000 | Custom |
Requests/Month | 10,000 | 100,000 | Custom |
Burst/Minute | 100 | 500 | Custom |
Support | Community | Phone/SLA |
Need help? Contact [email protected] or check our support documentation.