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

# Errors & Rate Limits

> Error formats, common error codes, and rate limit headers.

## Error response format

Error responses return JSON with an `error` code and `message`:

```json theme={null}
{
  "error": "SOME_ERROR_CODE",
  "message": "Human readable message"
}
```

<Note>
  The `X-Request-ID` header is **always** included on all responses. Some error responses may also include a `request_id` field in the JSON body, but this is not guaranteed for every endpoint/status code. Use the header value when contacting support.
</Note>

## Error code reference

### 401 - Authentication errors

**Invalid key format:**

```json theme={null}
{
  "error": "INVALID_KEY_FORMAT",
  "message": "Invalid API key format"
}
```

**Invalid or inactive key:**

```json theme={null}
{
  "error": "INVALID_API_KEY",
  "message": "Invalid or inactive API key"
}
```

### 422 - Validation errors

Validation errors include a `details` array with field-level issues:

```json theme={null}
{
  "error": "VALIDATION_ERROR",
  "message": "Invalid request parameters",
  "details": [
    {
      "field": "font_id",
      "message": "ensure this value is less than or equal to 90",
      "type": "value_error.number.not_le"
    }
  ]
}
```

### 429 - Rate limit exceeded

**Per-endpoint rate limit:**

```json theme={null}
{
  "error": "RATE_LIMIT_EXCEEDED",
  "message": "Rate limit exceeded for /generate: 500 requests per 1 minute(s)",
  "limit": 500,
  "endpoint": "/generate",
  "retry_after": 45
}
```

**Monthly request quota exceeded (generate only):**

```json theme={null}
{
  "error": "REQUEST_LIMIT_EXCEEDED",
  "message": "Request limit exceeded: 10000/10000 monthly requests used",
  "limit": 10000,
  "used": 10000,
  "period": "monthly",
  "reset_at": "2024-02-01T00:00:00Z"
}
```

### 500 - Server errors

Server errors include `request_id` for support:

```json theme={null}
{
  "error": "GENERATION_ERROR",
  "message": "Failed to generate image. Please try again later.",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
```

## Rate limit headers

When rate limiting applies, responses include:

| Header                  | Description                                   |
| ----------------------- | --------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the window        |
| `X-RateLimit-Remaining` | Requests remaining in current window          |
| `X-RateLimit-Reset`     | Unix timestamp when the window resets         |
| `Retry-After`           | Seconds to wait before retrying (on 429 only) |

## Per-endpoint rate limits

Rate limits are enforced per API key:

| Endpoint                |                 Limit |
| ----------------------- | --------------------: |
| `POST /api/v1/generate` | 500 requests / minute |
| `POST /api/v1/preview`  | 300 requests / minute |
| `GET /api/v1/fonts`     | 100 requests / minute |

<Note>
  `GET /api/v1/status` does not require authentication and is not subject to per-key rate limiting.
</Note>

## Request quotas (billing limits)

Accounts may have monthly request quotas. These are enforced on **`/generate` only** and return `429` with `REQUEST_LIMIT_EXCEEDED`.

## Request IDs

Every response includes the `X-Request-ID` header. You can supply your own `X-Request-ID` header on requests — the API will echo it back. This is useful for correlating requests in your logs with support inquiries.
