レート制限とスロットリング

API レート制限 429 スロットリング リトライ

レシートローラーのAPIで、リクエスト数の制限がかかるのはどこか、制限に達したときに何が返るか、制限がなくてもクライアント側でどう流量を抑えるべきかをまとめます。アプリのアクセストークンで呼ぶ店舗・ビジネスアカウント向けのAPIには、現在リクエスト数の制限はありません。制限があるのは、レシートの受け取りや端末からの送信など、一部の個別のエンドポイントだけです。

アプリのトークンで呼ぶAPI

  • 商品・売上・予約など、アクセストークンで呼ぶ /api/v1/... のエンドポイントには、リクエスト数の上限も、プラン別の上限や1日あたりの上限もありません。
  • そのため、X-RateLimit-Limit・X-RateLimit-Remaining・X-RateLimit-Reset のようなレート制限ヘッダーは返りません。
  • トークンエンドポイント POST /api/v1/auth/token も、認可コード(authorization_code)とリフレッシュトークン(refresh_token)の交換には制限がありません。

上限がないからといって、どれだけ並列に送ってもよいわけではありません。後述の「クライアント側での流量の抑え方」に沿って実装してください。

個別に制限があるエンドポイント

次のエンドポイントには、直近1分間(メールは1時間)のリクエスト数に上限があります。数えるのは送信元のIPアドレス単位、または表の「数える単位」の組み合わせごとです。

エンドポイント上限数える単位
POST /api/v1/devices/printer/receipts、POST /api/v1/devices/printer/receipts/{token}/ad-printed300回/分IPアドレス
GET /api/v1/apps/{shopId}/ads30回/分IPアドレス
GET /api/v1/receipt-ad60回/分IPアドレス
GET /api/v1/terminals/{publicTerminalId}/last-receipt、GET /api/v1/rx/{claimToken}10回/分IPアドレスと端末(またはトークン)の組
POST /api/v1/terminals/{publicTerminalId}/claim、POST /api/v1/rx/{claimToken}/claim3回/分(上の10回/分にも数えます)IPアドレスと端末(またはトークン)の組
POST /api/v1/user/terminals/{publicTerminalId}/claim、POST /api/v1/user/rx/{claimToken}/claim3回/分IPアドレスと端末(またはトークン)の組
POST /api/v1/rx/{claimToken}/claim でメール受け取りを選んだとき3回/時送り先メールアドレス

勤怠の打刻(POST /api/v1/stores/{storeId}/timecards/punch)は、レート制限ではありませんが、同じ人の PIN を5回続けて間違えるとその人の打刻が15分間止まり、429 と {"error": "pin_locked", "message": "...", "lockedUntil": "..."} が返ります。

このほか、レシートローラー公式アプリのサインイン用の処理(IDトークンの交換や匿名アカウントの作成)にも独自の制限がありますが、開発者アプリが使う認可コードとリフレッシュトークンの交換には関係しません。

制限に達したときのレスポンス

ステータスは 429 Too Many Requests です。返り方はエンドポイントによって2通りあります。

  • プリンター送信(/api/v1/devices/printer/...)、GET /api/v1/apps/{shopId}/ads、各 .../claim の3回/分の制限では、Retry-After: 60 ヘッダーと、本文として次のテキスト Too many requests. Please try again in a minute. が返ります。
  • それ以外の制限(last-receipt、GET /api/v1/rx/{claimToken}、GET /api/v1/receipt-ad、claim の10回/分、メールの3回/時)では、本文も Retry-After もない 429 が返ります。
HTTP/1.1 429 Too Many Requests
Retry-After: 60

Too many requests. Please try again in a minute.

JSON の本文やエラーコードはありません。429 はステータスコードで判定してください。

クライアント側での流量の抑え方

  • 一括処理は1件ずつ順番に送ります。Promise.all などで数百件を同時に送らないでください。
  • 一覧系のAPIを使い、1件ずつの取得を繰り返さないようにします。たとえば商品一覧 GET /api/v1/products は全件を1回で返すので、同期では一度取得して手元で照合します。
  • 変わりにくいデータ(店舗、商品マスタ)は手元にキャッシュします。
  • 変化を知るためのポーリングより Webhook を使います(Webhookで新しい売上を受け取り、Slackに通知する)。

リトライの方針

  • 429:Retry-After があればその秒数待ってから送り直します。ない場合も、制限は直近1分間で数えるため、1分ほど待ってから送り直します。
  • 503 などの 5xx、タイムアウト、接続エラー:間隔を倍々に広げ、ゆらぎ(ジッター)を加えて数回まで送り直します。
  • 400・401・403・404:同じリクエストを送り直しても結果は変わりません。401 はトークンを更新してから1回だけ送り直します。
  • 作成の POST をタイムアウト後に送り直すと、二重に作成されることがあります。共通の Idempotency-Key ヘッダーはないため、送り直す前に一覧などで作成済みかどうかを確認してください。
async function callWithRetry(send, maxAttempts = 5) {
  for (let attempt = 1; ; attempt++) {
    const response = await send();
    const retryable = response.status === 429 || response.status >= 500;
    if (!retryable || attempt === maxAttempts) return response;

    const retryAfter = Number(response.headers.get("Retry-After"));
    const backoff = Math.min(2 ** attempt, 60) * (0.75 + Math.random() * 0.5);
    const waitSeconds = response.status === 429 ? (retryAfter || 60) : backoff;
    await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
  }
}

関連情報

公開日: 2026-04-27 更新日: 2026-10-26
このトピックについて
開発者API
機能の詳細を見る
タグ
API (26) OAuth (21) Android (13) iOS (12) Webhook (9) api (6) oauth (5) クイックスタート (5) トラブル (5) Node.js (4)
関連記事