Errors & rate limits
This page describes the standard error format of the vario API, possible application codes, rate limits by plan, and recommendations for handling failures resiliently.
Standard error format
All errors return a JSON object with the following structure:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description of the error."
}
}
The code field is a stable English identifier that you can use in your error-handling logic. The message field is informational and may change between versions; do not depend on it for business logic.
Error code table
| HTTP | Code | Description | What to do |
|---|---|---|---|
400 | TEXT_TOO_LONG | The text field exceeds 8,000 characters. | Split the text into chunks and send multiple requests. |
400 | INVALID_LANGUAGE | The submitted language is not supported for the selected playbook. | Use "es" or "pt". Check GET /v1/playbooks. |
400 | INVALID_PLAYBOOK | The playbook value does not exist in the catalog. | Check GET /v1/playbooks for the list of valid values. |
400 | INVALID_MODE | The mode value is not valid. | Use "screening", "fast", or "full". |
400 | CONTEXT_TOO_LONG | thread_context exceeds 10 elements, or an element exceeds 500 characters. | Reduce the number of context messages or truncate the longer elements. |
400 | VALIDATION_ERROR | The request schema is invalid. | Check the body structure against the POST /v1/analyze reference. |
401 | INVALID_API_KEY | The API key does not exist, is expired, or was revoked. | Verify the key in the portal. |
401 | MISSING_AUTH | The Authorization header is absent. | Include Authorization: Bearer vario_YOUR_API_KEY. |
402 | SUBSCRIPTION_REQUIRED | The tenant does not have an active subscription. | Check the account status in the portal. |
402 | INSUFFICIENT_CREDITS | The tenant's credit balance is zero. | Purchase additional credits. See Buying credits. |
403 | INSUFFICIENT_SCOPE | The API key does not have the required scope. | Generate a new key with the necessary scopes. |
409 | email_already_registered | The email already has a registered API-only account. | Retrieve the existing key or revoke it and create a new one from the portal. |
429 | RATE_LIMIT_EXCEEDED | The allowed call limit was exceeded. | Implement exponential backoff. Respect the Retry-After header. |
503 | DEEP_ANALYSIS_UNAVAILABLE | Returned only when the request includes Prefer: deep-analysis-required and deep analysis is not available. | Omit the header to receive a partial response, or retry with backoff. |
503 | PRICING_NOT_CONFIGURED | The requested credit pack does not yet have a configured price. | Check GET /api/public/checkout/credits/preview or contact [email protected]. |
500 | PIPELINE_ERROR | Internal pipeline error. The text was not analyzed. | Retry. If the error persists, report to [email protected] with the request timestamp. |
Rate limits by plan
Rate limits are applied per API key and measured over two windows: per minute and per day.
| Plan | Calls per minute | Calls per day |
|---|---|---|
| API-only (trial / no seats) | 60 | 10,000 |
| Essentials | 60 | 10,000 |
| Professional | 300 | 100,000 |
| Enterprise | 1,000 | 500,000 |
When the limit is reached, the server responds with 429 RATE_LIMIT_EXCEEDED and includes the Retry-After header with the seconds you should wait before retrying.
Resilience recommendations
Exponential backoff with jitter
When receiving a 429 or 503, do not retry immediately. Implement exponential backoff with a random component (jitter) to prevent multiple clients from hitting the server at the same time:
import time
import random
def call_with_backoff(fn, max_retries=5):
for attempt in range(max_retries):
response = fn()
if response.status_code not in (429, 503):
return response
retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
sleep_time = retry_after + random.uniform(0, 1)
time.sleep(sleep_time)
raise Exception("Max retries exceeded")
Monitor X-RateLimit-Remaining
If the X-RateLimit-Remaining header is present in the response, use it to anticipate limit exhaustion before receiving a 429. When the value is low, reduce the request cadence.
Mode strategy by volume
For large corpora, use mode=fast as the first pass. Reserve mode=full for texts that require deeper analysis. This reduces credit consumption and the risk of hitting the rate limit within short windows.
Analysis degradation
When the request does not include the Prefer: deep-analysis-required header, a deep analysis failure returns 200 with bedrock_available: false and a partial analysis. Check this field if you need to guarantee the full depth of analysis before making a decision.
Example: handling INSUFFICIENT_CREDITS error
import httpx
response = httpx.post(
"https://api.vario.lat/v1/analyze",
headers={"Authorization": "Bearer vario_YOUR_API_KEY"},
json={"text": "...", "playbook": "price_fixing", "language": "es"}
)
if response.status_code == 402:
error = response.json()["error"]
if error["code"] == "INSUFFICIENT_CREDITS":
# Notify the team to top up credits
raise InsufficientCreditsError("Credit top-up required.")