Concepts
Rate limits & errors
Every error from the API has the same shape, application/problem+json (RFC 7807), with a stable code to switch on. title and detail are for people and may change; code won't.
{
"type": "https://api.getvindex.com/problems/invalid_request",
"title": "Invalid request",
"status": 400,
"detail": "The request json failed validation.",
"code": "invalid_request",
"issues": [
"input.prompt: Invalid input: expected string, received undefined"
]
}type: a URL for the code.status: the HTTP status, repeated.issues: on validation errors, one line per problem found.
Codes
| Status | code | What happened | What to do |
|---|---|---|---|
| 400 | invalid_request | The body, query or a header failed validation | Fix the request; issues says where |
| 400 | policy_version_requires_dry_run | policy_version without the dry-run header | Add X-Vindex-Dry-Run: true (Dry-run) |
| 401 | unauthorized | Key missing, malformed, unknown or revoked | Check Authorization: Bearer vx_… |
| 402 | plan_required | Not on this plan: a live key without a plan, or one more policy, rule or reviewer seat than the plan has | Change plans at upgrade_url (Plans & limits) |
| 402 | subscription_paused | A payment has been failing for 14 days, so checks are paused | Update the card (When a payment fails) |
| 402 | subscription_inactive | The plan has ended, so the workspace is read-only | Choose a plan at upgrade_url |
| 403 | test_key_forbidden | A test key tried something only a live key can | Use a live key (API keys) |
| 403 | reveal_forbidden | reveal from someone who isn't an owner or admin | Ask an owner or admin |
| 403 | mode_forbidden | A mode filter for the other mode: a key reads its own mode's data only | Use a key of that mode (API keys) |
| 404 | not_found | No such decision, review item, policy or key in this workspace | Check the id |
| 404 | policy_not_found | policy or policy_version names nothing | Check the id and version |
| 409 | already_decided | The review item already has a verdict | Nothing: it's decided |
| 409 | cannot_revoke_current_key | A key tried to revoke itself | Revoke it with another key |
| 413 | payload_too_large | The body is over 256 KiB | Send less |
| 415 | unsupported_media_type | The body isn't sent as JSON | Send Content-Type: application/json |
| 422 | invalid_policy | A policy document broke a rule | Fix what issues lists (Policies) |
| 422 | unknown_preset | No preset by that name | GET /v1/presets |
| 422 | no_default_policy | No policy given, and the workspace has none | Create one with POST /v1/policies |
| 422 | idempotency_key_reused | Same Idempotency-Key, different body | Use a new key (Idempotency) |
| 429 | rate_limited | Over the per-key rate limit | Wait Retry-After seconds |
| 500 | internal_error | Something broke on our side | Retry with the same Idempotency-Key |
| 503 | screening_unavailable | The screening engine couldn't be reached; nothing was decided | Wait Retry-After seconds and retry |
An unknown path is a plain 404 without a code. Dashboard-only codes (role_forbidden and the like) never reach an API key.
Rate limits
Each key gets its plan's requests a minute, over a sliding window: 600 on Starter, 1,200 on Growth, 3,000 on Scale, and 60 for a workspace with no plan yet (Plans & limits). Every /v1 response carries:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per minute |
X-RateLimit-Remaining | Requests left in the window |
X-RateLimit-Reset | Seconds until the window is clear |
Over the limit, the API answers 429 rate_limited with Retry-After in seconds. Need more than your plan's? Email hello@getvindex.com.
When screening is unavailable
If the screening engine can't be reached, POST /v1/check returns 503 screening_unavailable with Retry-After. Nothing was decided and nothing was logged. Vindex never answers allow because it couldn't check.
What your app does meanwhile is your call. Most hold the generation and retry. With the same Idempotency-Key, a retry can never log the decision twice. Idempotency has a retry loop in Node and Python.
Limits on a check
| Field | Limit |
|---|---|
input.prompt | 1 to 10,000 characters |
input.negative_prompt | 5,000 characters |
input.context | 20 items, 5,000 characters each |
metadata | 4,096 bytes of JSON |
user | 128 characters |
Idempotency-Key | 1 to 255 printable ASCII characters |
| Whole body | 256 KiB |