# API overview

> Base URL, request format, endpoints, common headers and response basics for the Numes compatible API.

Source: https://0.0.0.0:3000/docs/api-reference/overview
Documentation index: https://0.0.0.0:3000/docs/llms.txt

The Numes API follows the OpenAI Chat Completions shape and the OpenRouter API conventions, so most existing clients work after you change the base URL and key. This page covers what every request has in common. The endpoint pages in this tab are generated from the same OpenAPI contract the server is built against.

## Base URL and versioning

```text
https://api.numes.ai/api/v1
```

The major version is part of the path (`/api/v1`). New optional response fields can appear at any time, so ignore response fields you don't recognize. Request objects are closed: a field the API doesn't implement is rejected, not ignored.

The contract is available as an OpenAPI 3.1 document at [/docs/openapi.json](/docs/openapi.json); use it to generate a typed client.

## Request format

- **JSON bodies.** `POST` and `PATCH` routes take a JSON body with `Content-Type: application/json`; a body with another content type returns `415 unsupported_media_type`. [Transcriptions](/docs/api-reference/media/create-transcription) also accept `multipart/form-data`, and [encrypted chat](/docs/api-reference/encrypted-inference/create-encrypted-chat-completion) takes the SDK's encrypted body.
- **Closed request objects.** An unknown field anywhere in the body, including inside `provider`, `reasoning` or `response_format`, returns `400 invalid_request`. [Parameters](/docs/api-reference/parameters) lists every accepted field.
- **`null` means omitted.** Nullable fields such as `temperature`, `max_tokens` or `stop` treat `null` like leaving the field out.
- **Body size.** Each route has a body limit, from 32 KiB for key management to 25 MiB for transcriptions. See [Limits](/docs/api-reference/limits#request-body-size).
- **Encoding.** Bodies are UTF-8. Timestamps are RFC 3339 strings, except where OpenAI compatibility requires Unix seconds (`created`).
- **Server-side only.** Browser requests from third-party origins are rejected with `403 cors_forbidden`. Call the API from your backend and keep keys out of browser code.

```request
{
  "method": "POST",
  "path": "/chat/completions",
  "body": {
    "model": "openai/gpt-6-luna",
    "messages": [{ "role": "user", "content": "Summarize the CAP theorem in one sentence." }]
  }
}
```

## Endpoints

| Method | Path | Key | Purpose |
| --- | --- | --- | --- |
| `GET` | [`/models`](/docs/api-reference/models/list-models) | Inference | List models your key can use, priced in its currency |
| `GET` | [`/models/{author}/{slug}/endpoints`](/docs/api-reference/models/list-model-endpoints) | Inference | List the provider endpoints that serve one model |
| `POST` | [`/chat/completions`](/docs/api-reference/chat/create-chat-completion) | Inference | Chat completion, buffered or streamed |
| `GET` | [`/generation`](/docs/api-reference/chat/get-generation) | Inference | Usage and cost of one request |
| `POST` | [`/e2ee/resolve`](/docs/api-reference/encrypted-inference/resolve-encrypted-model) | Inference | Resolve an encrypted model before encrypting a request |
| `POST` | [`/e2ee/chat/completions`](/docs/api-reference/encrypted-inference/create-encrypted-chat-completion) | Inference | Encrypted chat completion, sent through an SDK |
| `POST` | [`/embeddings`](/docs/api-reference/embeddings/create-embedding) | Inference | Text and multimodal embeddings |
| `POST` | [`/images`](/docs/api-reference/media/create-image) | Inference | Image generation, buffered or streamed |
| `POST` | [`/audio/speech`](/docs/api-reference/media/create-speech) | Inference | Text to speech, streamed audio bytes |
| `POST` | [`/audio/transcriptions`](/docs/api-reference/media/create-transcription) | Inference | Speech to text |
| `GET` | [`/key`](/docs/api-reference/keys/get-current-key) | Either | Metadata, limit and usage of the calling key |
| `GET` | [`/keys`](/docs/api-reference/keys/list-keys) | Management | List inference keys |
| `POST` | [`/keys`](/docs/api-reference/keys/create-key) | Management | Create an inference key |
| `GET` | [`/keys/{hash}`](/docs/api-reference/keys/get-key) | Management | Get one inference key |
| `PATCH` | [`/keys/{hash}`](/docs/api-reference/keys/update-key) | Management | Rename, disable or change the limit of a key |
| `DELETE` | [`/keys/{hash}`](/docs/api-reference/keys/delete-key) | Management | Delete a key |
| `GET` | [`/credits`](/docs/api-reference/credits/get-credits) | Management | Credits and usage totals in one currency |

Paths are relative to `https://api.numes.ai/api/v1`. [Authentication](/docs/api-reference/authentication) explains the two key kinds.

An unknown path returns a plain `404`, and a known path with the wrong method returns `405` with an `Allow` header. Neither uses the JSON error envelope.

## Common headers

### Request headers

| Header | Applies to | Behavior |
| --- | --- | --- |
| `Authorization` | All routes | `Bearer <key>`. Required. See [Authentication](/docs/api-reference/authentication). |
| `Content-Type` | `POST`, `PATCH` | `application/json`, or `multipart/form-data` for transcriptions. |
| `X-Request-Id` | All routes | Optional correlation ID, 1–128 characters from `A-Z a-z 0-9 . _ -`. Must be unique per request in your account. |
| `X-Session-Id` | Chat | Optional sticky-routing key, at most 256 characters. A body `session_id` takes precedence. |
| `X-OpenRouter-Metadata` | Chat | `enabled` or `true` adds `openrouter_metadata` to the response. |
| `HTTP-Referer` | Inference routes | Optional. Groups your traffic by app in [Activity](/dashboard/activity). Only the public origin is kept. |
| `X-Title` | Inference routes | Accepted for compatibility and ignored. |

**`X-Request-Id`.** A valid value is used as sent; otherwise the API generates a 32-character hex ID. On inference routes the ID is recorded with the request, and reusing an ID already recorded in your account returns `400 invalid_request`, even for a retry of a failed request. It isn't an idempotency key: send a fresh ID on every call, including retries, or omit the header. See [Request and generation IDs](/docs/api-reference/errors#request-and-generation-ids).

**`X-OpenRouter-Metadata`.** The value is case-insensitive; anything other than `enabled` or `true` opts out. `X-OpenRouter-Experimental-Metadata` is accepted as an alias; when both are sent, `X-OpenRouter-Metadata` wins. Buffered chat responses and the usage chunk of a stream then include:

```json
{
  "openrouter_metadata": {
    "generation_id": "0f5b5c9a-3a7e-4c6f-9d7b-2e1f0a6b8c4d",
    "request_id": "checkout-summary-7f3a",
    "outcome": "success",
    "cancelled": false,
    "delivered_output": true,
    "provider": "Example Provider",
    "provider_slug": "example-provider",
    "endpoint": "example-provider/primary"
  }
}
```

### Response headers

| Header | Behavior |
| --- | --- |
| `X-Request-Id` | Your value or a generated one, on every response. Quote it when you contact support. |
| `X-Generation-Id` | The generation ID, on inference responses once output starts. Pass it to `GET /generation`. |
| `Retry-After` | Whole seconds to wait, on `429 rate_limited` responses from your rate limits or from repeated failed authentications. |
| `Cache-Control` | `no-store` on JSON responses, `no-cache` on event streams. |

## Responses

A buffered chat completion returns one JSON object; the [Create chat completion](/docs/api-reference/chat/create-chat-completion) reference lists every field. `choices` always has exactly one entry; there's no `n` parameter. Streams carry the same data as a sequence of chunks; see [Streaming](/docs/features/streaming).

### Finish reasons

| `finish_reason` | Meaning |
| --- | --- |
| `stop` | The model finished or hit a stop sequence. Also used when the provider reports no reason. |
| `length` | The output reached the output limit or a reasoning limit. |
| `tool_calls` | The model stopped to call one or more tools. Some endpoints report `stop` instead, so check for tool calls too. |
| `content_filter` | The provider filtered the output. |
| `other` | The provider reported a reason with no standard mapping. |
| `error` | Streams only: the stream ended with an [in-band error](/docs/features/streaming#errors-after-streaming-starts). |

Treat any other value as a normal completion.

## Look up cost and stats

Every buffered response includes `usage` with the request's `cost`, and every successful stream ends with a usage chunk. For the authoritative record, for example after a dropped connection, pass the generation ID from the `X-Generation-Id` header or the response `id` to [`GET /generation`](/docs/api-reference/chat/get-generation). [Usage accounting](/docs/account/usage-accounting) explains each usage field.

## Money

Every inference key has one billing currency, `USD` or `BTC`, fixed when the key is created. All prices, costs and limits the API returns for that key are in that currency, and there's no conversion between currencies.

| Field | Representation |
| --- | --- |
| `pricing` in `/models` and the endpoints list | Exact decimal strings per unit (per token for text), for example `"0.0000004"` |
| `usage.cost`, `total_cost`, key `limit` and `usage` | JSON numbers written from exact decimals, for example `0.000007` |
| `provider.max_price` | JSON numbers per unit, in the key's currency |

- **USD** amounts are dollars.
- **BTC** amounts are satoshis, not bitcoin: `1` means one satoshi, and costs can be fractional satoshis.
- JSON numbers are exact as written, but most parsers read them as binary floats. Parse costs as decimals before adding them, for example `json.loads(body, parse_float=Decimal)` in Python.

## Next steps

:::cards
- {key} [Authentication](/docs/api-reference/authentication) Key kinds, header format and failure codes.
- {braces} [Parameters](/docs/api-reference/parameters) Every chat completion field with ranges and defaults.
- {waves} [Streaming](/docs/features/streaming) Stream chunks, the usage chunk and in-band errors.
- {alert} [Errors and debugging](/docs/api-reference/errors) Status codes, error types and retry strategy.
- {gauge} [Limits](/docs/api-reference/limits) Rate limits, body sizes and spending limits.
- {plug} [OpenRouter compatibility](/docs/api-reference/compatibility) What differs from OpenRouter and how to migrate.
:::
