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

# API reference

> Base URL, authentication, error envelope, and spend headers for api.withhopper.com

All requests go to one host:

```
https://api.withhopper.com
```

OpenAI-compatible routes live under `/v1` (`/v1/chat/completions`, `/v1/audio/speech`, `/v1/audio/transcriptions`). Native routes sit at the root: `/tts/*`, `/voices`, `/pronunciation-dicts`, `/jobs`.

## Authentication

Every request carries a key in the `Authorization` header:

```
Authorization: Bearer sk_hopper_...
```

Keys are minted in the [dashboard](https://withhopper.com/console/keys) and shown once at creation. WebSocket connections also accept the key as a query parameter at upgrade: `wss://api.withhopper.com/stt/websocket?api_key=sk_hopper_...`.

## Errors

Every error uses the OpenAI envelope, so OpenAI SDK error handling works unchanged. A request without a key returns 401:

```json theme={null}
{
  "error": {
    "message": "You didn't provide an API key. Get one at https://withhopper.com/dashboard and pass it via the Authorization header: 'Authorization: Bearer sk_hopper_...'.",
    "type": "invalid_request_error",
    "code": "invalid_api_key",
    "param": null
  }
}
```

At zero balance, metered routes return 402 `insufficient_quota`; a streaming STT session closes with code 1008. Full per-endpoint codes are on each endpoint page and in [errors](/platform/errors).

## Spend headers

Metered responses report cost as 6-decimal USD strings:

| Header                           | Value                      |
| -------------------------------- | -------------------------- |
| `x-hopper-request-cost-usd`      | Cost of this request       |
| `x-hopper-credits-remaining-usd` | Balance after this request |

LLM streaming responses carry only `x-hopper-credits-remaining-usd`, the pre-request balance; the cost is unknown until the stream ends.

## Rate limits

600 requests per minute per key. Past the limit, 429 with a `Retry-After` header giving the seconds to wait.

To make a first request, follow the [quickstart](/quickstart).
