API guides
API overview
Base URL, request format, endpoints, common headers and response basics for the Numes compatible API.
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
https://api.numes.ai/api/v1The 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; use it to generate a typed client.
Request format
JSON bodies.
POSTandPATCHroutes take a JSON body withContent-Type: application/json; a body with another content type returns415 unsupported_media_type. Transcriptions also acceptmultipart/form-data, and encrypted chat takes the SDK's encrypted body.Closed request objects. An unknown field anywhere in the body, including inside
provider,reasoningorresponse_format, returns400 invalid_request. Parameters lists every accepted field.nullmeans omitted. Nullable fields such astemperature,max_tokensorstoptreatnulllike 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.
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.
curl -X POST "https://api.numes.ai/api/v1/chat/completions" \ -H "Authorization: Bearer $NUMES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-6-luna", "messages": [ { "role": "user", "content": "Summarize the CAP theorem in one sentence." } ] }'Endpoints
| Method | Path | Key | Purpose |
|---|---|---|---|
GET | /models | Inference | List models your key can use, priced in its currency |
GET | /models/{author}/{slug}/endpoints | Inference | List the provider endpoints that serve one model |
POST | /chat/completions | Inference | Chat completion, buffered or streamed |
GET | /generation | Inference | Usage and cost of one request |
POST | /e2ee/resolve | Inference | Resolve an encrypted model before encrypting a request |
POST | /e2ee/chat/completions | Inference | Encrypted chat completion, sent through an SDK |
POST | /embeddings | Inference | Text and multimodal embeddings |
POST | /images | Inference | Image generation, buffered or streamed |
POST | /audio/speech | Inference | Text to speech, streamed audio bytes |
POST | /audio/transcriptions | Inference | Speech to text |
GET | /key | Either | Metadata, limit and usage of the calling key |
GET | /keys | Management | List inference keys |
POST | /keys | Management | Create an inference key |
GET | /keys/{hash} | Management | Get one inference key |
PATCH | /keys/{hash} | Management | Rename, disable or change the limit of a key |
DELETE | /keys/{hash} | Management | Delete a key |
GET | /credits | Management | Credits and usage totals in one currency |
Paths are relative to https://api.numes.ai/api/v1. 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. |
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. 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.
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:
{ "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 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.
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. |
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. 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:
1means 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.