Kalpa LabsDocs
DashboardGet API key
Guides

Rate limits & errors

Rate-limit headers, error responses, and request limits.

Rate limits

Rate limits apply to generation endpoints. Check your current values in GET /v1/info under limits, or contact hello@kalpalabs.ai for a higher limit.

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute
X-RateLimit-RemainingRequests currently available
X-RateLimit-ResetSeconds until the limit resets

When you reach the limit, the API returns 429 with Retry-After:

JSON
{ "error": { "type": "rate_limit_exceeded", "message": "Rate limit exceeded (600 requests/min). Retry in 2s.", "request_id": "…" } }

Wait for Retry-After before retrying.

Usage allowance

Every API key belongs to an organization. The first organization you create has a one-time allowance of generated audio (1,000 minutes) that expires three months after it was created; organizations you create after that start without one. Once the allowance is used up or has expired, generation requests (/v1/tts/{voice_id}, /v1/converse, and the streaming variants) return 402, and a WebSocket initializeConnection closes with code 4402:

JSON
{ "error": { "type": "allowance_exhausted", "message": "Your organization has used its free allowance (1,000 of 1,000 minutes of generated audio). Contact hello@kalpalabs.ai to keep generating.", "request_id": "…" } }

Do not retry a 402: nothing about it clears with time. GET /v1/models, /v1/voices, /v1/info and /v1/usage keep answering with the same key, and the organization block of GET /v1/usage shows the usage and allowance the refusal was based on. Write to hello@kalpalabs.ai to move to a contract.

Usage is ingested asynchronously, so the gate sees a just-completed generation a few seconds later, and a WebSocket session is checked when it starts, not before every response.

If an organization is suspended, every one of its keys gets 403 with type account_suspended on every endpoint (WebSocket close code 4403).

Overloaded

When every model server is busy, a generation request is refused before generation starts, with type overloaded. REST requests (/v1/tts/{voice_id}, /v1/tts/{voice_id}/stream, /v1/converse, /v1/converse/stream) return 503 with Retry-After:

JSON
{ "error": { "type": "overloaded", "message": "The model is temporarily overloaded. Retry shortly.", "request_id": "…" } }

On a WebSocket, the affected response gets a non-fatal error frame with error.type overloaded, then settles like any failed response: responseCreated if it was not sent yet, then responseDone with status cancelled. The connection stays open, so send the text and flush again.

With generation_config, an utterance refused before the server has received its flush gets the error with fatal: true instead, and the connection closes (1011) without a responseDone: reconnect, then resend the whole turn.

overloaded is not caused by your request or your rate limit, and no audio was generated, so it is always safe to retry. Wait for Retry-After (about a second; WebSocket frames carry no header), then back off exponentially between further attempts. A failure after generation has started is an inference_error.

The error envelope

REST errors use one response shape:

JSON
{ "error": { "type": "…", "message": "…", "request_id": "…" } }

Validation errors may also include error.details. SSE and WebSocket streams send errors as protocol events after streaming begins; the raw audio stream of POST /v1/tts/{voice_id}/stream has no events, so a failure after its audio starts terminates the connection before the response completes.

StatustypeMeaning
400invalid_requestInvalid request, audio, model, or conversation.
400context_length_exceededGeneration filled the model's context window before a stop token. Try shorter sentences.
401authentication_errorMissing or invalid API key.
402allowance_exhaustedYour organization's allowance of generated audio is used up or has expired. Do not retry.
403account_suspendedYour organization is suspended.
404not_foundPath or voice not found.
405method_not_allowedHTTP method not supported.
422invalid_requestRequest body does not match the schema.
429rate_limit_exceededRate limit reached.
500internal_errorUnexpected server error.
502inference_errorModel backend error or timeout.
503overloadedThe model is temporarily at capacity. Retry after Retry-After.
503service_unavailableAPI-key verification is temporarily unavailable.

Request caps

Current request limits are also available from GET /v1/info:

LimitCurrent value
Text per request or turn8,000 characters
Turns per REST/SSE conversation64
Audio per turn25 MiB decoded WAV

Debugging a failed call

Use error.type in application logic and message for display. For persistent failures, share the request_id with support; it also appears in the X-Request-ID response header.