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.
Want early access to premium features? Join our Early Access program or subscribe to our newsletter for updates.
🚦 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-Resetfor next window availability
Error Handling Checklist
- Check for 429 status codes in all API calls
- Parse and respect
Retry-Afterheader 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.