レートリミット

TCG Price Lookup APIのレートリミット、日次クォータ、ヘッダー、使用量管理のベストプラクティス。


プラン別の制限

プラン価格1日のリクエスト数バーストリミット
無料$02001リクエスト / 3秒
Trader$14.99/月10,0001リクエスト / 秒
Business$89.99/月100,0003リクエスト / 秒

日次制限は毎日UTC深夜にリセットされます。バーストリミットは1秒ごとに適用されます — 日次クォータが残っていても超えると一時的な 429 が発生します。

レートリミットヘッダー

すべてのAPIレスポンスには、リアルタイムで使用量を監視できるこれらのヘッダーが含まれています:

X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 9987
X-RateLimit-Reset: 1712764800
ヘッダー説明
X-RateLimit-Limit1日のリクエスト許可数の合計
X-RateLimit-Remaining本日の残りリクエスト数
X-RateLimit-Reset制限がリセットされるUnixタイムスタンプ(UTC深夜)

コードでこれらのヘッダーを読み取る方法:

const response = await fetch('https://api.tcgpricelookup.com/v1/search?q=charizard', {
  headers: { 'X-API-Key': process.env.TCG_API_KEY }
});

const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
const resetAt = parseInt(response.headers.get('X-RateLimit-Reset'));

if (remaining < 50) {
  const resetDate = new Date(resetAt * 1000);
  console.warn(`Low quota: ${remaining} requests left. Resets at ${resetDate.toISOString()}`);
}

レートリミット時の対応

429 Too Many Requests レスポンスを受け取ります:

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "You have exceeded your rate limit. Try again in 60 seconds.",
    "status": 429
  }
}

Retry-After ヘッダーが待機する秒数を正確に示します:

HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1712764800

再試行前に少なくとも Retry-After 秒待機してください。すぐに再試行すると別の429が発生します。

バッチリクエストを使用する

リクエスト数を削減するための最大の効果はバッチエンドポイントを使用することです。

// 悪い例: 20回の個別リクエスト = 20クォータ消費
for (const id of cardIds) {
  const card = await tcg.getCard(id);
  process(card);
}

// 良い例: 1回のバッチリクエスト = 1クォータ消費
const cards = await tcg.batchLookup(cardIds); // 1回の呼び出しで最大20個のID
cards.data.forEach(process);

バッチリクエストは1回のリクエストで最大20個のカードIDを受け付けます。20枚以上のカードがある場合はチャンクに分割してください:

function chunk(arr, size) {
  return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
    arr.slice(i * size, i * size + size)
  );
}

const chunks = chunk(allCardIds, 20);
const results = await Promise.all(chunks.map(ids => tcg.batchLookup(ids)));
const allCards = results.flatMap(r => r.data);

レスポンスをキャッシュする

カード価格は継続的ではなく定期的に更新されます。数分間でもキャッシュするだけで、データの鮮度を犠牲にすることなくリクエスト数を大幅に削減できます。

// TTL付きのシンプルなメモリキャッシュ
const CACHE_TTL_MS = 5 * 60 * 1000; // 5分
const cache = new Map();

async function getCardCached(id) {
  const entry = cache.get(id);
  if (entry && Date.now() - entry.timestamp < CACHE_TTL_MS) {
    return entry.data;
  }
  const card = await tcg.getCard(id);
  cache.set(id, { data: card, timestamp: Date.now() });
  return card;
}

本番環境ではRedisなどの適切なキャッシュストアを使用してください:

import { createClient } from 'redis';

const redis = createClient({ url: process.env.REDIS_URL });

async function getCardCached(id) {
  const cached = await redis.get(`tcg:card:${id}`);
  if (cached) return JSON.parse(cached);

  const card = await tcg.getCard(id);
  // 5分間(300秒)キャッシュ
  await redis.setEx(`tcg:card:${id}`, 300, JSON.stringify(card));
  return card;
}

レートリミッターミドルウェア

多数のAPI呼び出しを行うアプリを構築している場合、クライアントサイドのレートリミッターを使用すると誤ってバーストリミットに達するのを防げます:

class RateLimiter {
  constructor(requestsPerSecond) {
    this.queue = [];
    this.interval = 1000 / requestsPerSecond;
    this.lastCall = 0;
  }

  async acquire() {
    const now = Date.now();
    const wait = Math.max(0, this.lastCall + this.interval - now);
    if (wait > 0) await new Promise(r => setTimeout(r, wait));
    this.lastCall = Date.now();
  }
}

const limiter = new RateLimiter(1); // 1リクエスト/秒(Traderプラン)

async function getCard(id) {
  await limiter.acquire();
  return tcg.getCard(id);
}

指数バックオフを実装する

レートリミットに達した場合は、サンダリングハード問題を避けるためにジッターを加えた指数バックオフを実装してください:

async function fetchWithRetry(fn, maxRetries = 4) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (err.status !== 429 || attempt === maxRetries - 1) throw err;

      const baseDelay = err.retryAfter
        ? err.retryAfter * 1000
        : Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s

      const jitter = Math.random() * 500; // 最大500msのジッター
      await new Promise(r => setTimeout(r, baseDelay + jitter));
    }
  }
}

ダッシュボードで使用量を監視する

ダッシュボードでは以下を確認できます:

  • 日次使用量グラフ — 過去30日間のリクエスト数
  • 現在のクォータ — 本日の残りリクエスト数
  • リセットカウントダウン — UTC深夜にリセットされるまでの時間
  • プラン使用率 — 1日の制限のうち消費した割合

プログラムから確認するには、任意のレスポンスの X-RateLimit-* ヘッダーを調べてください。追加のエンドポイントは不要です。

上位のリミットが必要ですか?

無料プラン(1日200リクエスト)は試験的な使用に最適です。本番環境向けのものを構築する準備ができたら:

  • Trader(月額$14.99)— 1日10,000リクエスト + 価格履歴へのアクセス
  • Business(月額$89.99)— 1日100,000リクエスト + 優先サポート

プランをアップグレード