Skip to content
docs
UseBuildChangelog
Open Lumin

Guide

Handle rate limits

Lumin meters per month, not per second. The challenge is knowing where the meter stands before you start a 25 to 40 call reading, not throttling burst traffic.

What gets metered

  • Only tools/call consumes allowance. Protocol traffic, initialize, tools/list, notifications, is never counted or blocked.
  • Only successful tool calls count. Failed calls (4xx, 5xx, validation errors) are free.
  • The allowance refreshes on the 1st of each month, UTC.
  • The meter is per account. Every API key on an account shares one allowance, and there is no allowance per key. Beyond it, the account's extra calls (bought through a top up, or given by Lumin) are one balance, shared by every key and the Claude connector and drawn only after the free allowance.

Read the headers, not the error

Any request that gets past authentication carries X-RateLimit-Limit and X-RateLimit-Remaining (a 401 returns before the rate limiter runs, so it carries neither). Read X-RateLimit-Remaining as you go and you will never be surprised by a 429 mid-reading. A call drawn from the account's extra calls also carries X-CallPack-Balance, the extra calls left after that draw (the header keeps its older name).

A complete reading uses 25 to 40 calls, and cannot answer below a mandatory floor of 20. If you start one and hit the limit at call 30, you have spent 30 calls on an answer you cannot finish. Pre-flight the budget instead: if fewer than 50 calls remain, warn the user before you begin. A focused question needs 12 and a factual lookup 5, so a thin budget is better spent routing to the smaller tiers than truncating a full reading. See reading depth and call floors.

Handling a 429

A 429 does not mean the free allowance ran out, it means the extra calls ran out too: the free allowance is spent first, a request it cannot cover is drawn from the account's extra calls, and it only fails when both are empty. When that happens, tools/call returns HTTP 429 with a JSON-RPC error, code -32000. The useful fields are in error.data:

json
{
  "error": {
    "code": -32000,
    "message": "Monthly call allowance reached (300 tool calls this month) and no call pack balance remains on this account. The allowance refreshes on the 1st of the month, UTC. Call packs: https://lumin.guru/pricing",
    "data": {
      "retryAfterSeconds": 604800,
      "limit": 300,
      "remaining": 0,
      "packBalance": 0,
      "resetAt": "2026-08-01T00:00:00.000Z"
    }
  }
}

Do not treat this like a burst limit. retryAfterSeconds (also sent as the Retry-After header) points at the monthly refresh, so it can be days away. Sleeping and retrying is the wrong shape entirely. The right handling is to stop the current reading, tell the user when the allowance refreshes using resetAt, and point at a top up if they need calls before then. packBalance is the extra calls balance, under its older name.

Server-side compaction

Lumin compacts large tool responses on the server before returning them. This reduces token cost on your end but does not affect the meter. One logical tool call still counts as one.