> ## Documentation Index
> Fetch the complete documentation index at: https://cryptorobot.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors & Rate Limits

> Error response format, HTTP status codes, and rate limiting

## Error Response Format

All errors follow a consistent JSON structure:

```json theme={null}
{
  "name": "BadRequest",
  "message": "Validation failed: 'pair' is required",
  "code": 400,
  "className": "bad-request",
  "errors": [
    {
      "field": "pair",
      "message": "'pair' is required"
    }
  ]
}
```

| Field       | Type    | Description                             |
| ----------- | ------- | --------------------------------------- |
| `name`      | string  | Error class name                        |
| `message`   | string  | Human-readable description              |
| `code`      | integer | HTTP status code                        |
| `className` | string  | Kebab-case error identifier             |
| `errors`    | array   | Optional: field-level validation errors |

## HTTP Status Codes

### Success

| Code  | Meaning | When                         |
| ----- | ------- | ---------------------------- |
| `200` | OK      | GET, PATCH, DELETE succeeded |
| `201` | Created | POST created a new resource  |

### Client Errors

| Code  | Name             | Meaning                                     |
| ----- | ---------------- | ------------------------------------------- |
| `400` | BadRequest       | Invalid parameters or validation failure    |
| `401` | NotAuthenticated | Missing or invalid authentication           |
| `403` | Forbidden        | Authenticated but lacking permission        |
| `404` | NotFound         | Resource doesn't exist                      |
| `405` | MethodNotAllowed | HTTP method not supported for this endpoint |
| `408` | Timeout          | Request took too long                       |
| `409` | Conflict         | Resource already exists or state conflict   |
| `422` | Unprocessable    | Request understood but semantically invalid |
| `429` | TooManyRequests  | Rate limit exceeded                         |

### Server Errors

| Code  | Name         | Meaning                                 |
| ----- | ------------ | --------------------------------------- |
| `500` | GeneralError | Internal server error                   |
| `502` | BadGateway   | Upstream service (exchange) unreachable |
| `503` | Unavailable  | Service temporarily unavailable         |

## Common Error Scenarios

<AccordionGroup>
  <Accordion title="401 — Token Expired">
    ```json theme={null}
    {
      "name": "NotAuthenticated",
      "message": "jwt expired",
      "code": 401,
      "className": "not-authenticated"
    }
    ```

    **Fix:** Re-authenticate with `POST /authentication` to get a fresh token.
  </Accordion>

  <Accordion title="403 — Resource Not Owned">
    ```json theme={null}
    {
      "name": "Forbidden",
      "message": "You do not have permission to access this resource",
      "code": 403,
      "className": "forbidden"
    }
    ```

    **Fix:** You can only access your own resources. Check the resource ID.
  </Accordion>

  <Accordion title="400 — Validation Error">
    ```json theme={null}
    {
      "name": "BadRequest",
      "message": "Validation failed",
      "code": 400,
      "className": "bad-request",
      "errors": [
        {"field": "pair", "message": "'pair' is required"},
        {"field": "timeframe", "message": "must be one of: 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w"}
      ]
    }
    ```

    **Fix:** Check the `errors` array for specific field issues.
  </Accordion>

  <Accordion title="429 — Rate Limited">
    ```json theme={null}
    {
      "name": "TooManyRequests",
      "message": "Rate limit exceeded. Try again in 30 seconds.",
      "code": 429,
      "className": "too-many-requests"
    }
    ```

    **Fix:** Implement exponential backoff. Check `Retry-After` header.
  </Accordion>
</AccordionGroup>

## Rate Limits

### Limits by Plan

| Plan       | Requests/min | Burst | WebSocket connections |
| ---------- | ------------ | ----- | --------------------- |
| Free       | 60           | 10    | 1                     |
| Pro        | 300          | 50    | 5                     |
| Enterprise | 1000         | 100   | 25                    |

### Rate Limit Headers

Every response includes rate limit information:

```http theme={null}
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1705316460
Retry-After: 30
```

| Header                  | Description                             |
| ----------------------- | --------------------------------------- |
| `X-RateLimit-Limit`     | Max requests per window                 |
| `X-RateLimit-Remaining` | Remaining requests in current window    |
| `X-RateLimit-Reset`     | Unix timestamp when the window resets   |
| `Retry-After`           | Seconds to wait (only on 429 responses) |

### Endpoint-Specific Limits

Some endpoints have stricter limits:

| Endpoint                    | Limit  | Reason                          |
| --------------------------- | ------ | ------------------------------- |
| `POST /authentication`      | 10/min | Brute-force protection          |
| `POST /auth-management`     | 5/min  | Password reset abuse prevention |
| `POST /strategies/backtest` | 5/min  | Compute-intensive               |
| `POST /strategies/hyperopt` | 3/min  | Very compute-intensive          |
| `POST /strategies/ai`       | 10/min | AI generation cost              |

## Handling Errors in Code

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function apiRequest(url, options) {
    const response = await fetch(url, options);

    if (!response.ok) {
      const error = await response.json();

      if (error.code === 401) {
        // Token expired — refresh and retry
        const newToken = await refreshAuth();
        options.headers['Authorization'] = `Bearer ${newToken}`;
        return fetch(url, options);
      }

      if (error.code === 429) {
        // Rate limited — wait and retry
        const retryAfter = response.headers.get('Retry-After') || 30;
        await new Promise(r => setTimeout(r, retryAfter * 1000));
        return fetch(url, options);
      }

      throw new Error(`${error.name}: ${error.message}`);
    }

    return response.json();
  }
  ```

  ```python Python theme={null}
  import time
  import requests

  def api_request(url, headers, max_retries=3):
      for attempt in range(max_retries):
          response = requests.get(url, headers=headers)

          if response.status_code == 200:
              return response.json()

          if response.status_code == 401:
              headers['Authorization'] = f'Bearer {refresh_auth()}'
              continue

          if response.status_code == 429:
              retry_after = int(response.headers.get('Retry-After', 30))
              time.sleep(retry_after)
              continue

          error = response.json()
          raise Exception(f"{error['name']}: {error['message']}")

      raise Exception("Max retries exceeded")
  ```
</CodeGroup>

## Retry Strategy

For production integrations, implement exponential backoff:

```javascript theme={null}
async function withRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 429 || error.code >= 500) {
        const delay = Math.pow(2, i) * 1000 + Math.random() * 1000;
        await new Promise(r => setTimeout(r, delay));
        continue;
      }
      throw error; // Don't retry client errors (4xx except 429)
    }
  }
  throw new Error('Max retries exceeded');
}
```

<Tip>
  Never retry `400`, `403`, or `404` errors — they won't resolve by retrying. Only retry `429` (rate limit) and `5xx` (server errors).
</Tip>
