# Knot — full reference > An OpenAI-compatible API gateway. One key for every model, automatic failover past rate limits, and honest per-token metering on every request. Base URL: `https://tryknot.xyz/v1` Auth: `Authorization: Bearer knot_live_…` (or `x-api-key: knot_live_…`) Compatibility: implements the OpenAI REST API. Any OpenAI SDK works by changing the base URL. Streaming, tool calling and vision pass through unchanged. --- ## POST /v1/chat/completions Request body is the standard OpenAI chat completion body. ```json { "model": "openai/gpt-5.6-luna", "messages": [{ "role": "user", "content": "Hello" }], "max_tokens": 1024, "stream": true } ``` Notes: - `model` is a public Knot model ID. Upstream names are never accepted or returned. - `max_tokens` is accepted and rewritten to `max_completion_tokens` for models that require it. Send either. - When `stream: true`, `stream_options.include_usage` is forced on so the final chunk carries token counts. That chunk is what the request is billed from. - The response `model` field is always the public ID you asked for. Upstream infrastructure names are never exposed. Response headers include `x-knot-attempts`: how many deployments were tried before one answered. Greater than 1 means failover occurred. ### Billing ``` cost = (prompt_tokens - cached_tokens) x input_price + cached_tokens x cached_input_price + completion_tokens x output_price ``` All prices are per 1,000,000 tokens. Two conventions matter: - `cached_tokens` is a **subset** of `prompt_tokens`, not an addition to it. - `reasoning_tokens` is already **inside** `completion_tokens` and is never billed twice. If `cached_input_price` is unset for a model, cached tokens are billed at the full input price — never free. --- ## POST /v1/embeddings Standard OpenAI embeddings body. Billed on input tokens only. ## POST /v1/images/generations Standard OpenAI image body. Billed on the token counts the model reports — image input tokens at their own rate where one is set — so cost tracks the size and quality of what was generated rather than a flat per-image fee. A model that reports no tokens falls back to a per-image price, charged on images actually returned rather than the requested `n`, so a partial generation is not charged as a complete one. ## POST /v1/audio/transcriptions `multipart/form-data` with `file` and `model`. Billed per minute of audio. Upstream reports audio duration only when `response_format` is `verbose_json`. Knot does not silently change the requested format, so for other formats the duration is estimated from the encoded file size at an assumed 128 kbps — which runs short, erring in the caller's favour. Estimated charges are flagged in the usage log and by the `x-knot-duration-source: estimated` response header. ## POST /v1/audio/speech Standard OpenAI speech body. Billed per 1,000,000 characters of `input`, counted in code points so emoji and non-Latin scripts are not charged double. The audio is streamed back untouched. ## GET /v1/models Public, no auth. Returns the OpenAI `{ "object": "list", "data": [...] }` shape, so `client.models.list()` works, plus Knot extensions on each entry: `kind`, `context_window`, `pricing`, `supports_streaming`, `supports_tools`, `supports_vision`. Never cached — a stale price is a wrong price. ## GET /v1/credits ```json { "data": { "total_credits": 100, "total_usage": 0.001208, "remaining_credits": 99.998792 } } ``` --- ## Errors OpenAI's error envelope, so existing SDK handling works: ```json { "error": { "message": "…", "type": "…", "code": "…", "param": null } } ``` | Status | `code` | Meaning | | --- | --- | --- | | 400 | — | Malformed body, missing `model`, or a model used on the wrong endpoint | | 401 | `invalid_api_key` | Missing, unknown or revoked key | | 402 | `insufficient_credit` | Balance is zero or below | | 403 | `account_disabled` | Account disabled by an administrator | | 404 | `model_not_found` | No such public model, or it is disabled | | 503 | `no_capacity` | Every deployment for that model failed | A 4xx from the upstream model is forwarded verbatim, so the caller sees exactly what the model said rather than a paraphrase. --- ## Routing and reliability Each public model maps to an ordered pool of capacity, possibly spanning several regions. Lower priority is tried first; ties break by least-recently-used. - 429, 408 and 5xx are retried against the next deployment. - Other 4xx are returned immediately — a malformed request fails identically everywhere, so retrying only burns quota. - After 3 consecutive failures a deployment is benched for 60 seconds. If every deployment is benched, the pool is tried anyway rather than failing outright. - Failover is decided on response headers, before any body bytes reach the client, which is why streamed requests get it too. Once a stream has begun it is committed to that deployment. ## Accounts and access Invite-only. A valid invite code is required to create an account and carries the credit the account starts with. Google and GitHub sign-up carry the code through the provider redirect and redeem it on return; an account that has not redeemed one holds no credit and cannot create API keys. API keys are shown once and stored only as a SHA-256 hash. Revocation takes effect within seconds. ## Links - [Home](https://tryknot.xyz/) - [Model catalog](https://tryknot.xyz/models) - [Documentation](https://tryknot.xyz/docs) - [Security](https://tryknot.xyz/security) - [Privacy](https://tryknot.xyz/privacy) · [Terms](https://tryknot.xyz/terms)