Rate Limits

InvoiceSpec shapes traffic with three per-plan allowances: a sustained request rate, a bounded burst capacity, and a concurrency cap. Together they protect the invoice engine from bursts and excessive in-flight work while keeping each plan's capacity predictable. Credits govern total paid usage; rate limits govern traffic shape and platform fairness.

Per-plan allowances

Every plan has explicit, authoritative values — no plan is "unlimited".

PlanRequests / minuteBurstConcurrent requests
Free1022
Starter60105
Pro3003020
Enterprise1200120100

Enterprise defaults are explicit starting values; negotiated per-user overrides are supported separately and take precedence when active.

One shared pool per user

All API keys owned by one user draw from a single shared allowance. Creating additional API keys does not multiply your capacity — your throughput is governed by your plan, not by your credential count.

How effective limits are resolved

Your effective limits follow your currently effective subscription, not a scheduled future plan. A plan change takes effect at the next billing-period boundary. The engine resolves the three allowances from your plan's entitlements as part of API-key authentication, without an extra lookup on every request.

You can view your effective values at any time on the dashboard Billing page, which shows the requests/minute, burst, and concurrency for your current plan.

Scope

Plan quotas apply to the invoice-engine endpoints (/v1/invoice/**). Health, readiness, OpenAPI/Swagger, and internal service-to-service billing and credit routes are outside customer quotas so operational control paths remain available.

Configuration failures

A missing, malformed, non-positive, or out-of-range entitlement is a platform configuration failure, not a quota reduction. It never implies unlimited access and never silently applies Free limits to a paid plan. When the engine cannot resolve your effective policy, it returns HTTP 503 RATE_LIMIT_CONFIGURATION_UNAVAILABLE — no invoice work is performed, no credits are charged, and the response includes X-Request-Id for support. Retry later; if it persists, contact support.

Enforcement contract

Every successfully authenticated request to /v1/invoice/** consumes exactly one request token before validation, credit checks, concurrency admission, or engine processing. This means a request that is later rejected by validation or billing still counts against your rate limit — the token was already consumed. A rate-limited rejection performs no engine work and acquires no concurrency permit.

429 Too Many Requests

When your request-token bucket is empty, the engine returns:

  • HTTP 429 with error code RATE_LIMIT_EXCEEDED
  • A JSON body matching the standard error shape (requestId, errorCode, message)
  • Retry-After — the rounded-up seconds until one token becomes available (minimum 1 second)
  • RateLimit-Limit — your plan's burst capacity
  • RateLimit-Remaining0 at the rejection point
  • RateLimit-Reset — the rounded-up seconds until the bucket is fully refilled

No invoice work is performed and no credits are charged on a 429. The request is still recorded as a single rejected API log entry.

Response headers on every authenticated request

Every authenticated invoice response — including a 429 — carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset for the request-token bucket. These headers describe only the token bucket and must not be read as concurrency availability; concurrency is enforced separately and is not reflected in these headers.

Retry guidance

When you receive a 429, wait the number of seconds indicated by Retry-After before sending the next request. For sustained throughput above your plan's requests/minute, upgrade to a higher plan or implement client-side throttling to stay within your allowance.

Plan changes and proportional usage

When your effective plan changes between requests (for example, a Free → Pro upgrade at a billing-period boundary), the engine reconfigures your bucket preserving proportional usage rather than resetting it to full capacity. If you had consumed half of your Free burst, you will have half of your new Pro burst available immediately after the upgrade — you do not get a fresh full bucket.