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/callconsumes 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 credential: per signed-in account on OAuth, per key on API key access.
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 call pack balance also carries X-CallPack-Balance, the balance left after that draw.
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 call pack balance ran out too: a request the free allowance cannot cover is drawn from the call pack balance first, and 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:
{
"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 call pack if packBalance is not already what they need before then.
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.