Skip to main content

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:

TierRequests/HourRequests/MonthBurst Limit
Free1,00010,000100/minute
Premium10,000100,000500/minute
EnterpriseCustomCustomCustom
Beta Program Notice

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

HeaderDescriptionExample
X-RateLimit-LimitMaximum requests allowed per period1000
X-RateLimit-RemainingRequests remaining in current period847
X-RateLimit-ResetUnix timestamp when limits reset1693526400
X-RateLimit-WindowRate limit window in seconds3600 (1 hour)
Retry-AfterSeconds 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:

  1. Contact Support: Email [email protected]
  2. Provide Details:
    • Current usage patterns
    • Expected future needs
    • Application description
    • Business case for increase
  3. 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

  1. Log Rate Limit Headers: Track your usage patterns
  2. Set Up Alerts: Warn when approaching limits
  3. Monitor Response Times: Detect performance issues
  4. 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

MetricFree TierPremium TierEnterprise
Requests/Hour1,00010,000Custom
Requests/Month10,000100,000Custom
Burst/Minute100500Custom
SupportCommunityEmailPhone/SLA

Need help? Contact [email protected] or check our support documentation.