> ## 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.

# HTTP Headers

> Request and response headers used by the HandTextAI API.

## Request headers

### Authorization (required)

```http theme={null}
Authorization: Bearer htext_your_api_key
```

Required for all endpoints except `GET /api/v1/status`.

### X-Request-ID (optional)

```http theme={null}
X-Request-ID: your-unique-request-id
```

Supply your own request identifier for correlation. The API echoes it back in the response. If omitted, the API generates a UUID.

**Recommended format:** `{app-name}-{uuid}` or plain UUID.

***

## Response headers

### X-Request-ID

Always present. Contains either your supplied ID or an API-generated UUID.

```http theme={null}
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
```

### X-Process-Time

Processing time in seconds (3 decimal places):

```http theme={null}
X-Process-Time: 0.523
```

### Rate limit headers

Present on authenticated endpoints after the rate-limit check runs:

| Header                  | Description             | Example      |
| ----------------------- | ----------------------- | ------------ |
| `X-RateLimit-Limit`     | Max requests in window  | `500`        |
| `X-RateLimit-Remaining` | Requests left in window | `499`        |
| `X-RateLimit-Reset`     | Unix timestamp of reset | `1704067200` |

<Note>
  If a `/generate` request is rejected due to a **monthly quota** (`REQUEST_LIMIT_EXCEEDED`), the response may not include `X-RateLimit-*` headers because quota checks happen before rate limiting.
</Note>

### Retry-After

Only on **rate-limit** 429 responses (`RATE_LIMIT_EXCEEDED`). Seconds to wait before retrying:

```http theme={null}
Retry-After: 45
```

For **quota** 429 responses (`REQUEST_LIMIT_EXCEEDED`), do not retry immediately — wait for the quota reset or increase your quota.

### Content-Disposition (PDF only)

For PDF responses, includes a suggested filename:

```http theme={null}
Content-Disposition: inline; filename="handtext_a4_300dpi.pdf"
```

***

## Handling rate limits

When you receive a `429` response:

1. Check the JSON `error` field:
   * `RATE_LIMIT_EXCEEDED`: read `Retry-After` (seconds) and retry after waiting
   * `REQUEST_LIMIT_EXCEEDED`: you’re out of quota — wait for reset or upgrade
2. Implement exponential backoff for repeated `RATE_LIMIT_EXCEEDED` responses

**Example retry logic (pseudocode):**

```python theme={null}
def request_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.post(url, headers=headers)

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

        return response

    raise Exception("Max retries exceeded")
```

***

## Header summary

| Header                  | Direction | Required | Description                 |
| ----------------------- | --------- | -------- | --------------------------- |
| `Authorization`         | Request   | Yes\*    | Bearer token authentication |
| `X-Request-ID`          | Both      | No       | Request correlation ID      |
| `X-Process-Time`        | Response  | —        | Server processing time      |
| `X-RateLimit-Limit`     | Response  | —        | Rate limit ceiling          |
| `X-RateLimit-Remaining` | Response  | —        | Remaining requests          |
| `X-RateLimit-Reset`     | Response  | —        | Window reset timestamp      |
| `Retry-After`           | Response  | —        | Wait time (429 only)        |
| `Content-Disposition`   | Response  | —        | Filename (PDF only)         |

\*Not required for `GET /api/v1/status`
