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.
Every plan has explicit, authoritative values — no plan is "unlimited".
| Plan | Requests / minute | Burst | Concurrent requests |
|---|---|---|---|
| Free | 10 | 2 | 2 |
| Starter | 60 | 10 | 5 |
| Pro | 300 | 30 | 20 |
| Enterprise | 1200 | 120 | 100 |
Enterprise defaults are explicit starting values; negotiated per-user overrides are supported separately and take precedence when active.
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.
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.
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.
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.
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.
When your request-token bucket is empty, the engine returns:
RATE_LIMIT_EXCEEDEDrequestId, errorCode,
message)Retry-After — the rounded-up seconds until one token becomes
available (minimum 1 second)RateLimit-Limit — your plan's burst capacityRateLimit-Remaining — 0 at the rejection pointRateLimit-Reset — the rounded-up seconds until the bucket is fully
refilledNo invoice work is performed and no credits are charged on a 429. The request is still recorded as a single rejected API log entry.
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.
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.
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.