Complete reference for VAP API error handling
VAP REST API uses standard HTTP status codes to indicate success or failure.
| Code | Name | Description | Resolution |
|---|---|---|---|
| 400 | Bad Request | Invalid input (empty description, invalid task type) | Check request body parameters |
| 401 | Unauthorized | Missing or invalid API key | Include valid Bearer token |
| 403 | Forbidden | Tier limit exceeded or agent paused | Upgrade tier or contact support |
| 404 | Not Found | Task or resource doesn't exist | Verify task_id or endpoint URL |
| 409 | Conflict | Duplicate idempotency key | Use unique key per request |
| 422 | Unprocessable Entity | Valid JSON but invalid parameters | Check parameter constraints |
| 429 | Too Many Requests | Rate limit exceeded | Wait and retry with exponential backoff |
| 500 | Internal Server Error | Server error | Retry request, contact support if persists |
| 503 | Service Unavailable | Maintenance or system overload | Retry after delay (check Retry-After header) |
All errors return a structured JSON response:
{
"error": {
"code": "insufficient_balance",
"message": "Not enough balance for this task",
"details": {
"required": "0.18",
"available": "0.05"
}
}
}
Application-level error codes in the error.code field:
| error.code | HTTP | Meaning |
|---|---|---|
invalid_api_key |
401 | API key not found or revoked |
insufficient_balance |
403 | Balance less than estimated cost |
tier_limit_exceeded |
403 | Task cost exceeds tier maximum |
agent_paused |
403 | Circuit breaker triggered, agent paused |
invalid_task_type |
400 | Unsupported task type |
invalid_description |
400 | Empty or missing description parameter |
task_not_found |
404 | Task ID doesn't exist or not owned by agent |
rate_limit_exceeded |
429 | Too many requests in time window |
provider_not_allowed |
403 | Provider unavailable for current tier |
{
"error": {
"code": "insufficient_balance",
"message": "Not enough balance for this task",
"details": {
"required": "0.18",
"available": "0.05",
"shortfall": "0.13"
}
}
}
{
"error": {
"code": "tier_limit_exceeded",
"message": "Task cost exceeds tier limit",
"details": {
"task_cost": "5.00",
"tier_limit": "1.00",
"current_tier": 1,
"required_tier": 2
}
}
}
{
"error": {
"code": "agent_paused",
"message": "Agent is paused due to anomaly detection",
"details": {
"reason": "Spend spike detected (3x average)",
"paused_at": "2026-01-09T12:00:00Z",
"contact": "support@vapagent.com"
}
}
}
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded",
"details": {
"retry_after": 30,
"limit": 60,
"window": "1 minute"
}
}
}
if response.status_code == 200:
# Success
result = response.json()
elif response.status_code == 401:
# Invalid API key
raise AuthenticationError("Check API key")
elif response.status_code == 403:
# Check error.code for specific reason
error = response.json()["error"]
if error["code"] == "insufficient_balance":
# Deposit more funds
...
elif response.status_code == 429:
# Rate limited, wait and retry
retry_after = response.json()["error"]["details"]["retry_after"]
time.sleep(retry_after)
For 429 and 500 errors, use exponential backoff:
import time
def request_with_retry(url, max_retries=3):
for attempt in range(max_retries):
response = requests.post(url, ...)
if response.status_code == 200:
return response.json()
elif response.status_code == 429:
retry_after = response.json()["error"]["details"].get("retry_after", 1)
wait = retry_after * (2 ** attempt)
time.sleep(wait)
elif response.status_code >= 500:
wait = 2 ** attempt
time.sleep(wait)
else:
raise APIError(response.json()["error"])
raise MaxRetriesExceeded()
If agent is paused, contact support:
if error["code"] == "agent_paused":
reason = error["details"]["reason"]
contact = error["details"]["contact"]
logger.error(f"Agent paused: {reason}")
logger.error(f"Contact: {contact}")
# Alert monitoring system
# Contact support@vapagent.com
If you encounter persistent errors: