Limits and quotas
Every hard limit Ringside enforces, grouped by surface, with the exact byte counts, item counts, character caps and time windows the route code checks. These are validation ceilings. Hit one and you get a 400 invalid_request_error (or a 429 on the rate paths) with a stable code you can branch on. Account-level rate limits and wallet budgets are configurable per developer and per customer; the numbers on this page are the platform-wide hard caps that no configuration can raise.
How to read this page
Two kinds of limit exist in Ringside, and it helps to keep them apart. The first kind is a fixed validation ceiling baked into the request handler. An embeddings call may carry at most 2,048 inputs and a chat message body may serialize to at most 100,000 bytes. You cannot raise these. Exceed one and the request is rejected before any model is called, so you are not billed. The second kind is a configurable quota such as your developer rate limit, a customer rpm_limit or a monthly_budget_usd. Those are set per account and enforced at request time, and the rate ones return 429 with a Retry-After header. This page documents the fixed ceilings in full and points at the configurable ones.
Every limit below maps to a stable error.code in the response body. Human-readable error.message strings change without notice. Match code (for example input_too_large, metadata_too_large, conversation_full) and you are forward-compatible. The request id is the X-Request-Id response header, not a body field.
Chat and message limits
The chat completions endpoint caps the serialized size of the messages you send and the number and size of multimodal content parts inside any one message. The byte cap is measured against the JSON-serialized messages array, so a few large base64 image data-URLs will hit it long before a wall of plain text would.
| Limit | Value | Scope | Error code on breach |
|---|---|---|---|
| Serialized messages | 100,000 bytes | per POST /v1/chat/completions body | messages_too_large |
| Image parts per message | 2 | per message | content_part_limit_exceeded |
| Image data-URL size | 20 MB | per image part (decoded bytes) | content_part_too_large |
| Audio data size | 25 MB | per input_audio part (decoded bytes) | content_part_too_large |
| Document (PDF) parts per message | 2 | per message | content_part_limit_exceeded |
| Document data-URL size | 20 MB | per file part (decoded bytes) | content_part_too_large |
The image and document size checks decode the base64 payload of a data: URL and compare the byte length against the cap before the bytes ever leave the edge. An fc_file:<id> reference or a file_id is resolved server-side and the same 20 MB document cap applies to the fetched blob. Plain https:// URLs are passed through to the provider unmeasured. Only application/pdf is accepted for file parts today; any other mime returns 400 invalid_content_part.
A size or count breach takes precedence over a "model does not support this" error. If you send 3 images to a non-vision model, you get content_part_limit_exceeded first, not content_parts_unsupported_by_model. Fix the count, then the model choice surfaces.
File upload limits
Files uploaded through POST /v1/files (for batch input, vector-store ingestion or chat attachment) have their own ceiling that is much larger than the inline content-part caps. Use a file upload plus an fc_file: reference when an asset is bigger than the inline data-URL caps allow.
| Limit | Value | Notes |
|---|---|---|
| File upload size | 512 MB | per uploaded file, any purpose |
| Inline image (chat) | 20 MB | data-URL or resolved fc_file: blob, max 2/message |
| Inline audio (chat) | 25 MB | base64 input_audio part (decoded bytes) |
| Inline document (chat) | 20 MB | PDF only, data-URL or resolved blob, max 2/message |
Embeddings limits
The embeddings handler bounds both the array length and each string length before the upstream call, so one request cannot amplify cost or pressure the heap with an unbounded array or a multi-megabyte string. An empty input array is also rejected.
| Limit | Value | Error code |
|---|---|---|
| Inputs per request | 2,048 | input_too_large |
| Characters per input string | 100,000 | input_too_large |
| Empty input array | rejected | missing_input |
{
"error": {
"type": "invalid_request_error",
"code": "input_too_large",
"message": "Each 'input' item must be \u2264 100000 characters",
"param": "input"
}
}Rerank limits
Rerank is the second pass of two-stage retrieval (vector search, then rerank). The document count and the query and per-document character lengths are all capped. A request is billed as one search unit, so the document cap is also a cost ceiling.
| Limit | Value | Error code |
|---|---|---|
| Documents per request | 1,000 | too_many_documents |
| Query length | 10,000 chars | query_too_large |
| Per-document length | 100,000 chars | document_too_large |
| Empty documents array | rejected | missing_documents |
Vector store limits
Vector stores ingest files asynchronously and carry their own metadata. The file-batch limit bounds how many files one ingest call can attach; ingestion status moves through pending, in_progress, completed, failed or cancelled per file.
| Limit | Value | Notes |
|---|---|---|
| Files per file batch | 500 | per file-batch attach call |
| Store metadata | 4 KB | serialized JSON; shares the 4 KB cap with customers and batches |
Brains limits
| Limit | Value | Error code / note |
|---|---|---|
| Entry body | 100 MB | entry_too_large (413) on PUT /v1/brains/{id}/entries. |
recall top_k | default 8, max 100 | Clamped silently to the range. |
recall misses_top_k | default = top_k, max 100 | Bounds the explain_misses list. Clamped. |
GET /entries limit | default 100, max 500 | Offset pagination; has_more in the response. |
GET /scopes / GET /entities limit | default 100, max 500 | Required scope; offset pagination. |
| Idempotency window | 24 h | A replayed idempotency-key with a different body → idempotency_key_reuse (409). |
Brains endpoints are covered by the standard /v1 rate limits (the four enforcement layers on the rate-limiting page); there is no separate per-endpoint Brains limit today.
Conversation and thread limits
Conversations (conv_) and OpenAI-compatible threads (thread_) share a hard message cap. When a conversation reaches it, no new turn may start. A turn also takes a row-level lock so two concurrent turns cannot interleave on the same conversation; the lock self-expires so a crashed client never wedges a conversation permanently.
| Limit | Value | Behaviour at limit |
|---|---|---|
| Messages per conversation/thread | 5,000 | lock acquire returns conversation_full; the conversation is sealed to new turns |
| Conversation lock TTL | 10 min | a lock older than 10 min is treated as orphaned and may be re-acquired or swept |
| Conversation metadata | 2 KB | metadata_too_large (tighter than the 4 KB default because metadata is carried per row) |
Start a second turn while one is in flight and the lock acquire fails with already_locked (HTTP 409 conflict_error). The lock is released in a finally block on the holding request and, as a backstop, a sweeper reclaims any lock older than 10 minutes. So a hung turn frees its conversation within 10 minutes even if the holder never returns.
Batch limits
A batch references an input file uploaded with purpose=batch, targets one of three endpoints and runs in a fixed 24-hour window. The only valid completion_window value is "24h". Batches run at half the synchronous per-call price, but only above a small request floor so a one-line "batch" cannot claim the discount on otherwise-synchronous traffic.
| Limit | Value | Error code / note |
|---|---|---|
| Requests per batch | 50,000 | enforced at worker validation |
| Batch metadata | 4 KB | metadata_too_large |
| Batch metadata keys | 16 | metadata_too_many_keys |
| Completion window | "24h" only | invalid_completion_window on any other value |
| Discount floor | 10 requests | under this, batch billing uses the normal 1.0x markup |
| Allowed endpoints | 3 | /v1/chat/completions, /v1/embeddings, /v1/moderations |
Metadata object limits
Most Ringside resources (customers, vector stores, batches, conversations, assistants, threads) accept a free-form metadata object. The serialized size cap differs by surface, but the per-object key and value shape is uniform.
| Constraint | Limit |
|---|---|
| Keys per object | 16 |
| Key length | 64 chars |
| Value length | 512 chars |
| Serialized size (customers, vector stores, batches) | 4 KB |
| Serialized size (conversations) | 2 KB |
The size check runs Buffer.byteLength(JSON.stringify(metadata)) against the per-surface cap. The 2 KB conversation cap is deliberately tighter than the 4 KB default because conversation metadata is stored per row and a conversation can carry up to 5,000 messages.
Client token limits
Client tokens are short-lived, per-customer JWTs that a browser presents directly to /v1/* with the Authorization: Client <jwt> scheme. Your developer key mints them server-side via POST /v1/customers/{id}/client_tokens and must hold the api:client_tokens scope. Mint rate, token lifetime and the token's own per-minute rate are all bounded.
| Parameter | Default | Min | Max | On breach |
|---|---|---|---|---|
ttl (seconds) | 900 | 60 | 3600 | invalid_ttl |
rpm | 60 | 1 | 600 | invalid_rpm_limit |
| Mint rate | - | - | 100 / min per customer | 429 mint_rate_limit |
origin_allowlist entry | - | - | 512 chars each | invalid_origin_allowlist |
The token rpm is also clamped to the owning customer's rpm_limit: if you request rpm: 600 but the customer is capped at 120, the mint fails with rpm_exceeds_customer_cap. So the effective ceiling is the lower of 600 and the customer's own limit. Optional bind_ip pins the token to the minting client IP; origin_allowlist restricts which browser origins may use it. The token jti carries the ctok_ prefix.
curl https://api.fightclub.pro/v1/customers/cus_42/client_tokens \
-H "Authorization: Bearer ko_<key>" \
-H "Content-Type: application/json" \
-d '{
"ttl": 1800,
"rpm": 120,
"scope": ["chat", "conversations"],
"origin_allowlist": ["https://app.example.com"]
}'Pagination limits
List endpoints are cursor-paginated. Pass limit to set the page size and after set to the previous response's next_cursor to page forward. The response shape is { "data": [...], "has_more": boolean, "next_cursor": string | null }; there is no object: "list" envelope field.
| List type | `limit` default | `limit` max |
|---|---|---|
| Most list endpoints | 20 | 100 |
| Vector-store queries | 50 | 200 |
A limit outside [1, max] is clamped into range rather than erroring, so passing limit=500 on a standard list simply returns 100 rows. Some lists also accept before, status, order_by and external_id filters; see the per-endpoint reference for which apply.
Rate limits and budgets
Rate limiting is layered. Four enforcement points each hold their own counter and the lowest remaining wins: edge per-IP, then per-client-token, then per-developer, then per-customer. When any layer is exhausted the request returns 429 rate_limit_error with a Retry-After integer-seconds header and a retry_after_seconds field in the body. The response also carries X-RateLimit-Remaining for the current window. There is no X-RateLimit-Limit or X-RateLimit-Reset header.
Spend ceilings are separate from rate ceilings. A customer monthly_budget_usd over its limit returns 402 customer_budget_exceeded; an empty customer wallet returns 402 customer_wallet_empty; an empty developer API pool returns 402 wallet_empty. The per-customer rpm_limit and tpm_limit are the configurable rate quotas referenced by the client-token clamp above. Unlike the validation ceilings on this page, these are set per account and you raise them by changing the customer or developer record.
Attributing a call to an unknown customer auto-creates it, but auto-creation is rate-limited. Burst past it and you get 429 lazy_create_rate_exceeded. For bulk onboarding, create customers explicitly with POST /v1/customers instead of relying on lazy-create.
Quick reference
| Surface | Limit | Value |
|---|---|---|
| Chat | serialized messages | 100,000 bytes |
| Chat | image / document parts | 2 each per message, 20 MB each |
| Chat | audio part | 25 MB |
| Files | upload size | 512 MB |
| Embeddings | inputs / chars per input | 2,048 / 100,000 |
| Rerank | docs / query / per-doc | 1,000 / 10,000 / 100,000 chars |
| Vector stores | file batch / metadata | 500 files / 4 KB |
| Conversations | messages / lock / metadata | 5,000 / 10 min / 2 KB |
| Batch | requests / metadata / window | 50,000 / 4 KB / 24h |
| Metadata | keys / key / value | 16 / 64 / 512 chars |
| Client tokens | ttl / rpm / mint | 60-3600s (900) / 60-600 (60) / 100/min |
| Pagination | limit default / max | 20 / 100 (vector queries 50 / 200) |