Rate Limiting & Error Handling
The MCP Tools & Servers API enforces rate limits to protect service stability and returns structured error responses to help you diagnose and recover from failures. This page covers the rate-limiting model, policy evaluation, error response format, and troubleshooting guidance.
Rate Limiting
Rate Limit Model
The API uses fixed-window, per-principal rate limiting. Each authenticated principal (user or service key) is allocated a maximum number of requests per minute (RPM). The window resets at fixed intervals regardless of when the first request was made.
Rate limits are tracked in Redis for consistency across API instances, with an in-memory fallback if Redis is temporarily unavailable.
Rate Limit Headers
All API responses include rate limit headers so your application can track its current usage:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp (seconds) when the current window resets |
Handling Rate Limits
When the rate limit is exceeded, the API returns an HTTP 429 response with a Retry-After header indicating how many seconds to wait before retrying.
Example 429 response:
{
"error_code": "RATE_LIMITED",
"message": "Rate limit exceeded. Try again in 32 seconds.",
"details": {}
}
Best practices:
- Implement exponential backoff with jitter — avoid retrying at fixed intervals, which can cause request bursts when the window resets.
- Check rate limit headers proactively — if
X-RateLimit-Remainingis low, slow down before hitting the limit. - Cache responses when possible — tool definitions and toolset configurations change infrequently and are good candidates for client-side caching.
- Use batch operations — prefer
POST /v1/toolsets:from_nexsetsandPOST /v1/tools:reconcileover individual create/update calls.
Policy Evaluation
Tool execution is subject to policy evaluation before any data is returned. The policy evaluator checks the caller's Nexset role and produces a decision that determines how the execution proceeds:
| Decision | Description |
|---|---|
allow | Execution permitted |
deny | Execution blocked — insufficient permissions |
allow_with_redactions | Execution permitted with sensitive data redacted from the result |
allow_with_limits | Execution permitted with result limits applied (e.g., reduced row count) |
Policy decisions are recorded in execution receipts for auditing. If a tool execution is denied, the API returns a 403 Forbidden error with a message describing the required role.
Error Response Format
All errors follow a standard JSON structure:
{
"error_code": "ERROR_CODE",
"message": "Human-readable description of the error",
"details": {}
}
| Field | Type | Description |
|---|---|---|
error_code | string | Machine-readable error code from the table below |
message | string | Human-readable description of what went wrong |
details | object | Additional context (varies by error type; may be empty) |
Error Codes Reference
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Invalid or expired authentication token |
FORBIDDEN | 403 | Insufficient permissions for this operation |
NOT_FOUND | 404 | Resource does not exist |
INVALID_ARGUMENT | 400 | Invalid request parameter or body |
VALIDATION_FAILED | 400 | Request body failed schema validation |
INVALID_STATE | 400 | Operation not allowed in current resource state |
ALREADY_EXISTS | 409 | Resource already exists (duplicate) |
CONFLICT | 409 | State conflict (concurrent modification) |
RATE_LIMITED | 429 | Rate limit exceeded |
INTERNAL | 500 | Internal server error |
UPSTREAM_ERROR | 502 | External service (Nexla API) error |
SERVICE_UNAVAILABLE | 503 | Service not ready (e.g., database unavailable) |
TIMEOUT | 504 | Request timed out |
PARTIAL_FAILURE | 207 | Bulk operation partially succeeded — check details for per-item results |
Common Error Scenarios
401 Unauthorized
Causes:
- Expired service key
- Missing
Bearerprefix in theAuthorizationheader - Invalid or malformed token
How to fix:
- Regenerate your service key in the Nexla UI (see Service Keys).
- Verify the header format is
Authorization: Bearer <NEXLA_SERVICE_KEY>. - Confirm you are using a service key, not an API key or session token.
403 Forbidden
Causes:
- Insufficient Nexset role for the requested operation (e.g., executing a tool without
operatorrole) - Attempting to access resources belonging to another organization
How to fix:
- Request the appropriate role from the Nexset owner. See Authentication for the role-to-permission mapping.
- Verify the tool or toolset belongs to your organization.
400 Invalid Argument
Causes:
- Missing required fields in the request body
- Invalid enum values (e.g., unsupported
kindorstatus) - Malformed JSON
How to fix:
- Check the request body against the endpoint documentation in Tools API or ToolSets API.
- Validate your JSON payload with a linter before sending.
- Review the
detailsfield in the error response for specific field-level errors.
429 Rate Limited
Causes:
- Too many requests within the current rate limit window
How to fix:
- Read the
Retry-Afterheader and wait before retrying. - Implement exponential backoff with jitter.
- Reduce request frequency by caching responses and using batch endpoints.
502 Upstream Error
Causes:
- The Nexla platform API is temporarily unavailable or returned an unexpected error
How to fix:
- Retry the request with exponential backoff.
- If the error persists, check the Nexla status page for platform incidents.
- For tool executions, query the Receipts API to verify whether the operation completed before retrying.
Best Practices
- Monitor rate limit headers — check
X-RateLimit-Remainingbefore making rapid successive requests to avoid hitting the limit. - Use exponential backoff with jitter — for all retry logic, not just rate-limit errors. This prevents thundering-herd effects.
- Prefer batch endpoints — use
POST /v1/toolsets:from_nexsetsinstead of separate tool creation and toolset creation calls. - Cache infrequently changing data — tool definitions and toolset configurations are stable between deployments and safe to cache.
- Use reconcile for bulk updates — call
POST /v1/tools:reconcileto sync multiple tools with their Nexsets in a single request instead of refreshing tools individually. - Handle partial failures — when a bulk operation returns
207 PARTIAL_FAILURE, inspect thedetailsobject to identify which items succeeded and which failed.