# Relaykit documentation (full text) > An OpenAI- and Anthropic-compatible API for Claude, GPT, Grok and DeepSeek. Prepaid credits, published per-token rates, per-key limits and ready setups for coding agents. Index: https://oneaiapikey.com/llms.txt --- # Documentation > Everything you need to call Claude, GPT, Grok and DeepSeek through one OpenAI- and Anthropic-compatible API: quickstart, API reference, errors and tool guides. Source: https://oneaiapikey.com/docs Relaykit is a prepaid gateway that puts many models behind one API key. If your code or tool already talks to the OpenAI or Anthropic API, you change two things: the **base URL** and the **API key**. Request bodies, SDKs and streaming stay the same. ## Start here 1. [Quickstart: Create a key and make your first request with curl, Python or Node.](https://oneaiapikey.com/docs/quickstart) 2. [API reference: Endpoints, headers, request and response shapes, streaming, tool calling and usage fields.](https://oneaiapikey.com/docs/api-reference) 3. [Migrating: Coming from the OpenAI API, the Anthropic API or another gateway: what to change and what differs.](https://oneaiapikey.com/docs/migrate) 4. [Errors: What each status code means, how to fix it, and a retry pattern that does not waste credit.](https://oneaiapikey.com/docs/errors) 5. [Tool guides: Exact settings for 8 coding tools, including Claude Code, Codex CLI, Cursor.](https://oneaiapikey.com/docs/guides) 6. [Models and rates: 11 models across Claude, GPT, Grok, DeepSeek, with exact ids and per-token prices.](https://oneaiapikey.com/models) ## How it works Your app sends a normal OpenAI- or Anthropic-format request with your key. The gateway checks the key and your balance, forwards the request to the model you named, returns the answer in the format you used, and deducts the tokens used at that model's published rate. The response comes back in the same format you sent. Its `usage` object lists the tokens you are charged for. ## Base URLs at a glance | Format | Base URL | Auth header | | --- | --- | --- | | OpenAI | `https://api.oneaiapikey.com/v1` | `Authorization: Bearer sk-…` | | Anthropic | `https://api.oneaiapikey.com` | `x-api-key: sk-…` | > **The /v1 rule:** OpenAI SDKs expect the base URL to _include_ `/v1`. Anthropic SDKs and Claude Code expect it _without_ `/v1`, because they append `/v1/messages` themselves. Most 404 errors come from mixing these up. ## Billing basics - Billing is prepaid. One credit is one US dollar of usage at the per-model rates on the [models page](https://oneaiapikey.com/models). - New accounts start at $0, with no sign-up credit. Models become available once the account has paid $3 or more; see [billing](https://oneaiapikey.com/console/billing). - Nothing renews on its own. Optional plans are opening soon. See [Pricing and billing](https://oneaiapikey.com/docs/billing). - Each request is charged for the input and output tokens it used. Requests the gateway rejects up front (invalid key, empty balance, unknown model) are not charged. - Your [keys](https://oneaiapikey.com/console/keys), [per-request usage log](https://oneaiapikey.com/console/usage) and [balance](https://oneaiapikey.com/console/billing) live in the [console](https://oneaiapikey.com/console). ## Tool guides Prefer a ready-made coding tool to writing code? Each guide gives the exact fields to fill in. - [Claude Code](https://oneaiapikey.com/docs/guides/claude-code) - [Codex CLI](https://oneaiapikey.com/docs/guides/codex-cli) - [Cursor](https://oneaiapikey.com/docs/guides/cursor) - [Cline](https://oneaiapikey.com/docs/guides/cline) - [Kilo Code](https://oneaiapikey.com/docs/guides/kilo-code) - [OpenCode](https://oneaiapikey.com/docs/guides/opencode) - [Aider](https://oneaiapikey.com/docs/guides/aider) - [Zed](https://oneaiapikey.com/docs/guides/zed) - [All guides](https://oneaiapikey.com/docs/guides) ## All topics ### Get started - [Quickstart](https://oneaiapikey.com/docs/quickstart) - [Authentication and keys](https://oneaiapikey.com/docs/authentication) - [Models](https://oneaiapikey.com/docs/models) - [Chat in the browser](https://oneaiapikey.com/docs/chat) ### Build - [Chat completions](https://oneaiapikey.com/docs/chat-completions) - [Anthropic Messages](https://oneaiapikey.com/docs/anthropic-messages) - [Responses API](https://oneaiapikey.com/docs/responses) - [Streaming](https://oneaiapikey.com/docs/streaming) - [Tool calling](https://oneaiapikey.com/docs/tool-calling) - [MCP servers](https://oneaiapikey.com/docs/mcp) - [Vision and files](https://oneaiapikey.com/docs/vision) - [Structured output](https://oneaiapikey.com/docs/structured-output) - [Reasoning models](https://oneaiapikey.com/docs/reasoning) ### Optimize - [Prompt caching](https://oneaiapikey.com/docs/prompt-caching) - [Cost control](https://oneaiapikey.com/docs/cost-control) - [Rate limits and retries](https://oneaiapikey.com/docs/rate-limits) - [Errors](https://oneaiapikey.com/docs/errors) - [Performance metrics](https://oneaiapikey.com/docs/performance) - [Routing and privacy](https://oneaiapikey.com/docs/routing-privacy) - [Migrating to Relaykit](https://oneaiapikey.com/docs/migrate) ### Account and billing - [Pricing and billing](https://oneaiapikey.com/docs/billing) - [Account security and alerts](https://oneaiapikey.com/docs/account-security) ### Apps and SDKs - [SDKs and frameworks](https://oneaiapikey.com/docs/sdks) - [Open WebUI](https://oneaiapikey.com/docs/apps/open-webui) - [LibreChat](https://oneaiapikey.com/docs/apps/librechat) - [LobeChat](https://oneaiapikey.com/docs/apps/lobechat) - [SillyTavern](https://oneaiapikey.com/docs/apps/sillytavern) - [Chatbox](https://oneaiapikey.com/docs/apps/chatbox) ### Reference - [API reference](https://oneaiapikey.com/docs/api-reference) - [Docs for AI tools](https://oneaiapikey.com/docs/llms) - [Changelog](https://oneaiapikey.com/changelog) --- # Quickstart > Create an API key, then send your first request with curl, Python or Node, stream tokens as they arrive and list every model your key can call. Source: https://oneaiapikey.com/docs/quickstart This page takes you from zero to a working response. You will use the same key for every model, through either the OpenAI-compatible or the Anthropic-compatible endpoint. Pick whichever SDK you already know. ## 1. Create an account [Sign up](https://oneaiapikey.com/console/sign-up) with your email. New accounts start at $0; add at least **$3** of prepaid credit on the [billing page](https://oneaiapikey.com/console/billing) before your first request (top-ups are $3 or more). Pay as you go needs no subscription and nothing renews automatically;optional plans are opening soon (see [pricing](https://oneaiapikey.com/pricing)). ## 2. Create and store a key 1. Open [API keys](https://oneaiapikey.com/console/keys) in the console. 2. Create a key. Give it a name per app or machine so you can revoke one without breaking the others. You can also set a spending cap per key. 3. Copy the key. It starts with `sk-`. Store it in an environment variable instead of pasting it into code. The examples below read `RELAYKIT_API_KEY`. macOS / Linux: ```bash export RELAYKIT_API_KEY="sk-your-key" ``` PowerShell: ```powershell # Current window only $env:RELAYKIT_API_KEY = "sk-your-key" # Persist for new terminals (then open a new window) setx RELAYKIT_API_KEY "sk-your-key" ``` Git Bash: ```bash echo 'export RELAYKIT_API_KEY="sk-your-key"' >> ~/.bashrc source ~/.bashrc ``` > **Keep keys server-side:** Anyone with your key can spend your balance. Never ship it inside a browser bundle or mobile app, never commit it to Git, and revoke it under [API keys](https://oneaiapikey.com/console/keys) straight away if it leaks. ## 3. Send your first request The OpenAI-format base URL is `https://api.oneaiapikey.com/v1`. The Anthropic-format base URL is `https://api.oneaiapikey.com` (no `/v1`, because the Anthropic SDK adds it). Both accept the same key. curl: ```bash curl https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "messages": [{"role": "user", "content": "Explain what an API gateway does in one sentence."}] }' ``` Python: ```python import os from openai import OpenAI client = OpenAI( base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"], ) resp = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Explain what an API gateway does in one sentence."}], ) print(resp.choices[0].message.content) ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY, }); const resp = await client.chat.completions.create({ model: "gpt-5.6-terra", messages: [{ role: "user", content: "Explain what an API gateway does in one sentence." }], }); console.log(resp.choices[0].message.content); ``` Anthropic SDK: ```python import os import anthropic client = anthropic.Anthropic( base_url="https://api.oneaiapikey.com", # no /v1 here api_key=os.environ["RELAYKIT_API_KEY"], ) msg = client.messages.create( model="claude-sonnet-5-5", max_tokens=512, messages=[{"role": "user", "content": "Explain what an API gateway does in one sentence."}], ) print(msg.content[0].text) ``` PowerShell: ```powershell $body = @{ model = "gpt-5.6-terra" messages = @(@{ role = "user"; content = "Explain what an API gateway does in one sentence." }) } | ConvertTo-Json -Depth 5 $resp = Invoke-RestMethod -Method Post -Uri "https://api.oneaiapikey.com/v1/chat/completions" ` -Headers @{ Authorization = "Bearer $env:RELAYKIT_API_KEY" } ` -ContentType "application/json" -Body $body $resp.choices[0].message.content ``` A successful call returns JSON with the model's reply plus a `usage` object that shows exactly how many tokens you were charged for. To try a different model, change only the `model` field. Claude models also work through the OpenAI endpoint, so you can compare `gpt-5.6-terra` and `claude-sonnet-5-5` without switching SDKs. ## 4. Stream the response Streaming sends tokens as soon as they are generated, so users see output immediately. It is also the most reliable way to run long generations: idle non-streaming connections can be closed by proxies after about 100 seconds, while a stream keeps the connection active. curl: ```bash curl -N https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "stream": true, "stream_options": {"include_usage": true}, "messages": [{"role": "user", "content": "Write a haiku about latency."}] }' ``` Python: ```python stream = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Write a haiku about latency."}], stream=True, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` Node: ```js const stream = await client.chat.completions.create({ model: "gpt-5.6-terra", messages: [{ role: "user", content: "Write a haiku about latency." }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); } ``` Anthropic SDK: ```python with client.messages.stream( model="claude-sonnet-5-5", max_tokens=1024, messages=[{"role": "user", "content": "Write a haiku about latency."}], ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` > **Rule of thumb:** Use `stream: true` for anything that may produce more than a few hundred tokens, for reasoning models and for agent loops. With `stream_options.include_usage` the final chunk carries the token usage. ## 5. List available models `GET /v1/models` returns every model id your key can call. Use these exact ids in the `model` field. Prices per million tokens are on the [models page](https://oneaiapikey.com/models). curl: ```bash curl https://api.oneaiapikey.com/v1/models \ -H "Authorization: Bearer $RELAYKIT_API_KEY" ``` Python: ```python for model in client.models.list(): print(model.id) ``` Node: ```js for await (const model of client.models.list()) { console.log(model.id); } ``` Response (shortened): ```json { "object": "list", "data": [ { "id": "claude-sonnet-5-5", "object": "model" }, { "id": "gpt-5.6-terra", "object": "model" } ] } ``` ## Use it in coding tools Coding agents and editors only need the base URL and your key. Tools that speak the Anthropic format (like Claude Code) use `https://api.oneaiapikey.com`; everything else uses `https://api.oneaiapikey.com/v1`. - [Claude CodeCLI · Anthropic format](https://oneaiapikey.com/docs/guides/claude-code) - [Codex CLICLI · OpenAI format](https://oneaiapikey.com/docs/guides/codex-cli) - [CursorIDE · OpenAI format](https://oneaiapikey.com/docs/guides/cursor) - [ClineExtension · OpenAI format](https://oneaiapikey.com/docs/guides/cline) - [Kilo CodeExtension · OpenAI format](https://oneaiapikey.com/docs/guides/kilo-code) - [OpenCodeCLI · OpenAI format](https://oneaiapikey.com/docs/guides/opencode) - [AiderCLI · OpenAI format](https://oneaiapikey.com/docs/guides/aider) - [ZedIDE · OpenAI format](https://oneaiapikey.com/docs/guides/zed) ## If something fails - **401 Unauthorized**: the key is missing, mistyped or revoked. Check that the environment variable is set in the same terminal. - **404 Not Found**: almost always the base URL. OpenAI SDKs need `/v1`; Anthropic SDKs must not have it. - **403 or "insufficient quota"**: your balance or the key's spending cap is used up. [Top up](https://oneaiapikey.com/console/billing) or raise the cap under [API keys](https://oneaiapikey.com/console/keys). - **Model not found**: copy the id from `GET /v1/models`. Ids are case-sensitive. The full list, with fixes and a safe retry pattern, is in [Errors & limits](https://oneaiapikey.com/docs/errors). ## Next steps - [API reference](https://oneaiapikey.com/docs/api-reference): every endpoint, header and field the gateway supports. - [Tool guides](https://oneaiapikey.com/docs/guides): exact settings for each coding tool. - [Models & rates](https://oneaiapikey.com/models): compare price and strengths before choosing a default model. --- # Authentication and API keys > How to send your API key in each format, which headers are accepted, how per-key spend limits, expiry dates and model allowlists work, and how to rotate keys safely. Source: https://oneaiapikey.com/docs/authentication Every request to the API carries one secret key. The same key works for every model and for both the OpenAI and the Anthropic request formats, so one key per app or machine is enough. OpenAI SDKs `https://api.oneaiapikey.com/v1` Anthropic SDKs `https://api.oneaiapikey.com` ## Get a key 1. Sign in and open [API keys](https://oneaiapikey.com/console/keys) in the console. 2. Choose **Create key**, give it a name (up to 50 characters) and, if you want, a spend limit, an expiry date or a model list. All three are optional and are explained below. 3. Copy the key from the dialog. It starts with `sk-`. **The console shows the full key only once.** If you lose it, revoke it and create a new one. You can hold up to 1,000 keys per account. Name each key after the app, machine or teammate that uses it, so you can revoke one without breaking the others. ## Send the key | Header | Accepted on | Used by | | --- | --- | --- | | `Authorization: Bearer sk-…` | Every endpoint | OpenAI SDKs, LangChain, Vercel AI SDK, most tools | | `x-api-key: sk-…` | `/v1/messages` and `/v1/models` only | Anthropic SDKs, Claude Code | The gateway accepts the key with or without the `sk-` prefix. Copying it exactly as the console shows it avoids mistakes. curl: ```bash # OpenAI format: Bearer token curl https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-5.6-terra", "messages": [{"role": "user", "content": "Hi"}]}' # Anthropic format: x-api-key (Bearer works too) curl https://api.oneaiapikey.com/v1/messages \ -H "x-api-key: $RELAYKIT_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{"model": "claude-sonnet-5-5", "max_tokens": 256, "messages": [{"role": "user", "content": "Hi"}]}' ``` Python: ```python import os from openai import OpenAI import anthropic # The SDK sends "Authorization: Bearer " openai_client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) # The SDK sends "x-api-key: "; no /v1 in the base URL claude_client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) ``` Node: ```js import OpenAI from "openai"; import Anthropic from "@anthropic-ai/sdk"; const openai = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); // No /v1 in the base URL: the SDK adds /v1/messages itself const claude = new Anthropic({ baseURL: "https://api.oneaiapikey.com", apiKey: process.env.RELAYKIT_API_KEY }); ``` ## Check that a key works Listing models is free and is the quickest test. A working key returns HTTP 200 and the models it may call: Terminal: ```bash curl -s https://api.oneaiapikey.com/v1/models \ -H "Authorization: Bearer $RELAYKIT_API_KEY" ``` A missing, mistyped, disabled, expired or used-up key returns the same response. The gateway does not say which of these it is, so check the key’s status in the console: 401 response: ```http HTTP/1.1 401 Unauthorized Content-Type: application/json X-Request-Id: {"error":{"code":"","message":"Invalid token (request id: )","type":"api_error"}} ``` ## Per-key settings Set these when you create a key, or later with **Edit** on the [API keys](https://oneaiapikey.com/console/keys) page. ### Spend limit The key stops working once it has spent this many dollars. Your account balance still applies on top, so a key never spends more than the account holds. - While some limit is left but less than the gateway’s up-front estimate for a request, that request fails with HTTP 403 and the code `pre_consume_token_quota_failed`. - Once the limit is fully used, the key returns 401 `Invalid token`, like a disabled key. Raise the limit, or clear the field to remove it, and the key works again. ### Expiry date The key stops working at the end of that day, in your local time. After that it returns 401. Edit the key and pick a later date, or clear the field, to switch it back on. ### Model list Choose “Every model your account can use” (the default) or “Only the models I pick”. With a list, a request for any other model fails with HTTP 403 and the message `This token has no access to model `, and `GET /v1/models` returns only the picked models. ### Disable and revoke **Disable** stops a key without deleting it; it returns 401 until you enable it again. **Revoke** deletes the key for good. > **Leave headroom on limited keys:** The gateway estimates each request’s cost from its prompt before it runs and refuses it when the key’s remaining limit is lower than that estimate. Keys that run long agent sessions need a limit with some room to spare. ## Store keys safely - Keep keys in environment variables or a secrets manager, never in source code. The examples in these docs read `RELAYKIT_API_KEY`. - Never put a key in a browser bundle, a mobile app or a public repository. Anyone who has it can spend your balance. Call the API from your server instead. - Use one key per app, machine or teammate, with a spend limit on each, so a leak or a runaway loop costs at most that limit. - Set an expiry date on keys for short projects, demos and contractors. ## Rotate or revoke a key 1. Create a new key and deploy it alongside the old one. 2. Check the [usage page](https://oneaiapikey.com/console/usage). Once the old key shows no new requests, disable it. 3. Revoke the old key after a few days without problems. If a key leaks, disable or revoke it straight away on the [API keys](https://oneaiapikey.com/console/keys) page, then create a replacement. ## Related - [Quickstart](https://oneaiapikey.com/docs/quickstart): your first request in curl, Python and Node. - [Cost control](https://oneaiapikey.com/docs/cost-control): spend limits, cheaper models and caching together. - [Errors](https://oneaiapikey.com/docs/errors#invalid-key): every 401 and 403 the API returns, with fixes. - [Account security and alerts](https://oneaiapikey.com/docs/account-security): two-factor sign-in, IP allowlists and low-balance alerts. --- # Models and model ids > Every model id you can send, which request formats each one accepts, its per-token rates, and how to list the models your key can call from code. Source: https://oneaiapikey.com/docs/models Send a model id in the `model` field of any request. Ids are lowercase and exact: `claude-sonnet-5-5` works, `Claude Sonnet` does not. One key and one balance cover every model below. ## Current models Rates are in US dollars per 1 million tokens. “Formats” says which request formats the model accepts natively: OpenAI means `/v1/chat/completions` (the [Responses API](https://oneaiapikey.com/docs/responses) is fully supported on GPT models only), Anthropic means `/v1/messages`. Click an id for the full spec sheet: context window, maximum output, modalities and plan coverage. | Model id | Formats | Input / 1M | Output / 1M | Cached / 1M | Context | | --- | --- | --- | --- | --- | --- | | [claude-opus-5-5](https://oneaiapikey.com/models/claude-opus-5-5) | Anthropic, OpenAI | $4.40 | $22 | $0.22 | 1M | | [claude-sonnet-5](https://oneaiapikey.com/models/claude-sonnet-5) | Anthropic, OpenAI | $2.20 | $11 | $0.22 | 1M | | [claude-haiku-4-5](https://oneaiapikey.com/models/claude-haiku-4-5) | Anthropic, OpenAI | $1.10 | $5.50 | $0.11 | 200K | | [gpt-6.1-sol](https://oneaiapikey.com/models/gpt-6-1-sol) | OpenAI | $2.20 | $11 | $0.11 | 1.05M | | [gpt-5.6-sol](https://oneaiapikey.com/models/gpt-5-6-sol) | OpenAI | $5.50 | $33 | $0.55 | 1.05M | | [gpt-5.6-terra](https://oneaiapikey.com/models/gpt-5-6-terra) | OpenAI | $2.20 | $13.20 | $0.22 | 1.05M | | [grok-4.6](https://oneaiapikey.com/models/grok-4-6) | OpenAI | $2.20 | $6.60 | $0.55 | 500K | | [claude-sonnet-5-5](https://oneaiapikey.com/models/claude-sonnet-5-5) | Anthropic, OpenAI | $2.20 | $11 | $0.22 | 1M | | [grok-4.7](https://oneaiapikey.com/models/grok-4-7) | OpenAI | $2.20 | $6.60 | $0.55 | 500K | | [deepseek-v4-flash](https://oneaiapikey.com/models/deepseek-v4-flash) | OpenAI | $0.24 | $0.96 | $0.0048 | 1M | | [deepseek-v4-pro](https://oneaiapikey.com/models/deepseek-v4-pro) | OpenAI | $1.32 | $3.96 | $0.044 | 1M | The same table, with filters and comparisons, is on the [models page](https://oneaiapikey.com/models). Rates there and here come from one file, so they always match. ## Free and discounted models There are no free models at the moment. The [free models page](https://oneaiapikey.com/models/free) lists them when there are. ## List models from code `GET /v1/models` returns the models your key can call right now. It is free and does not count against any limit, so it is also the quickest way to check that a key works. curl: ```bash curl -s https://api.oneaiapikey.com/v1/models \ -H "Authorization: Bearer $RELAYKIT_API_KEY" ``` Python: ```python import os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) for model in client.models.list(): print(model.id) ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); for await (const model of client.models.list()) { console.log(model.id); } ``` The response is the OpenAI list format: 200 OK (shortened): ```json { "object": "list", "success": true, "data": [ { "id": "claude-sonnet-5-5", "object": "model", "created": 1626777600, "owned_by": "…" } ] } ``` The list is filtered for you: - If the key has a model list (see [per-key settings](https://oneaiapikey.com/docs/authentication#model-list)), only those models appear. - Models that are not available to your account, or not yet priced, are left out. An account that has not paid $3 or more in total yet sees an empty list. ### Anthropic-style list Send both `x-api-key` and `anthropic-version` and the same route answers in Anthropic’s format, which is what Anthropic SDKs and Claude Code expect: Terminal: ```bash curl -s https://api.oneaiapikey.com/v1/models \ -H "x-api-key: $RELAYKIT_API_KEY" \ -H "anthropic-version: 2023-06-01" # {"data":[{"id":"claude-sonnet-5-5","created_at":"…","display_name":"claude-sonnet-5-5","type":"model"}], # "first_id":"…","has_more":false,"last_id":"…"} ``` > **Use the list, not the single-model route:** `GET /v1/models/{id}` exists for SDK compatibility, but it checks a built-in catalogue rather than your account and answers HTTP 200 even when the model is unknown. To check whether a model is available to your key, look for it in `GET /v1/models`. ## Choosing a model - **Start with a balanced model** such as `claude-sonnet-5-5` or `gpt-5.6-terra`. Move up to a flagship only when quality is the bottleneck. - **Use a fast model for sub-tasks**: classification, summaries, commit messages and agent sub-steps. `claude-haiku-4-5` costs a fraction of a flagship. - **Output tokens cost more than input tokens** on every paid model, usually three to five times as much. Asking for shorter answers saves more than trimming the prompt. - **Long, repeated prompts** (agents, RAG, chat history) get cheaper with [prompt caching](https://oneaiapikey.com/docs/prompt-caching). ## When a model id fails | Response | Meaning | Fix | | --- | --- | --- | | `503` with code `model_not_found` | The id is wrong, the model is not available to your account or plan, or the account has not paid $3 or more yet | Copy the id from the table above or from `GET /v1/models` | | `403` “This token has no access to model …” | The key has a model list without this model | Edit the key, or use a key without a model list | | `400` with code `model_price_error` | The model is listed but not open for requests yet | Pick another model; it will appear in `GET /v1/models` once it is open | All error bodies are on the [errors page](https://oneaiapikey.com/docs/errors#model-not-found). --- # Chat in the browser > Use every model in a browser chat on the same account and balance as the API. Sign-in, models, editing and exporting chats, where chats are stored and how replies are billed. Source: https://oneaiapikey.com/docs/chat You do not need to write code to try the models. [Chat](https://oneaiapikey.com/chat) is a browser chat app on the same account as the API: sign in once and every model is available, billed to the same balance at the same per-token rates. ## Start a conversation 1. Open [Chat](https://oneaiapikey.com/chat). Signed out, you can already type your first message; when you send it, you are taken to [sign in](https://oneaiapikey.com/console/sign-in?next=%2Fchat) and brought back to Chat with your message still in the box. If you do not have an account yet, [sign up](https://oneaiapikey.com/console/sign-up). New accounts start at $0; models become available once the account has paid $3 or more. 2. Pick a model in the model picker at the top. It shows each model’s input and output rate. 3. Type your message and press **Enter** to send (**Shift+Enter** for a new line). The reply streams in as it is written. You can also open Chat with a model already chosen by adding `?model=` to the URL, for example from a [model page](https://oneaiapikey.com/models). ## While you chat - **Stop** (or **Esc**) ends a reply at once. The partial reply stays, marked as stopped. - **Regenerate** asks the model again for the last reply. - **Edit** on one of your messages changes it and sends it again; the replies after it are replaced. - **Copy** copies a message as text. - **Conversation settings** set a system prompt (pick a preset or write your own) and the temperature for this chat. They are saved with the chat. - You can switch the model in the middle of a chat; each reply records which model wrote it. ## Your chats - **Chats are stored in this browser only**, not on our servers. They do not sync between browsers or devices, and clearing your browser data deletes them. - The chat list lets you search (**Ctrl+K**, or **Cmd+K** on a Mac), rename and delete chats. - **Export chat** downloads the open chat as Markdown (`.md`) or JSON (`.json`). Export chats you want to keep before you clear your browser. ## How chat is billed Chat requests run through the same gateway and the same rates as API requests. There is no separate chat subscription. - **Paid models** cost the published per-token rate from the [models table](https://oneaiapikey.com/docs/models#current-models), charged to your balance. Each reply shows what it cost. - **Free models**, when any are listed, cost $0 and work at a $0 balance once the account has paid $3 or more. - **Usage is logged.** Chat requests appear on the [usage page](https://oneaiapikey.com/console/usage) next to your API requests, under a key name that starts with `playground-`. You do not need to create an API key to chat. - When your balance is empty, paid models are refused with a link to add credit. > **Stopping early still costs the tokens already generated:** When you stop a reply, the gateway closes the connection to the model at once. You pay for the prompt and for the tokens generated up to that point, never for the rest. ## Chat, playground or API | | Chat | Console playground | API | | --- | --- | --- | --- | | Where | [/chat](https://oneaiapikey.com/chat) | [Console → Playground](https://oneaiapikey.com/console/playground) | Your code or tools | | Needs an API key | No, you sign in | No, you sign in | Yes | | Best for | Everyday conversations | Testing a prompt, model or parameter before you write code | Apps, agents and coding tools | | Billing | Same balance and rates | Same balance and rates | Same balance and rates | ## When you are ready to build - [Quickstart](https://oneaiapikey.com/docs/quickstart): create a key and send the same request from curl, Python or Node. - [Coding tools](https://oneaiapikey.com/docs/guides): use the models inside Claude Code, Cursor, Cline and others. - [Chat apps you host yourself](https://oneaiapikey.com/docs/apps/open-webui): connect Open WebUI, LibreChat and others with an API key. --- # Chat completions > Send OpenAI-format chat requests to every model with one key. Request fields, roles, the response and usage object, and what changes for Claude models. Source: https://oneaiapikey.com/docs/chat-completions `POST /v1/chat/completions` is the OpenAI Chat Completions API. It is the one request format that every model in the catalogue accepts, so it is the safest default for new code: switch models by changing one string. POST `https://api.oneaiapikey.com/v1/chat/completions` (OpenAI format) Base URL `https://api.oneaiapikey.com/v1` ## Send a request Point any OpenAI SDK at the base URL above and use your key. Nothing else in your code changes. curl: ```bash curl https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "messages": [ {"role": "system", "content": "You are a concise assistant."}, {"role": "user", "content": "Explain what an API gateway does in two sentences."} ], "max_tokens": 300 }' ``` Python: ```python import os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) reply = client.chat.completions.create( model="gpt-5.6-terra", messages=[ {"role": "system", "content": "You are a concise assistant."}, {"role": "user", "content": "Explain what an API gateway does in two sentences."}, ], max_tokens=300, ) print(reply.choices[0].message.content) print(reply.usage) ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const reply = await client.chat.completions.create({ model: "gpt-5.6-terra", messages: [ { role: "system", content: "You are a concise assistant." }, { role: "user", content: "Explain what an API gateway does in two sentences." }, ], max_tokens: 300, }); console.log(reply.choices[0].message.content); console.log(reply.usage); ``` ## Request fields | Field | Type | Notes | | --- | --- | --- | | `model` | string | Required. An exact id from the [models table](https://oneaiapikey.com/docs/models#current-models), for example `gpt-5.6-terra`. | | `messages` | array | Required. The conversation so far, oldest first. See [roles](https://oneaiapikey.com/docs/chat-completions#roles). | | `max_tokens` / `max_completion_tokens` | integer | Upper limit for the reply. Set it: it caps cost and keeps long answers from timing out. | | `temperature`, `top_p` | number | Sampling controls. Leave unset unless you need them; some reasoning models accept only the default. | | `stop` | string or array | Stop sequences. | | `stream` | boolean | `true` sends the reply as server-sent events. See [Streaming](https://oneaiapikey.com/docs/streaming). | | `stream_options` | object | `{"include_usage": true}` adds a final chunk with token counts. | | `tools`, `tool_choice`, `parallel_tool_calls` | | Function calling. See [Tool calling](https://oneaiapikey.com/docs/tool-calling). | | `response_format` | object | JSON mode and JSON Schema. See [Structured output](https://oneaiapikey.com/docs/structured-output) for which models honour it. | | `reasoning_effort` | string | `low`, `medium` or `high` on reasoning models. See [Reasoning models](https://oneaiapikey.com/docs/reasoning). | Other OpenAI fields (`seed`, `n`, `logprobs`, `presence_penalty`, `frequency_penalty`, `user` …) are passed to the provider as they are. Whether they have an effect depends on the model, as it would if you called the provider directly. ## Roles | Role | Use it for | | --- | --- | | `system` | Instructions that apply to the whole conversation. Put one at the start. (`developer` is accepted too.) | | `user` | What the person or your app asks. Text, or a list of parts with [images and files](https://oneaiapikey.com/docs/vision). | | `assistant` | Earlier replies from the model, sent back so it remembers the conversation. Also carries `tool_calls`. | | `tool` | The result of a tool the model asked you to run, with its `tool_call_id`. | The API is stateless: it does not remember earlier requests. To continue a conversation, send the whole history again, with the model’s previous answers as `assistant` messages: Multi-turn history: ```python messages = [ {"role": "system", "content": "You are a helpful SQL tutor."}, {"role": "user", "content": "What is a LEFT JOIN?"}, {"role": "assistant", "content": "A LEFT JOIN returns every row from the left table …"}, {"role": "user", "content": "Show me an example with orders and customers."}, ] ``` Every request is billed for all the tokens it sends, so a long history costs more with each turn. Trim or summarise old turns, and see [prompt caching](https://oneaiapikey.com/docs/prompt-caching) for how repeated prefixes get cheaper. ## Response 200 OK: ```json { "id": "chatcmpl-…", "object": "chat.completion", "created": 1791420807, "model": "gpt-5.6-terra", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "An API gateway is a single entry point …" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 31, "completion_tokens": 42, "total_tokens": 73, "prompt_tokens_details": { "cached_tokens": 0 }, "completion_tokens_details": { "reasoning_tokens": 0 } } } ``` - `choices[0].message.content` is the reply. With tools it may be `null` and `tool_calls` is set instead. - `finish_reason` is `stop` (finished), `length` (hit `max_tokens`), `tool_calls` (wants a tool run) or `content_filter`. - `usage` lists the tokens you are billed for. `prompt_tokens_details.cached_tokens` and `completion_tokens_details.reasoning_tokens` appear when the provider reports them. Reasoning tokens are already included in `completion_tokens`. - The `X-Request-Id` response header identifies the request. Quote it if you contact support. The response carries token counts, not a price. The cost of each request is on the [usage page](https://oneaiapikey.com/console/usage); the rates are in the [models table](https://oneaiapikey.com/docs/models#current-models). ## Claude models in this format Claude models accept chat completions too: the gateway translates the request to Anthropic’s format and the answer back. Most code works unchanged. The differences: - If you leave out `max_tokens`, the gateway sends 8,192, because Anthropic’s API requires a limit. Set your own. - `response_format` is **not** forwarded to Claude. Use a forced tool or the Messages endpoint instead (see [Structured output](https://oneaiapikey.com/docs/structured-output#claude-models)). - `n`, `seed`, `logprobs`, `presence_penalty`, `frequency_penalty` and `logit_bias` have no Anthropic equivalent and are dropped. - `reasoning_effort` turns on Claude’s extended thinking; the thinking text comes back as `reasoning_content` (see [Reasoning models](https://oneaiapikey.com/docs/reasoning)). - Images and PDFs given by URL are downloaded by the gateway and sent to Claude inline. If you need Anthropic-only features such as `cache_control` breakpoints, use the [Anthropic Messages](https://oneaiapikey.com/docs/anthropic-messages) endpoint with `claude-sonnet-5-5` instead. > **Change models without changing code:** Every model on this page shares the same request and response shape. Keep the model id in configuration, not in code, so you can move to a cheaper or newer model with one edit. ## Related - [Streaming](https://oneaiapikey.com/docs/streaming): show the reply as it is written. - [Errors](https://oneaiapikey.com/docs/errors): every status code and what to do about it. - [API reference](https://oneaiapikey.com/docs/api-reference#chat-completions): the full endpoint list. --- # Anthropic Messages API > Call Claude models through the native Anthropic Messages format with the Anthropic SDKs. Headers, base URL, prompt caching, thinking and beta features. Source: https://oneaiapikey.com/docs/anthropic-messages `POST /v1/messages` is Anthropic’s own API. Use it when your code or tool already speaks Anthropic (the Anthropic SDKs, Claude Code, Claude Agent SDK) or when you want Anthropic-only features such as `cache_control` breakpoints and thinking blocks. POST `https://api.oneaiapikey.com/v1/messages` (Anthropic format) Base URL `https://api.oneaiapikey.com` ## Which models The Messages format is for Claude models. For every other model, use [chat completions](https://oneaiapikey.com/docs/chat-completions), which Claude models accept as well. | Model id | Input / 1M | Output / 1M | | --- | --- | --- | | [claude-opus-5-5](https://oneaiapikey.com/models/claude-opus-5-5) | $4.40 | $22 | | [claude-sonnet-5](https://oneaiapikey.com/models/claude-sonnet-5) | $2.20 | $11 | | [claude-haiku-4-5](https://oneaiapikey.com/models/claude-haiku-4-5) | $1.10 | $5.50 | | [claude-sonnet-5-5](https://oneaiapikey.com/models/claude-sonnet-5-5) | $2.20 | $11 | ## Send a request curl: ```bash curl https://api.oneaiapikey.com/v1/messages \ -H "x-api-key: $RELAYKIT_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-5-5", "max_tokens": 1024, "system": "You are a careful code reviewer.", "messages": [ {"role": "user", "content": "Review this line: if (user = null) return;"} ] }' ``` Python: ```python import os import anthropic # base_url has no /v1: the SDK adds /v1/messages itself. client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) message = client.messages.create( model="claude-sonnet-5-5", max_tokens=1024, system="You are a careful code reviewer.", messages=[{"role": "user", "content": "Review this line: if (user = null) return;"}], ) print(message.content[0].text) print(message.usage) ``` Node: ```js import Anthropic from "@anthropic-ai/sdk"; // baseURL has no /v1: the SDK adds /v1/messages itself. const client = new Anthropic({ baseURL: "https://api.oneaiapikey.com", apiKey: process.env.RELAYKIT_API_KEY }); const message = await client.messages.create({ model: "claude-sonnet-5-5", max_tokens: 1024, system: "You are a careful code reviewer.", messages: [{ role: "user", content: "Review this line: if (user = null) return;" }], }); console.log(message.content[0].text); console.log(message.usage); ``` The Anthropic SDKs also read the base URL and key from the environment, so existing code needs no changes: Environment: ```bash export ANTHROPIC_BASE_URL="https://api.oneaiapikey.com" export ANTHROPIC_API_KEY="$RELAYKIT_API_KEY" # the Anthropic SDKs read both variables ``` > **No /v1 in the Anthropic base URL:** Anthropic SDKs and Claude Code append `/v1/messages` themselves. If you set the base URL to `https://api.oneaiapikey.com/v1`, requests go to `/v1/v1/messages` and fail with 404. The one exception is the Vercel AI SDK’s `createAnthropic`, which expects `/v1` (see [SDKs](https://oneaiapikey.com/docs/sdks)). ## Headers | Header | Required | Notes | | --- | --- | --- | | `x-api-key` | Yes (or the next one) | Your key. The Anthropic SDKs send this. | | `Authorization: Bearer ` | Alternative | Accepted too, so one HTTP client can serve both formats. | | `anthropic-version` | Recommended | `2023-06-01`. If you leave it out, the gateway sends that value. | | `anthropic-beta` | Optional | Forwarded to Anthropic as it is, for beta features. | | `Content-Type` | Yes | `application/json`. | ## Request and response The body is Anthropic’s: `model`, `max_tokens` (required by Anthropic; the gateway sends 8,192 if you leave it out), `messages`, and optionally `system`, `temperature`, `top_p`, `top_k`, `stop_sequences`, `stream`, `tools`, `tool_choice`, `thinking`, `metadata`. Message content can be a string or a list of blocks: `text`, `image`, `document`, `tool_use`, `tool_result`. 200 OK: ```json { "id": "msg_…", "type": "message", "role": "assistant", "model": "claude-sonnet-5-5", "content": [{ "type": "text", "text": "This assigns null instead of comparing …" }], "stop_reason": "end_turn", "usage": { "input_tokens": 28, "output_tokens": 64, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0 } } ``` `stop_reason` is `end_turn`, `max_tokens`, `stop_sequence` or `tool_use`. `usage.cache_read_input_tokens` and `usage.cache_creation_input_tokens` show [prompt caching](https://oneaiapikey.com/docs/prompt-caching) at work. ## Features that work here - **Prompt caching.** Mark a stable prefix with `"cache_control": {"type": "ephemeral"}`. See [Prompt caching](https://oneaiapikey.com/docs/prompt-caching) for what is billed. - **Extended thinking.** Pass `thinking`; the reply contains `thinking` blocks before the text. Thinking tokens are billed as output tokens. See [Reasoning models](https://oneaiapikey.com/docs/reasoning). - **Structured output.** Anthropic’s `output_config.format` with a JSON Schema is forwarded. See [Structured output](https://oneaiapikey.com/docs/structured-output#claude-models). - **Tools.** Anthropic tool definitions and `tool_use` / `tool_result` blocks. See [Tool calling](https://oneaiapikey.com/docs/tool-calling). - **Images and PDFs.** `image` and `document` blocks, base64 or URL. See [Vision and files](https://oneaiapikey.com/docs/vision). - **Streaming.** Anthropic’s server-sent events. See [Streaming](https://oneaiapikey.com/docs/streaming#anthropic-format). Extended thinking: ```python message = client.messages.create( model="claude-sonnet-5-5", max_tokens=16000, thinking={"type": "enabled", "budget_tokens": 8000}, messages=[{"role": "user", "content": "Is 2^61 - 1 prime? Show your reasoning briefly."}], ) for block in message.content: print(block.type, getattr(block, "thinking", None) or getattr(block, "text", "")) ``` ## Not available - `POST /v1/messages/count_tokens` is turned off. Use the `usage` in each response, or count locally. - The Message Batches and Files APIs are not offered. - `GET /v1/models` lists the models your key can use. Sent with `x-api-key` and `anthropic-version` (as the Anthropic SDK’s `client.models.list()` does), it answers in Anthropic’s list format; otherwise in OpenAI’s. ## Related - [Claude Code guide](https://oneaiapikey.com/docs/guides/claude-code): point Claude Code at the gateway. - [Authentication](https://oneaiapikey.com/docs/authentication): keys, headers and per-key limits. - [Errors](https://oneaiapikey.com/docs/errors#anthropic-format): the error body on this endpoint. --- # Responses API > Use OpenAI's Responses API through the gateway. Which models support it, why requests should be stateless, and when chat completions is the better choice. Source: https://oneaiapikey.com/docs/responses `POST /v1/responses` is OpenAI’s newer request format: `input` instead of `messages`, typed output items, and built-in reasoning controls. OpenAI’s Codex CLI and some agent frameworks use it by default. POST `https://api.oneaiapikey.com/v1/responses` (OpenAI format) ## Which models | Family | `/v1/responses` | | --- | --- | | GPT | Supported. This is OpenAI’s own format. | | Claude | Supported. The gateway translates the request to Anthropic’s format. Stateful fields are rejected (see below). | | Others | Depends on the provider’s own Responses support. Use [chat completions](https://oneaiapikey.com/docs/chat-completions) for these models; it works for every model. | If you are writing new code and do not need a Responses-only feature, chat completions is the safer choice: one format, every model. ## Send a request curl: ```bash curl https://api.oneaiapikey.com/v1/responses \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "instructions": "Answer in one short paragraph.", "input": "What does idempotent mean for an HTTP method?", "max_output_tokens": 400, "store": false }' ``` Python: ```python import os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) response = client.responses.create( model="gpt-5.6-terra", instructions="Answer in one short paragraph.", input="What does idempotent mean for an HTTP method?", max_output_tokens=400, store=False, ) print(response.output_text) print(response.usage) ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const response = await client.responses.create({ model: "gpt-5.6-terra", instructions: "Answer in one short paragraph.", input: "What does idempotent mean for an HTTP method?", max_output_tokens: 400, store: false, }); console.log(response.output_text); console.log(response.usage); ``` The reply has an `output` array of items (`message`, `reasoning`, `function_call` …). The SDKs’ `output_text` joins the text for you. `usage` has `input_tokens`, `output_tokens` and their details (cached and reasoning tokens). ## Keep requests stateless The gateway does not store responses. Send the whole conversation in `input` each time, set `store: false`, and do not rely on server-side state: - `previous_response_id`, `conversation` and stored `prompt` templates are not supported. For Claude models, a request that uses them is rejected with an error. - `GET /v1/responses/{id}` and `DELETE /v1/responses/{id}` are not available. Keep anything you need from a response yourself. - Background mode (`background: true`) is of no use here, because its result can only be fetched with `GET /v1/responses/{id}`. Multi-turn without stored state: ```python history = [ {"role": "user", "content": "Name a sorting algorithm with O(n log n) worst case."}, ] first = client.responses.create(model="gpt-5.6-terra", input=history, store=False) # Append the model's output items, then the next user turn, and send everything again. history += [item.model_dump(exclude_none=True) for item in first.output] history.append({"role": "user", "content": "How much extra memory does it need?"}) second = client.responses.create(model="gpt-5.6-terra", input=history, store=False) print(second.output_text) ``` > **Codex CLI:** Codex CLI uses this endpoint. The [Codex CLI guide](https://oneaiapikey.com/docs/guides/codex-cli) shows a configuration that works with the gateway. ## Streaming and tools - `stream: true` returns Responses-style server-sent events (`response.created`, `response.output_text.delta`, …, `response.completed`). The final `response.completed` event carries `usage`. - Function tools (`"type": "function"`) work as in OpenAI’s API. Provider-hosted tools such as file search or code interpreter only work where the provider runs them, and the gateway does not host files for them. - `reasoning: {"effort": "low" | "medium" | "high"}` sets the thinking effort on reasoning models. See [Reasoning models](https://oneaiapikey.com/docs/reasoning). ## Related - [Chat completions](https://oneaiapikey.com/docs/chat-completions): the format every model accepts. - [API reference](https://oneaiapikey.com/docs/api-reference#responses): endpoint summary. --- # Streaming responses > Stream replies token by token with server-sent events in both formats, get token usage in the last chunk, and handle timeouts, disconnects and errors mid-stream. Source: https://oneaiapikey.com/docs/streaming With `"stream": true` the reply arrives as [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) while the model writes it, instead of as one JSON body at the end. Use it for anything a person watches: the first words appear much sooner, and long answers do not run into idle timeouts. Streaming costs the same as a normal request. You pay for the same tokens. ## OpenAI format Set `stream: true` on [chat completions](https://oneaiapikey.com/docs/chat-completions). Add `stream_options: {"include_usage": true}` to get the token counts in one extra chunk at the end. curl: ```bash curl -N https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "stream": true, "stream_options": {"include_usage": true}, "messages": [{"role": "user", "content": "Write a haiku about latency."}] }' ``` Python: ```python import os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) stream = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Write a haiku about latency."}], stream=True, stream_options={"include_usage": True}, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) if chunk.usage: # last chunk, choices is empty print("\n", chunk.usage) ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const stream = await client.chat.completions.create({ model: "gpt-5.6-terra", messages: [{ role: "user", content: "Write a haiku about latency." }], stream: true, stream_options: { include_usage: true }, }); for await (const chunk of stream) { const text = chunk.choices[0]?.delta?.content; if (text) process.stdout.write(text); if (chunk.usage) console.log("\n", chunk.usage); // last chunk, choices is empty } ``` On the wire, each event is a `data:` line with one JSON chunk. The text is in `choices[0].delta.content`; the last content chunk carries `finish_reason`; the usage chunk has an empty `choices` array; `data: [DONE]` ends the stream. text/event-stream: ```text data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"gpt-5.6-terra","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]} data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"gpt-5.6-terra","choices":[{"index":0,"delta":{"content":"Packets"}}]} data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"gpt-5.6-terra","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"gpt-5.6-terra","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":19,"total_tokens":33}} data: [DONE] ``` > **Usage only when you ask for it:** Without `stream_options.include_usage`, a streamed response has no usage chunk. The request is still metered and billed in full; you just do not see the counts in the stream. They are on the [usage page](https://oneaiapikey.com/console/usage) either way. Reasoning models may stream their thinking first, as `delta.reasoning_content`, before the answer arrives in `delta.content`. See [Reasoning models](https://oneaiapikey.com/docs/reasoning). ## Anthropic format Set `"stream": true` on [`/v1/messages`](https://oneaiapikey.com/docs/anthropic-messages). The events are Anthropic’s own: `message_start`, then `content_block_start`, `content_block_delta` (text in `delta.text`), `content_block_stop` for each block, `message_delta` with `stop_reason` and the output token count, and `message_stop`. The Anthropic SDKs assemble them for you. curl: ```bash curl -N https://api.oneaiapikey.com/v1/messages \ -H "x-api-key: $RELAYKIT_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-5-5", "max_tokens": 512, "stream": true, "messages": [{"role": "user", "content": "Write a haiku about latency."}] }' ``` Python: ```python import os import anthropic client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) with client.messages.stream( model="claude-sonnet-5-5", max_tokens=512, messages=[{"role": "user", "content": "Write a haiku about latency."}], ) as stream: for text in stream.text_stream: print(text, end="", flush=True) final = stream.get_final_message() print("\n", final.usage) ``` Node: ```js import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://api.oneaiapikey.com", apiKey: process.env.RELAYKIT_API_KEY }); const stream = client.messages.stream({ model: "claude-sonnet-5-5", max_tokens: 512, messages: [{ role: "user", content: "Write a haiku about latency." }], }); stream.on("text", (text) => process.stdout.write(text)); const final = await stream.finalMessage(); console.log("\n", final.usage); ``` Usage is always included in this format: input tokens in `message_start`, output tokens in `message_delta`. ## Tool calls in a stream When a model calls a tool while streaming, the arguments arrive in pieces. In the OpenAI format they are in `delta.tool_calls[i].function.arguments`, keyed by `index`; in the Anthropic format they are `input_json_delta` events. Join the pieces and parse the JSON only after the call is complete (`finish_reason: "tool_calls"` or `content_block_stop`). The SDK helpers above do this for you. See [Tool calling](https://oneaiapikey.com/docs/tool-calling). ## Timeouts and disconnects - **Idle timeout.** The gateway ends a stream if the provider sends nothing for 300 seconds. Very long thinking phases on reasoning models are the usual cause; a lower `reasoning_effort` or a smaller `max_tokens` helps. - **Client timeouts.** Set your HTTP client’s read timeout above the longest gap you expect between chunks, not above the total response time. A stream can run for many minutes while chunks keep arriving. - **If you disconnect,** the gateway closes the request to the provider. You are billed for the tokens produced up to that point, not for the full `max_tokens`. - **Proxies and buffering.** If you relay the stream through your own server, turn off response buffering (for nginx: `proxy_buffering off`) or the text arrives in one lump at the end. ## Errors during a stream Errors that happen before the first byte (bad key, unknown model, low balance) come back as a normal JSON error with a 4xx or 5xx status; see [Errors](https://oneaiapikey.com/docs/errors). Once the stream has started the status is already `200`, so a later failure shows up as an `error` object inside a `data:` event, or the stream ends without `finish_reason` / `message_stop`. Treat a stream that ends without one of those as incomplete, and retry it if your app needs the full answer. ## Related - [Chat completions](https://oneaiapikey.com/docs/chat-completions) and [Anthropic Messages](https://oneaiapikey.com/docs/anthropic-messages): the request fields. - [Rate limits and retries](https://oneaiapikey.com/docs/rate-limits): when to retry, and how. - [Performance metrics](https://oneaiapikey.com/docs/performance): first-token time and tokens per second. --- # Tool calling > Let models call your functions. Define tools, run the call loop, send results back, force a tool, and handle parallel calls in OpenAI and Anthropic formats. Source: https://oneaiapikey.com/docs/tool-calling Tool calling (function calling) lets the model ask your code to run a function, for example a database lookup or an API call, and then use the result in its answer. The model never runs anything itself: it returns the function name and JSON arguments, your code runs it, and you send the result back. Every model with tool support in the [models table](https://oneaiapikey.com/docs/models#current-models) accepts tools in the OpenAI format. Claude models also accept them in the Anthropic format. ## The loop 1. Send the conversation plus a list of `tools`, each with a name, a description and a JSON Schema for its arguments. 2. If the reply has `finish_reason: "tool_calls"`, run each entry in `message.tool_calls`. 3. Append the assistant message, then one `tool` message per call with the matching `tool_call_id` and the result as a string. 4. Send the request again. Repeat until the model answers without tool calls. Python: ```python import json, os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) def get_order_status(order_id: str) -> dict: return {"order_id": order_id, "status": "shipped", "eta": "2 days"} # your real lookup tools = [{ "type": "function", "function": { "name": "get_order_status", "description": "Look up the shipping status of an order by its id.", "parameters": { "type": "object", "properties": {"order_id": {"type": "string", "description": "Order id, e.g. A-1042"}}, "required": ["order_id"], }, }, }] messages = [{"role": "user", "content": "Where is my order A-1042?"}] while True: reply = client.chat.completions.create(model="gpt-5.6-terra", messages=messages, tools=tools) msg = reply.choices[0].message messages.append(msg) # keep the assistant turn, tool_calls included if not msg.tool_calls: break for call in msg.tool_calls: # may be several in one turn args = json.loads(call.function.arguments) result = get_order_status(**args) messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)}) print(msg.content) ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const getOrderStatus = ({ order_id }) => ({ order_id, status: "shipped", eta: "2 days" }); // your real lookup const tools = [{ type: "function", function: { name: "get_order_status", description: "Look up the shipping status of an order by its id.", parameters: { type: "object", properties: { order_id: { type: "string", description: "Order id, e.g. A-1042" } }, required: ["order_id"], }, }, }]; const messages = [{ role: "user", content: "Where is my order A-1042?" }]; let msg; while (true) { const reply = await client.chat.completions.create({ model: "gpt-5.6-terra", messages, tools }); msg = reply.choices[0].message; messages.push(msg); // keep the assistant turn, tool_calls included if (!msg.tool_calls?.length) break; for (const call of msg.tool_calls) { const result = getOrderStatus(JSON.parse(call.function.arguments)); messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) }); } } console.log(msg.content); ``` Every round trip is a separate, separately billed request, and each one re-sends the tool definitions and the growing history. Keep tool descriptions short and cap the number of rounds in production code. ## Forcing or blocking tools tool_choice: ```python # Always call this one tool tool_choice={"type": "function", "function": {"name": "get_order_status"}} # Must call some tool / may call one (default) / must not call any tool_choice="required" # or "auto", "none" ``` A forced call is also the most portable way to get [structured output](https://oneaiapikey.com/docs/structured-output): define one tool whose parameters are the shape you want, and force it. ## Parallel calls Models may return several tool calls in one turn (for example, two order lookups). Run them all, in any order, and send one `tool` message per call before the next request. To get at most one call per turn, set `parallel_tool_calls: false`. ## Anthropic format On [`/v1/messages`](https://oneaiapikey.com/docs/anthropic-messages) the shapes are Anthropic’s: tools have an `input_schema`, the model replies with `tool_use` content blocks and `stop_reason: "tool_use"`, and you answer with `tool_result` blocks inside a `user` message. Anthropic tool use: ```python import os, json import anthropic client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) tools = [{ "name": "get_order_status", "description": "Look up the shipping status of an order by its id.", "input_schema": { "type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"], }, }] messages = [{"role": "user", "content": "Where is my order A-1042?"}] first = client.messages.create(model="claude-sonnet-5-5", max_tokens=1024, tools=tools, messages=messages) if first.stop_reason == "tool_use": call = next(b for b in first.content if b.type == "tool_use") result = {"order_id": call.input["order_id"], "status": "shipped"} messages += [ {"role": "assistant", "content": first.content}, {"role": "user", "content": [ {"type": "tool_result", "tool_use_id": call.id, "content": json.dumps(result)}, ]}, ] final = client.messages.create(model="claude-sonnet-5-5", max_tokens=1024, tools=tools, messages=messages) print(final.content[0].text) ``` When you call a Claude model in the OpenAI format, the gateway converts tools, `tool_choice`, `parallel_tool_calls` and the tool messages in both directions, so the OpenAI loop above works unchanged. ## Tips - Write descriptions for the model, not for people: say when to use the tool and what each argument means. - Validate arguments before you act on them. The JSON is usually well-formed, but the values can be wrong. - Return errors as tool results (`{"error": "order not found"}`) instead of failing the request, so the model can recover. - When streaming, the arguments arrive in pieces. See [Streaming](https://oneaiapikey.com/docs/streaming#tool-calls-in-a-stream). - Some reasoning models restrict tools in chat completions depending on the reasoning setting. If a model rejects your tools, the error message says why; try a lower `reasoning_effort` or the [Responses API](https://oneaiapikey.com/docs/responses). > **Tools that change things:** If a tool sends email, spends money or deletes data, ask the user to confirm before your code runs it. The model decides which tool to call from text it reads, and that text may include instructions planted in documents or web pages. ## Related - [Structured output](https://oneaiapikey.com/docs/structured-output) - [Chat completions](https://oneaiapikey.com/docs/chat-completions#roles): the `tool` role. - [MCP servers](https://oneaiapikey.com/docs/mcp): connect MCP tools in Claude Code, Codex CLI, Cursor and other coding tools. --- # Use MCP servers with your coding tools > MCP servers run in your coding tool, not on the gateway. How to add one in Claude Code, Codex CLI, OpenCode, Cline, Cursor, Kilo Code and Zed, and the cost. Source: https://oneaiapikey.com/docs/mcp The [Model Context Protocol](https://modelcontextprotocol.io) (MCP) lets a coding tool connect to extra tools and data: a file system, a database, an issue tracker, a browser. MCP servers run **inside your coding tool**, on your machine or at a URL you choose. Relaykit does not run or host MCP servers. The gateway only sees the model requests your tool sends, so any MCP server that works in your tool works the same way when the model runs through Relaykit. ## How it fits together 1. Your coding tool starts or connects to the MCP servers in its config and reads the list of tools they offer. 2. With each model request, the tool sends those tool definitions along with your conversation, through the gateway, to the model. 3. When the model asks to call a tool, your coding tool runs it through the MCP server and sends the result back in the next request. To the gateway, this is ordinary [tool calling](https://oneaiapikey.com/docs/tool-calling). Two things follow from that: - **The model must support tool calling.** Pick a model with the Tools capability on the [models page](https://oneaiapikey.com/docs/models#current-models). - **Tool definitions and tool results are input tokens.** They are billed at the model’s input rate on every request, and they reach the model’s provider like the rest of the conversation. A server with dozens of tools adds thousands of tokens to each turn. [Prompt caching](https://oneaiapikey.com/docs/prompt-caching) lowers the cost of the repeated part. > **Only run servers you trust:** A local MCP server runs with your user’s permissions, and whatever it returns is sent to the model. Install servers from sources you trust, give file-system servers only the folders they need, and keep secrets out of tool results. The examples below add the reference [filesystem server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem), which needs Node.js for `npx`. Replace `/path/to/project` with the folder it may read and write. `https://example.com/mcp` stands for any remote server you use. Set up the tool for Relaykit first with its guide. ## Claude Code Set up: [Claude Code guide](https://oneaiapikey.com/docs/guides/claude-code). Add servers with `claude mcp add`. They are saved for you in the current project by default; add `--scope project` to write a shared `.mcp.json` in the project, or `--scope user` to use the server in every project. Terminal: ```bash # A local server (stdio): everything after -- is the command that starts it claude mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/project # A remote server (Streamable HTTP) claude mcp add --transport http docs https://example.com/mcp # Check what is configured claude mcp list ``` .mcp.json (project scope): ```json { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"], "env": {} } } } ``` Inside Claude Code, `/mcp` shows each server’s status. On native Windows (not WSL), wrap `npx` servers as `cmd /c npx ...`. > **Tool search is off with a custom base URL:** When `ANTHROPIC_BASE_URL` points to a host other than Anthropic’s, Claude Code turns off its tool search, so the definitions of every connected MCP tool are sent with each request. Keep only the servers you need connected. Leave `ENABLE_TOOL_SEARCH` unset: it relies on a request feature that the gateway does not promise to pass through. Claude Code warns when one tool result exceeds 10,000 tokens and cuts it at 25,000 by default (`MAX_MCP_OUTPUT_TOKENS`). Official docs: [Claude Code MCP](https://code.claude.com/docs/en/mcp). ## Codex CLI Set up: [Codex CLI guide](https://oneaiapikey.com/docs/guides/codex-cli). Add a server from the terminal, or write it into `~/.codex/config.toml` next to your provider settings. Terminal: ```bash codex mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/project codex mcp list ``` ~/.codex/config.toml: ```toml # A local server (stdio) [mcp_servers.filesystem] command = "npx" args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"] # A remote server; the token is read from an environment variable [mcp_servers.docs] url = "https://example.com/mcp" bearer_token_env_var = "DOCS_MCP_TOKEN" ``` In the Codex interface, `/mcp` lists the connected servers. Official docs: [Codex MCP](https://learn.chatgpt.com/docs/extend/mcp?surface=cli). ## OpenCode Set up: [OpenCode guide](https://oneaiapikey.com/docs/guides/opencode). Add an `mcp` block to the same `opencode.json` that holds your provider. `{env:NAME}` reads an environment variable. opencode.json: ```json { "$schema": "https://opencode.ai/config.json", "mcp": { "filesystem": { "type": "local", "command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"], "enabled": true }, "docs": { "type": "remote", "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer {env:DOCS_MCP_TOKEN}" } } } } ``` Check with `opencode mcp list`. Official docs: [OpenCode MCP servers](https://opencode.ai/docs/mcp-servers/). ## Cline Set up: [Cline guide](https://oneaiapikey.com/docs/guides/cline). In the Cline panel, open the **MCP Servers** icon, then **Configure** and **Configure MCP Servers**. This opens `cline_mcp_settings.json`. Remote servers need `"type": "streamableHttp"`; without it Cline uses the older SSE transport. cline_mcp_settings.json: ```json { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"], "disabled": false, "autoApprove": [] }, "docs": { "type": "streamableHttp", "url": "https://example.com/mcp" } } } ``` `autoApprove` lists tools Cline may run without asking. Leave it empty until you trust a server. Official docs: [Cline MCP overview](https://docs.cline.bot/mcp/mcp-overview). ## Cursor Set up: [Cursor guide](https://oneaiapikey.com/docs/guides/cursor). Put servers in `.cursor/mcp.json` in a project, or in `~/.cursor/mcp.json` for every project. `${env:NAME}` reads an environment variable. .cursor/mcp.json: ```json { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"] }, "docs": { "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer ${env:DOCS_MCP_TOKEN}" } } } } ``` MCP tools are used in Agent chats. Requests use your Relaykit key only with the models you added under your own key; the [Cursor guide](https://oneaiapikey.com/docs/guides/cursor) explains which Cursor features use Cursor’s own infrastructure instead. Official docs: [Cursor MCP](https://cursor.com/docs/mcp). ## Kilo Code Set up: [Kilo Code guide](https://oneaiapikey.com/docs/guides/kilo-code). In VS Code, open Kilo Code’s **Settings**, then **MCP** and **Add Server**. The CLI reads `~/.config/kilo/kilo.jsonc`, or `kilo.jsonc` in a project. Remote servers use the same `type: "remote"` form as OpenCode. kilo.jsonc: ```jsonc { "mcp": { "filesystem": { "type": "local", "command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"], "enabled": true } } } ``` Official docs: [Kilo Code MCP](https://kilo.ai/docs/automate/mcp/using-in-kilo-code). ## Zed Set up: [Zed guide](https://oneaiapikey.com/docs/guides/zed). Add servers under `context_servers` in Zed’s `settings.json`. Remote servers take `url` and optional `headers` instead of `command`. settings.json: ```json { "context_servers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"], "env": {} } } } ``` Under **Settings**, **AI**, **MCP Servers**, a green dot means the server is running. Official docs: [Zed MCP](https://zed.dev/docs/ai/mcp). ## Aider Aider has no built-in MCP support. It is an [open feature request](https://github.com/Aider-AI/aider/issues/4506). Use one of the tools above if you need MCP. ## Troubleshooting - **The server does not start.** Run its command in a terminal yourself. Most failures are a missing Node.js or Python, a wrong path, or a missing environment variable. The gateway is not involved at this stage. - **The tools are listed but the model never uses them.** Check that the model has the Tools capability on the [models page](https://oneaiapikey.com/docs/models#current-models), and ask for the tool by name once to test. - **Requests got expensive.** Every connected tool’s definition is sent with each request. Disconnect servers you are not using and check the input tokens per request on the [usage page](https://oneaiapikey.com/console/usage). - **`400` about context length.** Long tool results build up in the conversation. Start a new session, or ask for smaller results (one file, not a folder). - **`401` or `403`.** These come from your key or model list, not from MCP. See [Errors](https://oneaiapikey.com/docs/errors#access-denied). ## Related - [Tool calling](https://oneaiapikey.com/docs/tool-calling): the request format behind every MCP tool call. - [Coding tools overview](https://oneaiapikey.com/docs/guides): set up each tool for Relaykit. - [Cost control](https://oneaiapikey.com/docs/cost-control): spend limits for keys used by agents. --- # Vision and files > Send images and PDFs to vision models by URL or as base64, in OpenAI and Anthropic formats. Which models accept images, how they are billed, and size limits. Source: https://oneaiapikey.com/docs/vision Vision models read images (and some read PDFs) as part of the prompt. You send them as content parts next to the text, either as a public URL or inline as base64. ## Models that accept images | Model id | Input / 1M | Output / 1M | | --- | --- | --- | | [claude-opus-5-5](https://oneaiapikey.com/models/claude-opus-5-5) | $4.40 | $22 | | [claude-sonnet-5](https://oneaiapikey.com/models/claude-sonnet-5) | $2.20 | $11 | | [claude-haiku-4-5](https://oneaiapikey.com/models/claude-haiku-4-5) | $1.10 | $5.50 | | [gpt-6.1-sol](https://oneaiapikey.com/models/gpt-6-1-sol) | $2.20 | $11 | | [gpt-5.6-sol](https://oneaiapikey.com/models/gpt-5-6-sol) | $5.50 | $33 | | [gpt-5.6-terra](https://oneaiapikey.com/models/gpt-5-6-terra) | $2.20 | $13.20 | | [grok-4.6](https://oneaiapikey.com/models/grok-4-6) | $2.20 | $6.60 | | [claude-sonnet-5-5](https://oneaiapikey.com/models/claude-sonnet-5-5) | $2.20 | $11 | | [grok-4.7](https://oneaiapikey.com/models/grok-4-7) | $2.20 | $6.60 | Models not in this list are text only: an image part sent to them fails or is ignored, depending on the provider. ## Send an image In the OpenAI format, `content` becomes a list of parts. An image is an `image_url` part whose `url` is either an `https://` link or a `data:` URL with base64 data. curl: ```bash curl https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-opus-5-5", "max_tokens": 500, "messages": [{ "role": "user", "content": [ {"type": "text", "text": "What is in this picture? One sentence."}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/3/3f/Fronalpstock_big.jpg"}} ] }] }' ``` Python: ```python import base64, os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) with open("receipt.png", "rb") as f: data_url = "data:image/png;base64," + base64.b64encode(f.read()).decode() reply = client.chat.completions.create( model="claude-opus-5-5", max_tokens=500, messages=[{ "role": "user", "content": [ {"type": "text", "text": "List the items and the total on this receipt."}, {"type": "image_url", "image_url": {"url": data_url}}, ], }], ) print(reply.choices[0].message.content) ``` Node: ```js import { readFile } from "node:fs/promises"; import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const dataUrl = "data:image/png;base64," + (await readFile("receipt.png")).toString("base64"); const reply = await client.chat.completions.create({ model: "claude-opus-5-5", max_tokens: 500, messages: [{ role: "user", content: [ { type: "text", text: "List the items and the total on this receipt." }, { type: "image_url", image_url: { url: dataUrl } }, ], }], }); console.log(reply.choices[0].message.content); ``` - **By URL**: the image must be publicly reachable without cookies or a login. For Claude models the gateway downloads it and sends it inline; for other models the provider fetches it. - **As base64**: works for private files and is the more reliable choice. Use the right MIME type (`image/png`, `image/jpeg`, `image/webp`, `image/gif`). - `detail` (`low`, `high`, `auto`) is passed to models that support it. - Several images can go in one message. Put the question after the images for best results. ## PDFs Claude models read PDFs, page text and images together. In the OpenAI format, send a `file` part with a base64 `data:application/pdf` URL; the gateway passes it to Claude as a document block. PDF in the OpenAI format: ```python with open("contract.pdf", "rb") as f: pdf_data = "data:application/pdf;base64," + base64.b64encode(f.read()).decode() reply = client.chat.completions.create( model="claude-sonnet-5-5", max_tokens=1000, messages=[{ "role": "user", "content": [ {"type": "file", "file": {"filename": "contract.pdf", "file_data": pdf_data}}, {"type": "text", "text": "Summarise the termination clauses."}, ], }], ) ``` `file_id` references do not work: the gateway does not offer a Files API. Always send the file data. Other providers’ PDF support varies; for them, extract the text yourself and send it as text. ## Anthropic format On [`/v1/messages`](https://oneaiapikey.com/docs/anthropic-messages), use Anthropic’s `image` and `document` blocks with a `base64` or `url` source. Anthropic image and document blocks: ```python message = client.messages.create( model="claude-sonnet-5-5", max_tokens=500, messages=[{ "role": "user", "content": [ {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": png_base64}}, {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_base64}}, {"type": "text", "text": "Does the chart in the image match the numbers in the PDF?"}, ], }], ) ``` ## Cost and limits - Images and PDF pages are billed as **input tokens** at the model’s normal input rate. How many tokens an image uses depends on its size and on the model; the provider counts them and the count is in `usage.prompt_tokens`. - Shrink large images before sending. A phone photo at full resolution costs more and rarely reads better than one scaled to about 1,500 px on the long side. - A request body can be up to 128 MB at the gateway. Providers apply their own, smaller limits on image size, image count and PDF pages, and reject requests that go over them. - Images sent again in later turns are billed again. Drop them from the history once the model has answered about them. > **Personal data in images:** Screenshots and scans often contain names, addresses or card numbers. Crop or redact them before sending, as you would for text. ## Related - [Models](https://oneaiapikey.com/docs/models#current-models): context windows and prices. - [Cost control](https://oneaiapikey.com/docs/cost-control): keeping input tokens down. --- # Structured output > Get JSON that matches your schema. response_format with JSON Schema, which models honour it, and the two methods that work for Claude models. Source: https://oneaiapikey.com/docs/structured-output Structured output means the model answers with JSON in a shape you define, so your code can parse it without guessing. There are three ways to ask for it; which one works depends on the model. ## Which method works where | Model id | `json_schema` | `json_object` | Forced tool | | --- | --- | --- | --- | | [claude-opus-5-5](https://oneaiapikey.com/models/claude-opus-5-5) | No (dropped) | No (dropped) | Yes | | [claude-sonnet-5](https://oneaiapikey.com/models/claude-sonnet-5) | No (dropped) | No (dropped) | Yes | | [claude-haiku-4-5](https://oneaiapikey.com/models/claude-haiku-4-5) | No (dropped) | No (dropped) | Yes | | [gpt-6.1-sol](https://oneaiapikey.com/models/gpt-6-1-sol) | Yes | Yes | Yes | | [gpt-5.6-sol](https://oneaiapikey.com/models/gpt-5-6-sol) | Yes | Yes | Yes | | [gpt-5.6-terra](https://oneaiapikey.com/models/gpt-5-6-terra) | Yes | Yes | Yes | | [grok-4.6](https://oneaiapikey.com/models/grok-4-6) | Yes | Use json_schema | Yes | | [claude-sonnet-5-5](https://oneaiapikey.com/models/claude-sonnet-5-5) | No (dropped) | No (dropped) | Yes | | [grok-4.7](https://oneaiapikey.com/models/grok-4-7) | Yes | Use json_schema | Yes | | [deepseek-v4-flash](https://oneaiapikey.com/models/deepseek-v4-flash) | No | Yes | Yes | | [deepseek-v4-pro](https://oneaiapikey.com/models/deepseek-v4-pro) | No | Yes | Yes | `json_schema` and `json_object` are values of `response_format` on [chat completions](https://oneaiapikey.com/docs/chat-completions). “Yes” means the provider documents support for it; “No (dropped)” is explained [below](https://oneaiapikey.com/docs/structured-output#claude-models). ## JSON Schema (recommended) `response_format: {"type": "json_schema", ...}` with `strict: true` makes supporting models follow the schema exactly. With strict mode, list every property in `required` (use a `null` type for optional values) and set `additionalProperties: false`. Python: ```python import json, os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) schema = { "type": "object", "properties": { "name": {"type": "string"}, "email": {"type": ["string", "null"]}, "topics": {"type": "array", "items": {"type": "string"}}, }, "required": ["name", "email", "topics"], "additionalProperties": False, } reply = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": "Extract the contact: 'Hi, I'm Dana Lee (dana@example.com), asking about invoices and refunds.'"}], response_format={"type": "json_schema", "json_schema": {"name": "contact", "strict": True, "schema": schema}}, ) contact = json.loads(reply.choices[0].message.content) print(contact) ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const schema = { type: "object", properties: { name: { type: "string" }, email: { type: ["string", "null"] }, topics: { type: "array", items: { type: "string" } }, }, required: ["name", "email", "topics"], additionalProperties: false, }; const reply = await client.chat.completions.create({ model: "gpt-5.6-terra", messages: [{ role: "user", content: "Extract the contact: 'Hi, I'm Dana Lee (dana@example.com), asking about invoices and refunds.'" }], response_format: { type: "json_schema", json_schema: { name: "contact", strict: true, schema } }, }); console.log(JSON.parse(reply.choices[0].message.content)); ``` In the [Responses API](https://oneaiapikey.com/docs/responses) the same schema goes in `text.format` instead of `response_format`. ## JSON mode `response_format: {"type": "json_object"}` only promises valid JSON, not a particular shape. Describe the shape in the prompt, and include the word “JSON” in it (some providers reject the request otherwise). Prefer JSON Schema where the model supports it. ## Claude models > **response_format is not forwarded to Claude:** When you call a Claude model through `/v1/chat/completions`, the gateway translates the request to Anthropic’s format, and `response_format` has no counterpart there, so it is **dropped without an error**. The model then answers in free text, which may or may not be JSON. Use one of the two methods below instead. **Forced tool call (OpenAI format).** Define one tool whose parameters are your schema and force it with `tool_choice`. The arguments of the tool call are your JSON. This works for every model with tool support, so it is also the most portable method. Forced tool, works for Claude and others: ```python reply = client.chat.completions.create( model="claude-sonnet-5-5", max_tokens=1024, messages=[{"role": "user", "content": "Extract the contact: 'Hi, I'm Dana Lee (dana@example.com), asking about invoices and refunds.'"}], tools=[{"type": "function", "function": { "name": "save_contact", "description": "Save the extracted contact.", "parameters": schema, # the same JSON Schema as above }}], tool_choice={"type": "function", "function": {"name": "save_contact"}}, ) contact = json.loads(reply.choices[0].message.tool_calls[0].function.arguments) ``` **Native structured output (Anthropic format).** On [`/v1/messages`](https://oneaiapikey.com/docs/anthropic-messages), Anthropic’s `output_config.format` with a JSON Schema is passed through to Claude, and the reply text is JSON that matches the schema. Anthropic output_config: ```python import anthropic, json, os client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) message = client.messages.create( model="claude-sonnet-5-5", max_tokens=1024, messages=[{"role": "user", "content": "Extract the contact: 'Hi, I'm Dana Lee (dana@example.com), asking about invoices and refunds.'"}], extra_body={"output_config": {"format": {"type": "json_schema", "schema": schema}}}, ) contact = json.loads(message.content[0].text) ``` ## Always validate Even with strict schemas, check the result before you use it: a reply cut off by `max_tokens` (`finish_reason: "length"`) is incomplete JSON, and a model can refuse instead of answering. Parse inside a `try`, validate with your schema library (Pydantic, Zod, JSON Schema), and retry or fall back when it fails. ## Related - [Tool calling](https://oneaiapikey.com/docs/tool-calling): the loop behind forced tools. - [Models](https://oneaiapikey.com/docs/models#current-models): capabilities of each model. --- # Reasoning models > Control how much reasoning models think with reasoning_effort or Claude's thinking, read the reasoning text, and understand how thinking tokens are billed. Source: https://oneaiapikey.com/docs/reasoning Reasoning models think before they answer: they write internal reasoning tokens, then the reply. That makes them better at maths, planning, debugging and multi-step tool use, and slower and more expensive per request. You decide how hard they think. ## Models with reasoning | Model id | Input / 1M | Output / 1M | | --- | --- | --- | | [claude-opus-5-5](https://oneaiapikey.com/models/claude-opus-5-5) | $4.40 | $22 | | [claude-sonnet-5](https://oneaiapikey.com/models/claude-sonnet-5) | $2.20 | $11 | | [claude-haiku-4-5](https://oneaiapikey.com/models/claude-haiku-4-5) | $1.10 | $5.50 | | [gpt-6.1-sol](https://oneaiapikey.com/models/gpt-6-1-sol) | $2.20 | $11 | | [gpt-5.6-sol](https://oneaiapikey.com/models/gpt-5-6-sol) | $5.50 | $33 | | [gpt-5.6-terra](https://oneaiapikey.com/models/gpt-5-6-terra) | $2.20 | $13.20 | | [grok-4.6](https://oneaiapikey.com/models/grok-4-6) | $2.20 | $6.60 | | [claude-sonnet-5-5](https://oneaiapikey.com/models/claude-sonnet-5-5) | $2.20 | $11 | | [grok-4.7](https://oneaiapikey.com/models/grok-4-7) | $2.20 | $6.60 | | [deepseek-v4-flash](https://oneaiapikey.com/models/deepseek-v4-flash) | $0.24 | $0.96 | | [deepseek-v4-pro](https://oneaiapikey.com/models/deepseek-v4-pro) | $1.32 | $3.96 | ## Set the effort (OpenAI format) On [chat completions](https://oneaiapikey.com/docs/chat-completions), `reasoning_effort` sets how much the model thinks. Common values are `low`, `medium` and `high`; some models also accept `minimal`, `none` or `xhigh`. Lower effort answers faster and uses fewer tokens. curl: ```bash curl https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-6.1-sol", "reasoning_effort": "low", "max_completion_tokens": 4000, "messages": [{"role": "user", "content": "A bat and a ball cost $1.10 in total. The bat costs $1.00 more than the ball. How much is the ball?"}] }' ``` Python: ```python import os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) reply = client.chat.completions.create( model="gpt-6.1-sol", reasoning_effort="low", max_completion_tokens=4000, messages=[{"role": "user", "content": "A bat and a ball cost $1.10 in total. The bat costs $1.00 more than the ball. How much is the ball?"}], ) print(reply.choices[0].message.content) print(reply.usage.completion_tokens_details.reasoning_tokens, "reasoning tokens") ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const reply = await client.chat.completions.create({ model: "gpt-6.1-sol", reasoning_effort: "low", max_completion_tokens: 4000, messages: [{ role: "user", content: "A bat and a ball cost $1.10 in total. The bat costs $1.00 more than the ball. How much is the ball?" }], }); console.log(reply.choices[0].message.content); console.log(reply.usage.completion_tokens_details?.reasoning_tokens, "reasoning tokens"); ``` - Set `max_completion_tokens` (or `max_tokens`) with room for the thinking **and** the answer. If the limit is reached while the model is still thinking, you get `finish_reason: "length"` and little or no text. - Many reasoning models accept only the default `temperature` and `top_p`. Leave them unset. - The OpenRouter-style `reasoning` object (`{"effort": "high"}` or `{"max_tokens": 4000}`) is accepted as well. - In the [Responses API](https://oneaiapikey.com/docs/responses), use `reasoning: {"effort": "low"}`. ## Claude models Claude calls this extended thinking. You can turn it on in either format. **OpenAI format.** Send `reasoning_effort` to a Claude model and the gateway turns it into Claude’s thinking settings. The thinking text comes back in `message.reasoning_content` (and `delta.reasoning_content` when streaming), separate from `content`. Claude thinking via chat completions: ```python reply = client.chat.completions.create( model="claude-sonnet-5-5", reasoning_effort="medium", # turns on Claude's extended thinking max_tokens=8000, messages=[{"role": "user", "content": "Plan a zero-downtime migration from MySQL 5.7 to 8.0."}], ) msg = reply.choices[0].message print(getattr(msg, "reasoning_content", None)) # Claude's thinking, when returned print(msg.content) # the answer ``` **Anthropic format.** On [`/v1/messages`](https://oneaiapikey.com/docs/anthropic-messages), pass Anthropic’s `thinking` parameter. The reply contains `thinking` blocks before the `text` block. Claude thinking via Messages: ```python import anthropic, os client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) message = client.messages.create( model="claude-sonnet-5-5", max_tokens=16000, thinking={"type": "enabled", "budget_tokens": 8000}, # budget must be below max_tokens messages=[{"role": "user", "content": "Plan a zero-downtime migration from MySQL 5.7 to 8.0."}], ) for block in message.content: if block.type == "thinking": print("THINKING:", block.thinking[:200], "...") elif block.type == "text": print(block.text) ``` In multi-turn conversations with tools, send the assistant’s `thinking` blocks back unchanged, as Anthropic requires. The Anthropic SDKs do this when you append `message.content` as it is. ## What you are billed for - Reasoning tokens are **output tokens**, billed at the model’s output rate, even when the reasoning text is not shown to you. - `usage.completion_tokens` already includes them; `usage.completion_tokens_details.reasoning_tokens` shows how many there were, when the provider reports it. - A hard question at `high` effort can use many times more tokens than the visible answer. Start at `low` and raise it only where answers get better. > **Long thinking and timeouts:** A model can think for minutes before the first answer token. Stream the response so the connection stays active, and keep in mind that the gateway closes a stream after 300 seconds without any data. See [Streaming](https://oneaiapikey.com/docs/streaming#timeouts-and-disconnects). ## Related - [Cost control](https://oneaiapikey.com/docs/cost-control): set limits per key. - [Models](https://oneaiapikey.com/docs/models#current-models): output prices per model. --- # Prompt caching > How repeated prompt prefixes are cached and billed at the lower cached rate, how to mark cache breakpoints for Claude, and how to check cache hits in usage. Source: https://oneaiapikey.com/docs/prompt-caching When many requests start with the same text (a long system prompt, a document, tool definitions, the earlier part of a chat), the provider can reuse its work on that prefix. The reused part is billed at the model’s **cached input** rate, which is far below the normal input rate. ## Cached rates | Model id | Input / 1M | Cached input / 1M | | --- | --- | --- | | [claude-opus-5-5](https://oneaiapikey.com/models/claude-opus-5-5) | $4.40 | $0.22 | | [claude-sonnet-5](https://oneaiapikey.com/models/claude-sonnet-5) | $2.20 | $0.22 | | [claude-haiku-4-5](https://oneaiapikey.com/models/claude-haiku-4-5) | $1.10 | $0.11 | | [gpt-6.1-sol](https://oneaiapikey.com/models/gpt-6-1-sol) | $2.20 | $0.11 | | [gpt-5.6-sol](https://oneaiapikey.com/models/gpt-5-6-sol) | $5.50 | $0.55 | | [gpt-5.6-terra](https://oneaiapikey.com/models/gpt-5-6-terra) | $2.20 | $0.22 | | [grok-4.6](https://oneaiapikey.com/models/grok-4-6) | $2.20 | $0.55 | | [claude-sonnet-5-5](https://oneaiapikey.com/models/claude-sonnet-5-5) | $2.20 | $0.22 | | [grok-4.7](https://oneaiapikey.com/models/grok-4-7) | $2.20 | $0.55 | | [deepseek-v4-flash](https://oneaiapikey.com/models/deepseek-v4-flash) | $0.24 | $0.0048 | | [deepseek-v4-pro](https://oneaiapikey.com/models/deepseek-v4-pro) | $1.32 | $0.044 | Models not in this table bill repeated input at their normal input rate. ## How you are billed - **Cache reads**: the cached part of the prompt is billed at the cached rate above. The rest of the prompt is billed at the normal input rate, and output as usual. - **Cache writes (Claude only)**: writing a prefix to Claude’s cache is billed at 1.25 times the input rate for the default 5-minute cache, and 2 times the input rate for the 1-hour cache (`"ttl": "1h"`). A write pays for itself after one or two reads. - **Other providers** do not charge extra for writes. Caching is automatic there, so you only see the discount. - A cache entry expires after a few minutes without use (about 5 minutes for Claude’s default). Nothing is billed for keeping it. Your [usage page](https://oneaiapikey.com/console/usage) shows the cost of each request with the cached tokens already priced in. ## Automatic caching (non-Claude models) The other providers in the table cache long prompt prefixes on their own, typically once a prompt passes about 1,024 tokens. You do not change anything in the request. To get more hits: - Put what stays the same first (instructions, documents, tool definitions, examples) and what changes last (the user’s question). - Keep that prefix byte-for-byte identical: no timestamps, request ids or shuffled examples at the top. - Send related requests close together; caches expire after a few idle minutes. - On OpenAI models, `prompt_cache_key` (any stable string, for example a user or document id) helps route similar requests to the same cache. Check `usage.prompt_tokens_details.cached_tokens` in the response to see how much was a hit: A cache hit in the OpenAI format: ```json "usage": { "prompt_tokens": 5120, "completion_tokens": 180, "total_tokens": 5300, "prompt_tokens_details": { "cached_tokens": 4864 } } ``` ## Claude: mark what to cache Claude caches only the prefixes you mark with `cache_control`, and the marker exists only in Anthropic’s format. Use the [Anthropic Messages](https://oneaiapikey.com/docs/anthropic-messages) endpoint and put `"cache_control": {"type": "ephemeral"}` on the last block of the stable part (a system block, a tool, or a message). Everything up to and including that block is cached. Caching a long document with Claude: ```python import anthropic, os client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) handbook = open("handbook.md").read() # a long document you ask many questions about def ask(question: str): return client.messages.create( model="claude-sonnet-5-5", max_tokens=1024, system=[ {"type": "text", "text": "Answer questions about the company handbook."}, {"type": "text", "text": handbook, "cache_control": {"type": "ephemeral"}}, # cache up to here ], messages=[{"role": "user", "content": question}], ) first = ask("How many vacation days do new staff get?") print(first.usage.cache_creation_input_tokens, "tokens written to the cache") second = ask("What is the policy on remote work?") print(second.usage.cache_read_input_tokens, "tokens read from the cache") ``` - Claude only caches prefixes above a minimum length (around 1,024 tokens on most models; shorter prefixes are processed normally and not cached). - You can set up to four breakpoints, for example one after the tools and one after the documents. - `usage.cache_creation_input_tokens` is what was written (billed at the write rate), `usage.cache_read_input_tokens` what was read (billed at the cached rate), and `usage.input_tokens` the rest. > **Chat completions does not cache on Claude:** When you call a Claude model through `/v1/chat/completions`, the gateway translates the request to Anthropic’s format and does not carry `cache_control` markers over. Those requests get no cache hits and pay the full input rate for the whole prompt. For long, repeated prompts on Claude, use `/v1/messages`. ## When caching helps | Workload | Benefit | | --- | --- | | Coding agents and long chats (the history is resent every turn) | Large: most of every request is the same prefix. | | Questions over one long document or codebase | Large, if the document comes first. | | Many short, unrelated prompts | None: there is nothing to reuse. | | Batch jobs with a shared instruction block | Moderate, if requests run close together. | ## Related - [Cost control](https://oneaiapikey.com/docs/cost-control): other ways to spend less. - [Models](https://oneaiapikey.com/docs/models#current-models): all rates. --- # Cost control > Work out what a request costs, choose cheaper models, cap output, use prompt caching, and limit each key's spending so a bug cannot drain your balance. Source: https://oneaiapikey.com/docs/cost-control You pay per token at the rates in the [models table](https://oneaiapikey.com/docs/models#current-models), from a prepaid balance. Nothing renews or charges on its own, and requests stop when the balance reaches $0. This page covers how to spend less and how to make sure a bug cannot spend more than you intend. ## What a request costs Per request: ```text cost = input_tokens × input rate / 1,000,000 + output_tokens × output rate / 1,000,000 rounded to the nearest $0.000002 (at least $0.000002 on a paid model) (cached input tokens use the cached rate; reasoning tokens count as output) ``` For example, a request with 3,000 input tokens and 800 output tokens costs: | Model id | Input / 1M | Output / 1M | This request | | --- | --- | --- | --- | | claude-sonnet-5-5 | $2.20 | $11 | $0.0154 | | gpt-5.6-terra | $2.20 | $13.20 | $0.01716 | | claude-haiku-4-5 | $1.10 | $5.50 | $0.0077 | Output tokens cost several times more than input tokens on most models, so long answers and long thinking are usually where the money goes. The exact cost of every request is on the [usage page](https://oneaiapikey.com/console/usage), which you can filter by model and key and export as CSV. ## Spend less per request 1. **Pick the smallest model that does the job.** Try a fast model first and move up only for the requests where it falls short. Every model shares the same API, so this is a one-line change. 2. **Cap the output.** Set `max_tokens` on every request. Without it, a model can write until its own maximum, and you pay for every token. 3. **Turn reasoning down.** On [reasoning models](https://oneaiapikey.com/docs/reasoning), `reasoning_effort: "low"` often answers as well for routine work, with far fewer output tokens. 4. **Send less history.** The whole conversation is billed on every turn. Drop old turns, summarise them, and remove images once they have been discussed. 5. **Reuse prefixes.** Keep long instructions and documents at the start of the prompt so [prompt caching](https://oneaiapikey.com/docs/prompt-caching) bills them at the cached rate. 6. **Ask for short answers.** “Answer in at most three sentences” in the system prompt is the cheapest optimisation there is. ## Free and discounted models No models in this list right now. See [all models](https://oneaiapikey.com/models). When free models are listed, they cost nothing and work with a zero balance once the account has paid $3 or more. Discounted models are listed on the [models page](https://oneaiapikey.com/docs/models#free-and-discounted-models) with their end dates. ## Limit what a key can spend Give every app, environment and teammate its own key under [API keys](https://oneaiapikey.com/console/keys), and set on each: - **Spend limit**: the most this key can ever spend. When it is used up the key stops working; your balance and your other keys are not affected. - **Expiry date**: for temporary access, demos and contractors. - **Model list**: only the models this key may call, so a typo or a changed config cannot switch an app to an expensive model. > **Agents and loops:** Coding agents and tool loops can make hundreds of requests on their own, each re-sending a long history. Run them with a key that has a spend limit you are comfortable losing, and cap the number of rounds in your own code. See [Authentication and keys](https://oneaiapikey.com/docs/authentication#per-key-settings) for how each setting behaves. ## Keep an eye on spending - The [usage page](https://oneaiapikey.com/console/usage) shows each request with its model, key, tokens and cost, for up to 30 days at a time. - The [billing page](https://oneaiapikey.com/console/billing) shows your balance, what you have spent to date, and a notice when the balance is empty. - The API response itself carries token counts in `usage`, not prices. Multiply by the rates if you want to track cost in your own logs. ## Related - [Pricing and billing](https://oneaiapikey.com/docs/billing): how the balance, credit packs and plans work. - [Models](https://oneaiapikey.com/docs/models#current-models): every rate. - [Account security and alerts](https://oneaiapikey.com/docs/account-security#low-balance-alerts): a webhook alert when the balance runs low. --- # Rate limits and retries > Where 429 errors come from, how many requests you can run at once, and how to retry with backoff and cap concurrency so bursts and long jobs finish reliably. Source: https://oneaiapikey.com/docs/rate-limits ## Where limits come from - **The gateway** does not cap how many requests per minute your account or key sends to the API. What limits your usage is your balance and each key’s spend limit. - **Model providers** throttle their own capacity. When a model is busy, the provider answers `429` or `503`, and the gateway passes that on to you unchanged. This is the usual source of a `429`. - **Per-model limits** are off by default and can be switched on if a model needs protecting. A request over such a limit gets `429` with the message “Rate limit reached: at most N requests per M minutes”, which tells you the exact numbers. The gateway does not retry a failed request on another route for you. What the provider answered is what you get, so your code decides whether to retry. ## Retry the right errors | Status | Retry? | Why | | --- | --- | --- | | `429` | Yes, with backoff | Temporary: the model or provider is busy. Honour `Retry-After` if present. | | `500`, `502`, `503` | Yes, with backoff | Usually short provider problems. | | Network error, connection reset | Yes | Nothing reached the model, or the stream broke. | | Timeout without a response | Carefully | The model may have finished and been billed. See [Errors](https://oneaiapikey.com/docs/errors#timeouts). | | `400`, `401`, `403`, `404`, `413`, `501` | No | The same request fails the same way. Fix the request, key or balance. | Back off exponentially (1 s, 2 s, 4 s, 8 s, with random jitter) and give up after about five tries. The [Errors](https://oneaiapikey.com/docs/errors#retries) page has a hand-written retry loop if you are not using an SDK. ## Cap concurrency For batch jobs, the most reliable pattern is a fixed number of requests in flight plus the SDK’s built-in retries. Start at around 8 parallel requests and raise it while errors stay rare. Python: ```python import asyncio, os from openai import AsyncOpenAI client = AsyncOpenAI( base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"], max_retries=5, # the SDK retries 429, 5xx and network errors with backoff timeout=120, ) limit = asyncio.Semaphore(8) # at most 8 requests in flight async def summarise(text: str) -> str: async with limit: reply = await client.chat.completions.create( model="claude-haiku-4-5", max_tokens=300, messages=[{"role": "user", "content": f"Summarise in two sentences:\n\n{text}"}], ) return reply.choices[0].message.content async def main(texts): return await asyncio.gather(*(summarise(t) for t in texts)) ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY, maxRetries: 5, // the SDK retries 429, 5xx and network errors with backoff timeout: 120_000, }); // Run at most 8 requests at a time. async function mapLimited(items, limit, fn) { const results = new Array(items.length); let next = 0; const worker = async () => { while (next < items.length) { const i = next++; results[i] = await fn(items[i]); } }; await Promise.all(Array.from({ length: limit }, worker)); return results; } const summaries = await mapLimited(texts, 8, async (text) => { const reply = await client.chat.completions.create({ model: "claude-haiku-4-5", max_tokens: 300, messages: [{ role: "user", content: `Summarise in two sentences:\n\n${text}` }], }); return reply.choices[0].message.content; }); ``` > **SDK retries are on by default:** The official OpenAI and Anthropic SDKs already retry `429`, `5xx` and connection errors twice with backoff. Raise `max_retries` / `maxRetries` for batch work rather than wrapping a second retry loop around theirs, which multiplies the attempts. ## Keep long requests alive - Stream long outputs. The gateway ends a stream after 300 seconds without data, and a non-streamed answer arrives only at the end, so proxies and clients in between may give up first. See [Streaming](https://oneaiapikey.com/docs/streaming#timeouts-and-disconnects). - Set your client’s timeout to a few minutes for reasoning models. - A request your client abandons is still billed for the tokens the model produced before it stopped. ## When one model keeps failing If a model returns `5xx` or `429` for several minutes, switch to a sibling model from the [models table](https://oneaiapikey.com/docs/models#current-models). Every model uses the same API, so it is a change of the `model` string. Keep the fallback list in your config so you can change it without a deploy. ## Related - [Errors](https://oneaiapikey.com/docs/errors): every status code and message. - [Cost control](https://oneaiapikey.com/docs/cost-control): per-key spend limits. - [Routing and privacy](https://oneaiapikey.com/docs/routing-privacy#retries-and-failover): the gateway makes one attempt per request. --- # Errors > What each HTTP status code and error message from the API means, how to fix 401, 403, 404 and 429 errors, low balance and unknown models, and when to retry. Source: https://oneaiapikey.com/docs/errors Errors come back as normal HTTP status codes with a JSON body that says what went wrong. Most problems are one of three things: the key, the base URL, or the balance. This page helps you tell them apart quickly. ## Error format Errors use the OpenAI-style `error` envelope, which every OpenAI SDK already understands. Errors that come from the model provider keep the provider's status code and message. 401 invalid key: ```json { "error": { "code": "", "message": "Invalid token (request id: 20261008101503123456789abcdef)", "type": "api_error" } } ``` 403 balance: ```json { "error": { "code": "insufficient_user_quota", "message": "Your balance is too low for this request. Add credit under Billing in the console. (request id: 20261008101503123456789abcdef)", "type": "api_error" } } ``` 503 model unavailable: ```json { "error": { "code": "model_not_found", "message": "No available channel for model claude-sonnet-5-5 under group default (distributor) (request id: 20261008101503123456789abcdef)", "type": "api_error" } } ``` 404 wrong path: ```json { "error": { "message": "Unknown endpoint. The API lives under /v1 (for example POST /v1/chat/completions).", "type": "invalid_request_error", "param": null, "code": "not_found" } } ``` Branch on the HTTP status first, then on `error.code` where it is set (for example `model_not_found` or `insufficient_user_quota`). Match on the code, not on the message text: messages are written for people and can change. Log `error.message` anyway, since it names the model, group or limit involved. ### On /v1/messages Once a request on the Anthropic endpoint reaches the model step, errors use Anthropic's shape. Checks that run before that (key, model allowlist, IP allowlist, model availability) answer with the OpenAI envelope above on every endpoint. The Anthropic SDKs raise an error from the HTTP status either way. Anthropic-format error: ```json { "type": "error", "error": { "type": "api_error", "message": "Your balance is too low for this request. Add credit under Billing in the console. (request id: 20261008101503123456789abcdef)" } } ``` ### Request id Every response has an `X-Request-Id` header, and gateway error messages end with `(request id: …)`. Log it with failures; it is the fastest way for support to find your request. ## Status codes Fix the request, do not retry Safe to retry with backoff - 400Bad Request Bad RequestFix, do not retry Cause: Invalid JSON, a missing or invalid field, a prompt longer than the context window, or a model that is listed but not priced yet (model_price_error). Fix: Read the message, fix the body, shorten the input. [Details](https://oneaiapikey.com/docs/errors#bad-request) - 401Unauthorized UnauthorizedFix, do not retry Cause: Key missing, mistyped, disabled, expired or deleted, or sent in a header the endpoint does not read. Fix: Check the header and that the environment variable is loaded. Create a new key if needed. [Details](https://oneaiapikey.com/docs/errors#invalid-key) - 403Forbidden ForbiddenFix, do not retry Cause: Balance, plan allowance or the key spending limit is used up; the key may not use this model or group; or the request came from an IP the key does not allow. Fix: Add credit, raise the key limit, or edit the key settings. [Details](https://oneaiapikey.com/docs/errors#insufficient-balance) - 404Not Found Not FoundFix, do not retry Cause: Wrong URL path, usually /v1 missing (OpenAI format) or doubled (Anthropic format). Fix: Use the base URLs exactly as shown in the docs. [Details](https://oneaiapikey.com/docs/errors#error-format) - 413Payload Too Large Payload Too LargeFix, do not retry Cause: The request body is over 128 MB, usually from large base64 images or files. Fix: Shrink or split the input. [Details](https://oneaiapikey.com/docs/errors#bad-request) - 429Too Many Requests Too Many RequestsRetry with backoff Cause: The model provider is throttling, or a per-model rate limit is in force. Fix: Back off and retry. Reduce parallel requests. [Details](https://oneaiapikey.com/docs/errors#rate-limited) - 500Internal Server Error Internal Server ErrorRetry with backoff Cause: Unexpected error in the gateway or at the provider. Fix: Retry with backoff. Report it with the request id if it persists. [Details](https://oneaiapikey.com/docs/errors#upstream-errors) - 501Not Implemented Not ImplementedFix, do not retry Cause: An OpenAI endpoint the gateway does not offer, such as Files, fine-tuning or image variations. Fix: Use a supported endpoint (see the API reference). [Details](https://oneaiapikey.com/docs/errors#not-implemented) - 502Bad Gateway Bad GatewayRetry with backoff Cause: The provider returned an invalid response or dropped the connection. Fix: Retry with backoff. [Details](https://oneaiapikey.com/docs/errors#upstream-errors) - 503Service Unavailable Service UnavailableRetry with backoff Cause: No provider route is available for the model right now, the model is not enabled for your account, or the provider is overloaded. Fix: Check the model id; retry later or switch to a sibling model. [Details](https://oneaiapikey.com/docs/errors#model-not-found) ## Invalid or missing key Every key problem returns `401` with the message `Invalid token`, whatever the exact cause, so check each of these: - Print the variable in the same terminal (`echo $RELAYKIT_API_KEY`, or `$env:RELAYKIT_API_KEY` in PowerShell). An empty value is the most common cause. - Make sure the value starts with `sk-` and has no quotes, spaces or line breaks copied along with it. - OpenAI clients send `Authorization: Bearer`; Anthropic clients send `x-api-key`. `x-api-key` is read only on `/v1/messages` and `/v1/models`; everywhere else use the Bearer header. - Check under [API keys](https://oneaiapikey.com/console/keys) that the key is enabled, not past its expiry date, and not deleted. ## Insufficient balance When the request cannot be paid for, the gateway rejects it before calling the model, with `403`. Nothing is charged. The `code` tells you which limit was hit: - `insufficient_user_quota`: the account balance (or the plan allowance, if billing uses a plan only) is too low. Add credit under [Billing](https://oneaiapikey.com/console/billing). - `pre_consume_token_quota_failed`: this key's own spending limit is reached, even though the account may have money. Raise or remove the limit under [API keys](https://oneaiapikey.com/console/keys). Before a request runs, the gateway reserves an estimate of its cost, so a request can be refused while the balance is still slightly above zero. A lower `max_tokens` lowers the reservation. New balance applies to the next request immediately. > **Avoid surprises:** Give each app or teammate its own key with a spending limit. A runaway agent loop then stops at the limit instead of draining your whole balance. See [Cost control](https://oneaiapikey.com/docs/cost-control). ## Access denied (other 403s) All 403 messages you can get from the gateway, and what each one means: | Message | `code` | Meaning | | --- | --- | --- | | Your balance is too low for this request. Add credit under Billing in the console. | insufficient_user_quota | Your account balance cannot cover the request. | | Your plan allowance is used up or no plan is active. Add balance, or wait for the allowance to reset. | insufficient_user_quota | Billing is set to use a plan only, and the plan allowance is spent. | | token quota is not enough, token remain quota: …, need quota: … | pre_consume_token_quota_failed | This key reached the spending limit you set on it. The account balance is not the problem. | | This token has no access to model … | (empty) | The key has a model allowlist and this model is not on it. | | This key or account cannot use the … group | (empty) | The key is pinned to a group your account cannot use, often after a plan change. | | The … group is no longer available | (empty) | The key is pinned to a group that was retired. | | Your IP address is not on this key's allowed list | access_denied | The key has an IP allowlist and the request came from another address. Check the key’s Allowed IP addresses on the keys page, or clear the field to accept any address. | Spend limit, expiry and model list are on the key's edit dialog under [API keys](https://oneaiapikey.com/console/keys). See [Authentication and keys](https://oneaiapikey.com/docs/authentication). ## Model not found If the gateway cannot route the `model` you sent, it answers `503` with code `model_not_found` and a message that names the model, or with "Model … is not available right now. Try again shortly or pick another model." Some clients show this as "model not found" or 404. - Copy ids exactly from `GET /v1/models` or the [models table](https://oneaiapikey.com/docs/models#current-models). Ids are case-sensitive: `claude-sonnet-5-5`, not a display name. - New accounts start at $0 and see no models until the account has paid $3 or more in total; until then every model returns this error. Add credit under [Billing](https://oneaiapikey.com/console/billing). - Calling an OpenAI-only model on `/v1/messages`? Use the OpenAI endpoint instead (see [models by format](https://oneaiapikey.com/docs/api-reference#model-formats)). ## Bad request and context length A `400` means the request itself needs changing, so retrying it unchanged will fail again. Typical messages mention an invalid parameter, a missing `model`, an image the model cannot read, or a prompt that exceeds the context window. For long conversations, trim or summarise old turns, and keep `max_tokens` within the model's output limit. - `model_price_error` ("Model … has not been priced by the administrator yet"): the model is listed but not open for use yet. Pick another model, or [tell us](mailto:support@relaykit.dev) which one you need. - `413`: the body is larger than 128 MB, almost always because of inline images or files. Resize them or send fewer per request. ## Not implemented (501) Some OpenAI endpoints are not offered: Files (`/v1/files`), fine-tuning, image variations, deleting models, and `/v1/messages/count_tokens`. They return `501` with code `api_not_implemented` (or `404`). The [API reference](https://oneaiapikey.com/docs/api-reference#endpoints) lists what is available. ## Rate limited (429) `429` means slow down, not stop. Today it almost always comes from the model provider during peak load; the gateway does not apply its own per-account request limit on the API. If a per-model limit is switched on, its message says so: "Rate limit reached: at most N requests per M minutes". Retry with exponential backoff, honour a `Retry-After` header when present, and cap how many requests you run in parallel. See [Rate limits and retries](https://oneaiapikey.com/docs/rate-limits). ## Upstream errors (5xx) `500`, `502` and `503` are usually short-lived: a provider hiccup or a capacity spike for one model. Retry with backoff. The gateway does not retry for you, so a failed attempt reaches your code as it happened. If one model keeps failing, switch to a sibling model temporarily; the rest of your code does not change because every model shares the same API. ## Timeouts - **Streaming**: the gateway closes a stream when the provider sends nothing for 300 seconds. Reasoning models with long thinking phases are the usual cause. - **Non-streaming**: the gateway does not cut the request off, but your HTTP client and any proxy on the way may. A long answer with nothing sent until the end is the most likely to time out. - Use `stream: true` for long outputs: tokens flow continuously, so nothing sits idle. Set your client's read timeout to a few minutes. - Do not blindly retry a timed-out request: the model may have finished and been billed even though your client never saw the answer. If you disconnect early, you pay for the tokens produced up to that point. ## Retries with backoff Retry only on `429`, `500`, `502`, `503` and network errors. Wait longer after each attempt (1s, 2s, 4s… with random jitter) and give up after about five tries. Never retry `400`, `401`, `403`, `404` or `501`: they will fail the same way. Python: ```python import os, random, time from openai import OpenAI, APIStatusError, APIConnectionError client = OpenAI( base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"], max_retries=0, # we handle retries ourselves below ) RETRYABLE = {429, 500, 502, 503} def backoff(attempt, retry_after=None): if retry_after and retry_after.isdigit(): return int(retry_after) # honour the server's hint return min(30, 2 ** attempt) + random.random() # 1s, 2s, 4s… + jitter def chat(attempts=5, **kwargs): for attempt in range(attempts): try: return client.chat.completions.create(**kwargs) except APIStatusError as e: if e.status_code not in RETRYABLE or attempt == attempts - 1: raise time.sleep(backoff(attempt, e.response.headers.get("retry-after"))) except APIConnectionError: if attempt == attempts - 1: raise time.sleep(backoff(attempt)) reply = chat(model="gpt-5.6-terra", messages=[{"role": "user", "content": "ping"}]) ``` Node: ```js const RETRYABLE = new Set([429, 500, 502, 503]); const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const backoff = (i) => Math.min(30_000, 1000 * 2 ** i) + Math.random() * 1000; export async function chat(body, attempts = 5) { for (let i = 0; i < attempts; i++) { let res; try { res = await fetch("https://api.oneaiapikey.com/v1/chat/completions", { method: "POST", headers: { Authorization: `Bearer ${process.env.RELAYKIT_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify(body), }); } catch (err) { if (i === attempts - 1) throw err; // network error await sleep(backoff(i)); continue; } if (res.ok) return res.json(); if (!RETRYABLE.has(res.status) || i === attempts - 1) { throw new Error(`${res.status} ${res.headers.get("x-request-id")}: ${await res.text()}`); } const hint = Number(res.headers.get("retry-after")); await sleep(hint > 0 ? hint * 1000 : backoff(i)); } } ``` > **SDKs already retry:** The official OpenAI and Anthropic SDKs retry connection errors, `429` and `5xx` a couple of times by default. Tune it with `max_retries` (Python) or `maxRetries` (Node) instead of wrapping your own loop around theirs. ## Getting help If an error persists, email [support@relaykit.dev](mailto:support@relaykit.dev) with: - the request id (from the `X-Request-Id` header or the end of the error message); - the time of the request (with timezone), the endpoint and the model id; - the HTTP status and the full error message; - the key's name or its last four characters. **Never send the full key.** Minimal reproduction to include: ```bash curl -i https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "claude-haiku-4-5", "messages": [{"role": "user", "content": "ping"}]}' ``` --- # Performance metrics explained > What first-token time, total duration and tokens per second measure, where the gateway takes each one, what changes them, and how to read them in the console. Source: https://oneaiapikey.com/docs/performance Three numbers describe how fast a model answers: how long until the first token arrives, how long the whole request takes, and how quickly tokens come once they start. The gateway records the total duration of every request and the first-token time of every streamed one; the third number follows from them. This page explains each one and what moves it. It contains no benchmark figures: speed depends on the model, the prompt and the provider’s load at that moment, so measure your own traffic. ## The three numbers | Metric | What it measures | Where it is taken | | --- | --- | --- | | **First-token time** | From the gateway receiving your request to the first chunk of the answer arriving from the provider. | At the gateway, in milliseconds. Streamed requests only. | | **Total duration** | From the gateway receiving your request to the end of the answer. | At the gateway, in whole seconds. Every request. | | **Tokens per second** | Output tokens divided by the time spent producing them, that is total duration minus first-token time. | Calculated from the two above and the output token count. | First-token time is what a person in a chat feels as “waiting”. Tokens per second is how fast the text then fills in. Total duration is what a batch job or an agent loop pays for each call. > **Streamed requests only:** A non-streamed request returns nothing until the answer is complete, so it has no separate first-token time. Its usage record shows the total duration only. Send `"stream": true` if you want both numbers. ## Read them in the console - **Usage averages.** Above the chart, the [usage page](https://oneaiapikey.com/console/usage) shows the **Success rate**, **Avg first token** and **Output speed** for the selected range. First token and output speed average the newest streamed requests in the range (up to 100), and success rate compares billed requests with failed attempts. - **Usage page.** Each request on the [usage page](https://oneaiapikey.com/console/usage) shows its duration, and for streamed requests its first-token time in seconds. Requests that finished in under a second show `<1 s`. - **CSV export.** The export has a `first_token_ms` column (empty for non-streamed requests) and a `duration_s` column, so you can work out medians and percentiles in a spreadsheet. - **Playground.** The [playground](https://oneaiapikey.com/console/playground) shows the first-token time measured in your browser. It includes the network between you and the gateway, so it is a little higher than the gateway’s figure for the same request. ## What makes them rise or fall **First-token time** grows with: - **Prompt length.** The model reads the whole prompt before it writes anything, so a long conversation history or a large document takes longer to start. - **Reasoning.** [Reasoning models](https://oneaiapikey.com/docs/reasoning) think before they answer. Depending on the model, the first chunk is either thinking text or arrives only after the thinking is done. A lower `reasoning_effort` starts the answer sooner. - **Provider load.** At busy times a provider may queue requests before running them. - **Cache misses.** With [prompt caching](https://oneaiapikey.com/docs/prompt-caching), a repeated prefix can be read from the provider’s cache instead of being processed again, which can shorten the wait on long prompts. **Total duration** grows mostly with the number of **output tokens**. A model that writes twice as much takes roughly twice as long once it has started. Capping `max_tokens` and asking for shorter answers is the most reliable way to cut it. **Tokens per second** depends mainly on the **model**: smaller and faster tiers generate quicker than flagship models, and the same model can vary with provider load. Tool-using agents add one more factor: each tool call is a separate request, so an agent turn that calls five tools is six round trips. Their durations add up. ## Read them carefully - **Compare medians, not single requests.** One slow request says little. Export a day of usage and look at the median and the slowest 5 %. - **Ignore tokens per second on short requests.** Duration is recorded in whole seconds, so for a request that takes one or two seconds the figure is coarse. It becomes meaningful once requests run for several seconds. - **Compare like with like.** A 20,000-token prompt starts slower than a 200-token one on any model. Compare models on the same prompt. - **Count your own network.** The gateway’s figures start when the request reaches it. Your users also wait for the trip from their device to your server and on to the gateway. ## Measure it yourself This script streams one answer and prints first-token time, total time and tokens per second as your code sees them, including the network: Python: ```python import os, time from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) start = time.perf_counter() first = None usage = None stream = client.chat.completions.create( model="claude-haiku-4-5", messages=[{"role": "user", "content": "Explain DNS in one paragraph."}], stream=True, stream_options={"include_usage": True}, ) for chunk in stream: if first is None and chunk.choices and chunk.choices[0].delta.content: first = time.perf_counter() # first visible text if chunk.usage: usage = chunk.usage end = time.perf_counter() first = first or end # no visible text (for example, only reasoning) print(f"first token: {(first - start) * 1000:.0f} ms") print(f"total: {end - start:.2f} s") if usage and end > first: print(f"tokens/s: {usage.completion_tokens / (end - first):.1f}") ``` Node: ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const start = performance.now(); let first = 0; let usage; const stream = await client.chat.completions.create({ model: "claude-haiku-4-5", messages: [{ role: "user", content: "Explain DNS in one paragraph." }], stream: true, stream_options: { include_usage: true }, }); for await (const chunk of stream) { if (!first && chunk.choices[0]?.delta?.content) first = performance.now(); // first visible text if (chunk.usage) usage = chunk.usage; } const end = performance.now(); first ||= end; // no visible text (for example, only reasoning) console.log(`first token: ${Math.round(first - start)} ms`); console.log(`total: ${((end - start) / 1000).toFixed(2)} s`); if (usage && end > first) console.log(`tokens/s: ${(usage.completion_tokens / ((end - first) / 1000)).toFixed(1)}`); ``` Run it a few times with your real prompt and model, and compare with the figures on the usage page. ## Related - [Streaming](https://oneaiapikey.com/docs/streaming): how to stream in both formats and get usage in the last chunk. - [Rate limits and retries](https://oneaiapikey.com/docs/rate-limits): timeouts for long requests. - [Routing and privacy](https://oneaiapikey.com/docs/routing-privacy): what the usage record contains. --- # Routing and privacy > What happens to a request between your code and the model, when the gateway retries (it does not), what it records about each call, and what it never stores. Source: https://oneaiapikey.com/docs/routing-privacy Every request you send to `api.oneaiapikey.com` goes through the same short path: the gateway checks it, forwards it to the provider that runs the model, streams the answer back and writes one usage record. This page describes each step, what is kept and what is not. ## How a request flows 1. **Your code sends the request** with your API key to `api.oneaiapikey.com`, in OpenAI or Anthropic format. 2. **The gateway checks the key.** It must exist, be enabled and not expired. If you set a spend limit, a model list or an IP allowlist on the key, the request must fit them. A failed check returns `401` or `403` and nothing is billed. See [Errors](https://oneaiapikey.com/docs/errors#access-denied). 3. **The gateway checks your balance.** It estimates the cost from the prompt. If your balance or the key’s spend limit cannot cover that estimate, the request stops here with `403`. 4. **The gateway forwards the request** to the provider that operates the model, using Relaykit’s own provider credentials. Your API key is never sent upstream. If your request and the provider use different formats, the gateway converts between them. 5. **The answer comes back** through the gateway. Streamed answers are passed on chunk by chunk as they arrive. 6. **The gateway meters the request.** It takes the token counts the provider reports, prices them at the [published rates](https://oneaiapikey.com/docs/models#current-models), settles the reserved amount and writes one usage record. ## Retries and failover The gateway makes **one attempt** per request. If the provider answers with an error, or the connection to it fails, you get that error back with a request id. The gateway does not resend the request to another provider and does not switch you to a different model. This keeps behaviour predictable: the model you name is the model that answers, and you are never billed twice for one call. It also means retrying is up to your code. The official OpenAI and Anthropic SDKs already retry `429`, `5xx` and connection errors; [Rate limits and retries](https://oneaiapikey.com/docs/rate-limits) shows how to tune that and when to switch to a sibling model. ## What the gateway records Each request leaves one usage record, which you can see on the [usage page](https://oneaiapikey.com/console/usage) and export as CSV: | Field | Example | Notes | | --- | --- | --- | | Time | date and time | When the request finished. The CSV uses UTC. | | Key | the key’s name | The name you gave the key, never the key itself. | | Model | the model id you sent | | | Tokens | input, output, cached | As reported by the provider. | | Cost | the amount deducted | Tokens × rates, see [Pricing and billing](https://oneaiapikey.com/docs/billing). | | Duration | whole seconds | From the gateway receiving the request to the last byte. | | First token | milliseconds | Streamed requests only. See [Performance metrics](https://oneaiapikey.com/docs/performance). | | Stream | yes or no | | | Request id | a long id | Also in the `X-Request-Id` response header. Quote it when you contact support. | Failed requests leave a record too, with the error message instead of a cost. Usage records do not include your IP address or your user agent. The gateway’s server log also records each HTTP request’s time, path, status code, duration and client IP address. It is used for security and troubleshooting and is not shown in the console. > **Prompts and answers are not stored:** The gateway relays the content of your requests in memory to complete them. It does not write prompts, completions, images, files, tool definitions or tool results to the usage records or to its server log. Usage records hold token counts, not text. ## What the provider receives To answer, the provider that runs the model receives the full request: your messages, system prompt, images, tools and parameters. Each provider processes it under its own API terms, which can include short-term retention for abuse monitoring. The [privacy policy](https://oneaiapikey.com/legal/privacy#prompts) explains this and names the providers. The provider does not receive your API key, your email address or your account details. Fields you put in the request body yourself, such as `user` or `metadata`, are forwarded with it, so leave personal data out of them. ## Chat in the browser Conversations on the [Relaykit chat](https://oneaiapikey.com/docs/chat) are stored only in your browser, separately for each account you sign in with. They are not uploaded, not synced between devices and not kept on our servers. Clearing this site’s data in your browser deletes them. Each message you send is still an API request: it travels the path above, reaches the provider, and leaves a usage record with tokens and cost, but no text. ## Retention How long usage records, server logs and billing records are kept, and how to ask for a copy or for deletion, is set out in the [privacy policy](https://oneaiapikey.com/legal/privacy#retention). Relaykit does not train models on your prompts or outputs and does not sell them. ## Related - [Authentication and keys](https://oneaiapikey.com/docs/authentication#per-key-settings): spend limits, model lists and expiry per key. - [Account security and alerts](https://oneaiapikey.com/docs/account-security): two-factor sign-in, IP allowlists and low-balance alerts. - [Errors](https://oneaiapikey.com/docs/errors): every status code and what it means. --- # Migrating > Move existing code from the OpenAI API, the Anthropic API, OpenRouter or another gateway. Change the base URL, key and model ids, then check the few features that differ. Source: https://oneaiapikey.com/docs/migrate Most code moves over with three changes: the **base URL**, the **API key**, and the **model id**. Request and response shapes stay the same, because Relaykit speaks the OpenAI and Anthropic formats directly. This page lists those changes and the features that behave differently. ## The short version 1. Create a key under [API keys](https://oneaiapikey.com/console/keys). 2. Point your SDK or tool at the base URL for the format it uses. 3. Replace model ids with ids from the [models table](https://oneaiapikey.com/docs/models#current-models). 4. Run your tests, then read the section below for where you came from. If your code reads the standard environment variables, you may not need to touch it at all: .env: ```bash # OpenAI SDKs (Python, Node, and most tools built on them) OPENAI_BASE_URL=https://api.oneaiapikey.com/v1 OPENAI_API_KEY= # Anthropic SDKs (no /v1 at the end) ANTHROPIC_BASE_URL=https://api.oneaiapikey.com ANTHROPIC_API_KEY= ``` ## From the OpenAI API Python: ```python import os from openai import OpenAI # Before: client = OpenAI() client = OpenAI( base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"], ) reply = client.chat.completions.create( model="gpt-5.6-terra", # an id from the models table messages=[{"role": "user", "content": "Hello"}], ) ``` Node: ```js import OpenAI from "openai"; // Before: const client = new OpenAI(); const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY, }); const reply = await client.chat.completions.create({ model: "gpt-5.6-terra", // an id from the models table messages: [{ role: "user", content: "Hello" }], }); ``` - **Works the same:** chat completions, streaming with `stream_options`, tools and parallel tool calls, `response_format` on GPT models, images in messages, `reasoning_effort`, and the [Responses API](https://oneaiapikey.com/docs/responses) on GPT models. - **Stateless only:** the Responses API does not store responses for you to fetch later. Send the full history each time instead of relying on stored state. - **Not available:** Assistants, Threads, Files, Batch, fine-tuning and image variations. Requests to them fail with `404` or `501`. - **Other models, same code:** you can now send the same request to Claude, Grok, DeepSeek and other families. Read [Claude models in this format](https://oneaiapikey.com/docs/chat-completions#claude-models-in-this-format) before you switch a request that uses `response_format`. ## From the Anthropic API Python: ```python import anthropic, os # Before: client = anthropic.Anthropic() client = anthropic.Anthropic( base_url="https://api.oneaiapikey.com", # no /v1: the SDK adds /v1/messages api_key=os.environ["RELAYKIT_API_KEY"], ) message = client.messages.create( model="claude-sonnet-5-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], ) ``` Node: ```js import Anthropic from "@anthropic-ai/sdk"; // Before: const client = new Anthropic(); const client = new Anthropic({ baseURL: "https://api.oneaiapikey.com", // no /v1: the SDK adds /v1/messages apiKey: process.env.RELAYKIT_API_KEY, }); const message = await client.messages.create({ model: "claude-sonnet-5-5", max_tokens: 1024, messages: [{ role: "user", content: "Hello" }], }); ``` - **Works the same:** Messages with streaming, system prompts, tools, images and PDFs, `cache_control` prompt caching, extended `thinking`, and `anthropic-beta` headers, which are passed through. - **Base URL:** no `/v1` at the end. The SDKs and Claude Code add `/v1/messages` themselves. - **Not available:** token counting (`/v1/messages/count_tokens`), Message Batches and the Files API. Estimate tokens on your side, or read `usage` from the reply. - **Claude Code and agents:** see the [Claude Code guide](https://oneaiapikey.com/docs/guides/claude-code) for the two environment variables it needs. ## From OpenRouter or another gateway - **Model ids** have no provider prefix here. Look up each model in the [models table](https://oneaiapikey.com/docs/models#current-models); spellings and version numbers can differ from other gateways, so copy the id rather than editing the old one. - **The OpenAI-style request** is the same, including the `reasoning` object (`{"effort": "high"}`). Extra attribution headers such as `HTTP-Referer` or `X-Title` are not needed and can stay. - **Routing options** such as provider preferences or a `models` fallback list are not used here. Each model id goes to one route. If you relied on automatic fallback, put the fallback in your own code (see [Rate limits and retries](https://oneaiapikey.com/docs/rate-limits#when-one-model-keeps-failing)). - **Billing** is a prepaid balance in US dollars. Each request is billed at the rates on the [models page](https://oneaiapikey.com/docs/models#current-models); there is no per-request platform fee on top. ## Features that differ by model > **Check these before you switch models:** - `response_format` (JSON mode and JSON Schema) is dropped when a Claude model is called through chat completions. Use a forced tool call or `/v1/messages` instead. See [Structured output](https://oneaiapikey.com/docs/structured-output#claude-models). > - Prompt caching on Claude works only through `/v1/messages`. See [Prompt caching](https://oneaiapikey.com/docs/prompt-caching). > - Claude needs `max_tokens`; through chat completions it defaults to 8192 when you leave it out. ## Check that it worked Send one cheap request, then open the [usage page](https://oneaiapikey.com/console/usage). The request should be listed with its model, key, tokens and cost. If it fails, the [Errors](https://oneaiapikey.com/docs/errors) page explains each status code. ## Related - [Quickstart](https://oneaiapikey.com/docs/quickstart): your first request from scratch. - [SDKs and frameworks](https://oneaiapikey.com/docs/sdks): base URL settings for each library. --- # Pricing and billing > How the prepaid balance works, what each request costs, how to add credit, which models are free, how plans will work, and where to see every charge. Source: https://oneaiapikey.com/docs/billing Relaykit is prepaid. You add credit to one balance, and every request deducts its cost at the published per-token rate of the model you called. Nothing renews or charges on its own, and pay as you go has no monthly fee. ## How a request is billed - Each model has an input rate and an output rate per million tokens, listed in the [models table](https://oneaiapikey.com/docs/models#current-models). Some also have a lower cached-input rate (see [Prompt caching](https://oneaiapikey.com/docs/prompt-caching)). - A request costs its input tokens at the input rate plus its output tokens (including any reasoning tokens) at the output rate. The [cost control](https://oneaiapikey.com/docs/cost-control#what-a-request-costs) page has worked examples. - Before a request runs, the gateway checks that your balance and the key’s spend limit can cover an estimate of it. After it finishes, you are charged for the tokens actually used. - Requests that fail before the model produces anything are not charged. A stream you stop early is charged for the tokens produced until then. ## Your balance 1 credit is $1 of usage at the published rates. The [billing page](https://oneaiapikey.com/console/billing) shows: - your **balance**, rounded down to the cent, so the console never shows money you do not have; - **spent to date** and the number of requests made; - the **credit history**: every addition to your balance, with its date and source; - a notice when the balance is empty. When the balance reaches $0, paid requests are refused with HTTP `403` and the message “Your balance is too low for this request. Add credit under Billing in the console.” Nothing else happens to your account or keys, and requests work again as soon as you add credit. The check runs when a request starts: a reply that is already running finishes and is billed in full, so the balance can end slightly below $0, and the next top-up covers that amount first. `max_tokens` limits how far one reply can go. New accounts start at $0: there is no sign-up credit. Models, including free models and promotions, become available once the account has paid at least $3 in total, and each top-up is at least $3. Until then, requests are refused. The conditions are in the [Terms of Service](https://oneaiapikey.com/legal/terms#trial). ## Add credit Credit comes in packs at $1 per credit, whatever the size, and credit does not expire while your account is open. | Pack | Credit | Price | Per credit | | --- | --- | --- | --- | | Starter | $10 | $10 | $1 | | Builder | $50 | $50 | $1 | | Team | $200 | $200 | $1 | Online checkout is not open yet. To buy a pack today, use **Email to buy** next to the pack on the [billing page](https://oneaiapikey.com/console/billing), or write to [support@relaykit.dev](mailto:support@relaykit.dev) with your username and the pack you want. We reply with how to pay and add the credit to your balance once the payment is received. Prices are in US dollars. Refunds are covered by the [refund policy](https://oneaiapikey.com/legal/refund). ## Free models No model is free at the moment. When one is, it is listed here and marked "Free" in the models table. ## Plans **Plans are not open yet.** Pay as you go is the only way to pay today. The plans below are what we intend to offer; prices and allowances may still change before they open. | Plan | Price / month | Uncached input / week | Up to, with cache hits | Rates while subscribed | | --- | --- | --- | --- | --- | | Pro | $5 | 3.4M | 13M | Published rates | | Plus | $9 | 6.4M | 25M | 2% off the published rates | | Max | $29 | 21.2M | 81M | 4% off the published rates | - The allowance pays for any model, at the published rate less the plan’s discount where it has one; the same rates apply when the balance pays. The token figures are measured on [DeepSeek V4 Flash](https://oneaiapikey.com/models/deepseek-v4-flash): a model with higher rates uses the allowance up faster. The allowance refills every Monday at 00:00 UTC; unused allowance does not carry over. - Input served from the provider’s cache uses 1/50 as much allowance as uncached input, and output tokens count 4 times. The uncached number is what the allowance always pays for; the “up to” number assumes about 76% of input is cache hits, which is common for coding agents that reuse long prompts. - **First-month trial:** $3 once, by card, for the first month of Pro. One per account; it does not renew. - A plan is paid for one month at a time and does not renew on its own. - Under **Plans** in the console you choose what pays first when you have both a plan and a balance: the plan, the balance, or only one of them. - When the allowance is used up, the balance pays. If you chose to pay from the plan only, requests are refused with “Your plan allowance is used up or no plan is active. Add balance, or wait for the allowance to reset.” - The [pricing page](https://oneaiapikey.com/pricing#allowances) has worked examples and a calculator. ## See every charge The [usage page](https://oneaiapikey.com/console/usage) lists each request with its time, model, key, tokens and cost. Filter by date range (up to 30 days at a time), model or key, and export the view as CSV (up to 5,000 rows per export). > **Keep spending predictable:** Give each app its own key with a spend limit. A key that reaches its limit stops on its own; your balance and other keys are untouched. See [Cost control](https://oneaiapikey.com/docs/cost-control#limit-what-a-key-can-spend). ## Related - [Models](https://oneaiapikey.com/docs/models#current-models): every rate. - [Errors](https://oneaiapikey.com/docs/errors#insufficient-balance): balance and plan errors. --- # Account security and alerts > Protect your account with two-factor sign-in, lock API keys to IP addresses, cap their spend, and get an email or webhook alert before your balance runs out. Source: https://oneaiapikey.com/docs/account-security Your Relaykit account holds prepaid money, and your API keys can spend it. This page covers the settings that protect both: how you sign in, what each key may do, and how to hear about a low balance before requests start failing. Account settings are on the [Settings](https://oneaiapikey.com/console/settings) page of the console; key settings are on the [API keys](https://oneaiapikey.com/console/keys) page. ## Signing in ### Two-factor authentication With two-factor authentication on, signing in needs your password and a six-digit code from an authenticator app such as 1Password, Bitwarden, Google Authenticator or Aegis. Sensitive changes, such as turning two-factor off or deleting the account, ask for a code too. 1. On the [Settings](https://oneaiapikey.com/console/settings) page, choose **Set up two-factor**. Confirm your password if asked. Setup must be finished within 5 minutes. 2. Add the account to your authenticator app with the setup key the console shows. It is a standard time-based key that makes six-digit codes. 3. Save the backup codes shown during setup somewhere safe, away from your phone. Each one works once in place of an app code. 4. Enter the current code from the app as the **6-digit code** and choose **Turn on**. To turn two-factor off (**Turn off**) or get new backup codes (**New backup codes**), confirm with a code from the app or a backup code in the **Authentication code** field. New backup codes replace all the old ones. After several wrong codes in a row, code checks pause for a while before you can try again. If you lose your phone, sign in with a backup code and set up the app again. If you have lost both, write to [support@relaykit.dev](mailto:support@relaykit.dev) from the account’s email address. ### Signed-in devices The Settings page lists the browsers signed in to your account. End any session you do not recognise, or sign out everywhere else at once. Changing your password also signs out every other device. Your API keys keep working in both cases; they are separate from sign-in sessions. ## Protecting API keys Each key can carry its own limits. Set them when you create the key, or later by editing it on the [API keys](https://oneaiapikey.com/console/keys) page. | Setting | What it does | When a request breaks it | | --- | --- | --- | | **IP allowlist** | Only requests from the listed addresses are accepted. Set it in the **Allowed IP addresses** field when you create or edit a key. One IP address or CIDR range per line, for example `203.0.113.7` or `203.0.113.0/24`. IPv6 works too. Empty means any address. | `403` with “Your IP address is not on this key’s allowed list” | | **Spend limit** | The key stops after spending this many dollars. | `403` while the rest is too small for one request, then `401` once used up | | **Expiry date** | The key stops at the end of that day. | `401` | | **Model list** | The key can only call the models you pick. | `403` with “This token has no access to model …” | [Authentication and keys](https://oneaiapikey.com/docs/authentication#per-key-settings) explains spend limits, expiry dates and model lists in detail. The allowlist checks the address the request comes from as the gateway sees it: for a server behind NAT or a cloud load balancer, that is its public outgoing address, not its private one. The **Last used** column on the keys page shows when each key was last used for a billed request, which makes forgotten keys easy to spot. A key that only calls free models may show as never used. Disable or revoke keys you no longer need. > **A good default for production keys:** One key per app or server, an IP allowlist with that server’s address, a spend limit you could afford to lose, and only the models the app calls. ## Low-balance alerts A low-balance alert tells you when your balance falls below a level you choose, so you can top up before requests start failing with `403`. On the [Settings](https://oneaiapikey.com/console/settings) page, under **Low-balance alerts**, choose **Email** or **Webhook** under **Send alerts by**, enter the level in **Alert when the balance is below (USD)** (for a webhook also the **Webhook URL** and, optionally, a **Signing secret**), then choose **Save alerts**. How alerts behave: - The gateway checks the balance after every billed request. If the balance left after that request is below your level, it sends an alert. - While the balance stays below the level, alerts repeat, at most two in any ten minutes. - If you set no level, the service’s default level applies. ### By webhook The gateway sends a `POST` request with a JSON body to your URL. Any `2xx` response counts as delivered. Use a public HTTPS URL: the gateway does not send alerts to `localhost` or to private-network addresses. | Field | Contents | | --- | --- | | `type` | `quota_exceed` for a low-balance alert. | | `title` | A short heading. | | `content` | The message text. | | `values` | The parts filled into `content`: the heading, your balance as text and a link for adding credit. | | `timestamp` | When the alert was sent, in Unix seconds. | Base your handling on `type` and write your own message. The wording of `title` and `content` may change and is not guaranteed to be in English. If you set a secret, each request carries an `X-Webhook-Signature` header: the hex-encoded HMAC-SHA256 of the raw request body, keyed with your secret. Check it before you act on the alert: Python: ```python import hashlib, hmac def is_valid(raw_body: bytes, signature: str | None, secret: str) -> bool: """raw_body: the request body exactly as received, before any JSON parsing.""" expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature or "") # signature = request.headers.get("X-Webhook-Signature") ``` Node: ```js import { createHmac, timingSafeEqual } from "node:crypto"; // rawBody: the request body exactly as received (a Buffer), before JSON.parse export function isValid(rawBody, signature, secret) { const expected = Buffer.from(createHmac("sha256", secret).update(rawBody).digest("hex")); const given = Buffer.from(signature ?? ""); return expected.length === given.length && timingSafeEqual(expected, given); } // const signature = req.headers["x-webhook-signature"]; ``` ### By email If your account has an email address, alerts go to it by default, even before you save anything: then they start once the balance is nearly used up. Save a level to be warned earlier. Each email states your balance and links to **Billing**. Alerts go only to the address on your account, never to another one. Accounts without an email address get alerts by webhook. ## If a key leaks 1. On the [API keys](https://oneaiapikey.com/console/keys) page, disable the key straight away. It stops working at once. 2. Create a replacement with an IP allowlist and a spend limit, and deploy it. 3. On the [usage page](https://oneaiapikey.com/console/usage), filter by the old key to see what it was used for. 4. Revoke the old key. 5. Remove the key from wherever it leaked: repository history, logs, shared documents. If you see usage you did not make, write to [support@relaykit.dev](mailto:support@relaykit.dev) with the key’s name and the time range. ## Related - [Authentication and keys](https://oneaiapikey.com/docs/authentication): sending keys, rotating them, and every per-key setting. - [Cost control](https://oneaiapikey.com/docs/cost-control): spend limits, cheaper models and caching. - [Routing and privacy](https://oneaiapikey.com/docs/routing-privacy): what the gateway records about each request. --- # Coding tool setup guides > Step-by-step setup for Claude Code, Codex CLI, Cursor, Cline and other coding tools: base URL, API key, model ids and fixes for common errors. Source: https://oneaiapikey.com/docs/guides Every tool below works with one key from your Relaykit account. The only thing that changes between them is which API format the tool speaks, and therefore which base URL you paste in. Pick your tool for exact field names, recommended model ids and a quick way to verify the connection. ## Anthropic-format tools Set the gateway origin (no /v1) as the Anthropic base URL. Base URL: `https://api.oneaiapikey.com` - [Claude Code (CLI): Run Claude Code on prepaid credits: set ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN, pin Opus, Sonnet and Haiku model ids, then verify with /status.](https://oneaiapikey.com/docs/guides/claude-code) ## OpenAI-compatible tools Choose an "OpenAI compatible" provider and use the /v1 base URL. Base URL: `https://api.oneaiapikey.com/v1` - [Codex CLI (CLI): Connect OpenAI Codex CLI to our gateway with a model_providers entry in ~/.codex/config.toml, a key env var and the Responses wire API, plus checks and fixes.](https://oneaiapikey.com/docs/guides/codex-cli) - [Cursor (IDE): Use Claude, GPT, Grok and DeepSeek in Cursor Chat and Agent via your OpenAI API key and Override OpenAI Base URL, with custom models, checks and known limits.](https://oneaiapikey.com/docs/guides/cursor) - [Cline (Extension): Configure Cline in VS Code with the OpenAI Compatible provider: base URL, API key and model id for Claude, GPT, Grok or DeepSeek, then verify it and fix errors.](https://oneaiapikey.com/docs/guides/cline) - [Kilo Code (Extension): Add our gateway to Kilo Code as a custom OpenAI Compatible provider in VS Code or in kilo.jsonc for the CLI, with model ids, auto-fetched models and fixes.](https://oneaiapikey.com/docs/guides/kilo-code) - [OpenCode (CLI): Add our gateway to OpenCode as a custom provider in opencode.json with @ai-sdk/openai-compatible, store the key via /connect, pick models and fix errors.](https://oneaiapikey.com/docs/guides/opencode) - [Aider (CLI): Run Aider through our OpenAI-compatible endpoint: set OPENAI_API_BASE and OPENAI_API_KEY, launch with openai/, save it in .aider.conf.yml and fix errors.](https://oneaiapikey.com/docs/guides/aider) - [Zed (IDE): Add our gateway to Zed via language_models.openai_compatible in settings.json, enter your key, pick Claude, GPT, Grok or DeepSeek in the Agent Panel and fix errors.](https://oneaiapikey.com/docs/guides/zed) Using something that is not listed? Any client that lets you set a custom OpenAI or Anthropic base URL will work. Start with the [quickstart](https://oneaiapikey.com/docs/quickstart), then check the [API reference](https://oneaiapikey.com/docs/api-reference) for supported endpoints. --- # Claude Code setup: point it at a custom Anthropic base URL > Run Claude Code on prepaid credits: set ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN, pin Opus, Sonnet and Haiku model ids, then verify with /status. Source: https://oneaiapikey.com/docs/guides/claude-code Base URL: `https://api.oneaiapikey.com` Claude Code speaks the Anthropic Messages API, and so does our gateway. That means no proxy scripts, no wrappers and no patched binaries: you set two environment variables and Claude Code sends its requests to us instead of to Anthropic. Usage is deducted from your prepaid balance at the published per-model rates. ## What you need - Claude Code installed and working in your terminal (any recent version). - An account with a positive balance (top-ups start at $3). [Create an account](https://oneaiapikey.com/console/sign-up) if you don’t have one. - An API key from the [API keys page](https://oneaiapikey.com/console/keys). Keys start with `sk-`. Copy it once and keep it somewhere safe. ## Step 1: set the base URL and token Claude Code reads `ANTHROPIC_BASE_URL` to decide where requests go and `ANTHROPIC_AUTH_TOKEN` for the credential, which it sends as an `Authorization: Bearer` header. Use the gateway origin **without** `/v1`: Claude Code appends `/v1/messages` itself. Start with a quick test in a single terminal session: macOS / Linux / Git Bash: ```bash export ANTHROPIC_BASE_URL=https://api.oneaiapikey.com export ANTHROPIC_AUTH_TOKEN=sk-your-key-here ``` Windows PowerShell: ```powershell $env:ANTHROPIC_BASE_URL = "https://api.oneaiapikey.com" $env:ANTHROPIC_AUTH_TOKEN = "sk-your-key-here" ``` Then run `claude` from that same terminal so it inherits the variables. ## Step 2: make it permanent with settings.json Shell variables vanish when the terminal closes, and editors launched from a dock or Start menu never see them. For a setup that works everywhere, put the values in the `env` block of your user settings file: `~/.claude/settings.json` on macOS and Linux, `%USERPROFILE%\.claude\settings.json` on Windows. ~/.claude/settings.json: ```json { "env": { "ANTHROPIC_BASE_URL": "https://api.oneaiapikey.com", "ANTHROPIC_AUTH_TOKEN": "sk-your-key-here", "ANTHROPIC_MODEL": "claude-sonnet-5", "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-5-5", "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-5", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5" } } ``` If the file already exists, merge the `env` keys into it rather than replacing the whole file. When a shell variable and the settings file disagree, the settings file wins. > Never put your key in a project’s `.claude/settings.json`. That file is usually committed to git and shared with everyone who clones the repository. Use the user-level file, or a project’s `.claude/settings.local.json` that is listed in your gitignore. Using the Claude Code extension for VS Code? Add the same two variables under `claudeCode.environmentVariables` in your VS Code user settings, because the extension checks credentials there before it starts. ## Step 3: choose your models `ANTHROPIC_MODEL` sets the default for the session. The three `ANTHROPIC_DEFAULT_*_MODEL` variables decide what Claude Code uses when you (or a sub-agent) pick the Opus, Sonnet or Haiku slot in `/model`. The Haiku slot also handles lightweight background work such as session titles, so pointing it at the fast model keeps those calls cheap. Over the Anthropic route we serve Claude models only, which is also the only combination Claude Code is designed for. Current rates (USD per 1M tokens): | Model id | Good for | Input | Output | | --- | --- | --- | --- | | `claude-opus-5-5` | Agentic coding, Large refactors, Complex reasoning | $4.40 | $22 | | `claude-sonnet-5` | Daily coding, Code review, Writing | $2.20 | $11 | | `claude-haiku-4-5` | Sub-agents, Extraction, Chat | $1.10 | $5.50 | A practical default: Sonnet for everyday work, switch to Opus with `/model` for a hard refactor, and leave Haiku on background and sub-agent duty. See the full list on the [models page](https://oneaiapikey.com/models). ## Step 4: verify it works First, test the key and URL directly, outside Claude Code. A JSON reply containing `"content"` means the gateway accepted your key: curl: ```bash curl -sS "$ANTHROPIC_BASE_URL/v1/messages" \ -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{"model":"claude-haiku-4-5","max_tokens":16,"messages":[{"role":"user","content":"Say hi"}]}' ``` PowerShell: ```powershell Invoke-RestMethod -Method Post -Uri "$env:ANTHROPIC_BASE_URL/v1/messages" ` -Headers @{ "Authorization" = "Bearer $env:ANTHROPIC_AUTH_TOKEN"; "anthropic-version" = "2023-06-01" } ` -ContentType "application/json" ` -Body '{"model":"claude-haiku-4-5","max_tokens":16,"messages":[{"role":"user","content":"Say hi"}]}' ``` Then start `claude`, send any prompt, and run `/status`. The **Anthropic base URL** line should show `https://api.oneaiapikey.com`, and an **Auth token** line should name `ANTHROPIC_AUTH_TOKEN`. If both are there and the prompt got an answer, you’re done. Your console’s usage log will show the request within a few seconds. ## Troubleshooting **401 Unauthorized.** The key is wrong, revoked, or was sent in a header the gateway didn’t expect. Re-copy the key from the console, check for stray spaces or quotes, and confirm you used `ANTHROPIC_AUTH_TOKEN`. If you set `ANTHROPIC_API_KEY` instead, Claude Code asks you once to approve it; a previously declined key is silently ignored until you enable it again under `/config`. **Claude Code still asks me to log in.** The variables didn’t reach the process. Put them in `~/.claude/settings.json` (the user-level file is read before first-run setup) or export them in the same terminal you launch `claude` from. If you also have a saved claude.ai login, startup may warn about two credential sources: run `/logout` to use the gateway key only. **404 or “not found” on every request.** Almost always a doubled path. `ANTHROPIC_BASE_URL` must be `https://api.oneaiapikey.com` with no trailing `/v1` and no trailing slash; otherwise requests go to `/v1/v1/messages`. **Model not found / no available channel.** The id is misspelled or isn’t served on the Anthropic route. Use the exact ids from the table above, all lowercase. Non-Claude models (GPT, Grok, DeepSeek) are available through our OpenAI-compatible endpoint, not through Claude Code. **Timeouts or a request cut off after about 100 seconds.** Claude Code streams by default, which keeps long generations alive. If you call the API from your own scripts, send `"stream": true` for long outputs; proxies along the way drop idle non-streaming requests at roughly 100 seconds. **Out of balance.** Requests fail once credits hit zero. [Top up](https://oneaiapikey.com/console/billing) from the console; your keys and settings keep working, no restart needed. ## Next steps Want to use the same key elsewhere? The [quickstart](https://oneaiapikey.com/docs/quickstart) covers raw API calls and SDKs, and the other [tool guides](https://oneaiapikey.com/docs/guides) cover OpenAI-format tools like Codex CLI and Cursor. Check current [pricing](https://oneaiapikey.com/pricing) before a long agentic session, and keep an eye on the usage log in the console. --- # Codex CLI setup: add a custom model provider in config.toml > Connect OpenAI Codex CLI to our gateway with a model_providers entry in ~/.codex/config.toml, a key env var and the Responses wire API, plus checks and fixes. Source: https://oneaiapikey.com/docs/guides/codex-cli Base URL: `https://api.oneaiapikey.com/v1` Codex CLI lets you register any service that implements the OpenAI Responses API as a model provider. Our gateway does, so the whole setup is one small block in Codex’s config file plus an environment variable for your key. Every token Codex uses is billed against your prepaid balance at the published rates. ## What you need - Codex CLI installed (for example with `npm install -g @openai/codex`) and starting normally. - A funded account; top-ups start at $3: [create an account](https://oneaiapikey.com/console/sign-up). - An API key from the [API keys page](https://oneaiapikey.com/console/keys) (it starts with `sk-`). ## Step 1: store your key in an environment variable Codex never reads the key from the config file. Instead, the provider entry names an environment variable (`env_key`) and Codex reads the key from there at startup. We’ll call it `RELAYKIT_API_KEY`, but any name works as long as the config matches. macOS / Linux / Git Bash: ```bash # add to ~/.bashrc or ~/.zshrc to keep it export RELAYKIT_API_KEY="sk-your-key-here" ``` Windows PowerShell: ```powershell # current window only $env:RELAYKIT_API_KEY = "sk-your-key-here" # persist for new terminals (then open a new window) setx RELAYKIT_API_KEY "sk-your-key-here" ``` `setx` only affects terminals opened afterwards, so close and reopen your terminal before running Codex. ## Step 2: add the provider to config.toml Codex keeps its settings in `~/.codex/config.toml` (on Windows, `%USERPROFILE%\.codex\config.toml`). Create the file if it doesn’t exist, then add: ~/.codex/config.toml: ```toml # Default model and provider for every Codex session model = "gpt-6.1-sol" model_provider = "relaykit" [model_providers.relaykit] name = "Relaykit" base_url = "https://api.oneaiapikey.com/v1" env_key = "RELAYKIT_API_KEY" wire_api = "responses" # Optional resilience settings stream_idle_timeout_ms = 300000 request_max_retries = 4 ``` What each line does: - `model_provider` tells Codex to send requests to the provider block below instead of OpenAI. - `base_url` **must end in `/v1`**. Codex appends the endpoint path (`/responses`) to it. - `env_key` is the name of the variable holding your key, not the key itself. - `wire_api = "responses"` selects the Responses API, which is what current Codex releases speak. Older guides on the web show `wire_api = "chat"`; that value has been retired in recent versions. - `stream_idle_timeout_ms` gives long reasoning steps more room before Codex treats a quiet stream as dead. If you already have a `config.toml`, keep your other settings and just add these keys. TOML is strict: top-level keys such as `model` must appear before the first `[section]` header. ## Step 3: pick a model Codex is tuned for GPT models and their tool-calling style, so those are what we recommend here. Rates are USD per 1M tokens: | Model id | Good for | Input | Output | | --- | --- | --- | --- | | `gpt-6.1-sol` | Codex CLI, Tool calling, Long context | $2.20 | $11 | | `gpt-5.6-terra` | General coding, Writing, RAG | $2.20 | $13.20 | | `gpt-5.6-sol` | Hard reasoning, Planning, Math | $5.50 | $33 | `gpt-6.1-sol` is a strong default for agentic coding. Switch per session with the `-m` flag or a one-off config override: Per-session model: ```bash codex -m gpt-5.6-terra codex --config model='"gpt-5.6-sol"' ``` You can also change models inside the interactive UI with `/model`. Want Claude in the terminal instead? Use [Claude Code](https://oneaiapikey.com/docs/guides/claude-code), which talks to our Anthropic-compatible endpoint natively. ## Step 4: verify it works Run a one-shot, non-interactive prompt: Smoke test: ```bash codex exec "Reply with the single word: ready" ``` You should see the model’s reply printed in the terminal, and a matching entry in your console’s usage log a few seconds later. In an interactive session, `/status` shows the active model and provider so you can confirm Codex picked up the right block. ## Troubleshooting **“Missing environment variable” or 401 Unauthorized.** Codex couldn’t find a valid key. Check that the variable name in `env_key` matches exactly (it’s case-sensitive on macOS and Linux), that you opened a new terminal after `setx`, and that the key wasn’t revoked in the console. Paste the key without quotes or trailing spaces. **404 Not Found.** The base URL is off by a path segment. It must be `https://api.oneaiapikey.com/v1`: without `/v1` Codex calls a path that doesn’t exist, and with a full endpoint such as `/v1/responses` the path gets doubled. **Model not found / no available channel.** The `model` value doesn’t match a catalogue id. Ids are lowercase and use dots and dashes exactly as shown on the [models page](https://oneaiapikey.com/models). **Stream disconnected or the request times out.** Codex always streams, so long outputs stay alive as long as tokens keep flowing. If a long reasoning pause trips the idle timer, raise `stream_idle_timeout_ms`. In your own scripts, use `"stream": true` for long generations: proxies cut idle non-streaming requests at roughly 100 seconds. **Codex ignores my provider.** Usually a TOML ordering problem (top-level keys placed under a section) or a typo in `model_provider`. The value must equal the name after `model_providers.` exactly. **Balance ran out mid-task.** Requests are refused at zero balance. [Top up](https://oneaiapikey.com/console/billing) in the console and re-run; nothing in your config needs to change. ## Next steps Head to the [quickstart](https://oneaiapikey.com/docs/quickstart) for raw API and SDK examples using the same key, or browse the other [tool guides](https://oneaiapikey.com/docs/guides). Compare per-model costs on the [pricing page](https://oneaiapikey.com/pricing) before kicking off a long autonomous run. ## Other OpenAI format tools - [Cursor IDE](https://oneaiapikey.com/docs/guides/cursor) - [Cline Extension](https://oneaiapikey.com/docs/guides/cline) - [Kilo Code Extension](https://oneaiapikey.com/docs/guides/kilo-code) --- # Cursor setup: use your own key with an OpenAI base URL override > Use Claude, GPT, Grok and DeepSeek in Cursor Chat and Agent via your OpenAI API key and Override OpenAI Base URL, with custom models, checks and known limits. Source: https://oneaiapikey.com/docs/guides/cursor Base URL: `https://api.oneaiapikey.com/v1` Cursor lets you bring your own OpenAI-format key and point it at a different base URL. Because our gateway speaks the OpenAI Chat Completions format for every model in the catalogue, one key and one URL give Cursor access to Claude, GPT, Grok and DeepSeek, all billed from the same prepaid balance. ## What you need - The Cursor desktop app, signed in. - A funded account (top-ups start at $3): [sign up here](https://oneaiapikey.com/console/sign-up). - An API key from the [API keys page](https://oneaiapikey.com/console/keys), starting with `sk-`. ## Before you start: what bring-your-own-key covers Be aware of how Cursor routes requests so nothing surprises you later: - Your key and base URL are used for **local Chat and Agent** requests with the models you enable. - Cursor’s own features such as **Tab** autocomplete, **Auto** model selection, cloud or background agents and the Cursor CLI keep using Cursor’s infrastructure. They don’t go through your key. - **Override OpenAI Base URL is a single global switch.** While it’s on, OpenAI-family requests go to the overridden URL, including some of Cursor’s built-in models. Turn it off when you want to go back to models from your Cursor plan. ## Step 1: open the model settings Open **Cursor Settings** (the gear icon in the top-right corner, or `Ctrl+Shift+J` / `Cmd+Shift+J`). Make sure you’re in Cursor’s own settings, not the VS Code-style editor settings: only Cursor Settings has a **Models** section. Select **Models** and scroll to the **API Keys** area. ## Step 2: add the key and override the base URL In the OpenAI section: 1. Turn on **OpenAI API Key** and paste your `sk-` key. 2. Turn on **Override OpenAI Base URL** and enter the URL below, including `/v1`: Override OpenAI Base URL: ```text https://api.oneaiapikey.com/v1 ``` 3. Click **Verify** (or save). Cursor sends a small test request; a success message means the key and URL were accepted. ## Step 3: add the models you want Back in the model list, click **Add Custom Model** (labelled **+ Add model** in some versions) and type a model id exactly as it appears in our catalogue. Add as many as you like, then make sure each one is toggled on. Good choices for Cursor (USD per 1M tokens): | Model id | Good for | Input | Output | | --- | --- | --- | --- | | `claude-sonnet-5` | Daily coding, Code review, Writing | $2.20 | $11 | | `claude-opus-5-5` | Agentic coding, Large refactors, Complex reasoning | $4.40 | $22 | | `gpt-6.1-sol` | Codex CLI, Tool calling, Long context | $2.20 | $11 | | `grok-4.6` | Fast answers, Research, Chat | $2.20 | $6.60 | | `deepseek-v4-flash` | Budget coding, Sub-agents, Batch jobs | $0.24 | $0.96 | `claude-sonnet-5` is a great everyday Agent model; reach for `claude-opus-5-5` on big refactors and `deepseek-v4-flash` when you want to keep costs low. See the full catalogue on the [models page](https://oneaiapikey.com/models). If Cursor replies that a model **already exists**, the id matches one of Cursor’s built-in entries. Don’t add a duplicate: simply enable the built-in entry. With the override on, Cursor sends that lowercase id to our gateway unchanged. ## Step 4: verify it works Optional but quick: confirm the key outside Cursor by listing the models your key can use. curl: ```bash curl -sS https://api.oneaiapikey.com/v1/models -H "Authorization: Bearer sk-your-key-here" ``` PowerShell: ```powershell Invoke-RestMethod -Uri "https://api.oneaiapikey.com/v1/models" -Headers @{ Authorization = "Bearer sk-your-key-here" } ``` Then open a Chat or Agent panel, choose one of the models you added from the model picker, and send a short prompt. The reply should arrive normally, and the request will appear in the usage log in your console within a few seconds. If you see the request there, routing works. ## Troubleshooting **Verify fails or chat shows 401 / invalid API key.** Re-copy the key from the console and paste it without spaces. Confirm the key isn’t disabled and your balance is above zero. If you recently rotated the key, update it in Cursor too. **404 or a generic network error.** Check the override URL. It must be `https://api.oneaiapikey.com/v1`, with `/v1` and without a trailing slash or an endpoint path such as `/chat/completions`. **“Model not found” or “the requested model is not available”.** The custom model name doesn’t match a catalogue id. Ids are case-sensitive and use the exact dots and dashes shown above. Also check that the model is toggled on. **Cursor’s built-in models stopped working.** That’s the global override at work: OpenAI-family requests are going to our gateway. Turn off **Override OpenAI Base URL** while you use your Cursor plan models, and turn it back on when you switch back. **Tab completions don’t show up in my usage log.** Expected. Tab always runs on Cursor’s own models, whatever key you configure. **Long answers stop partway.** Cursor streams responses, which keeps long generations alive. If you call the API from your own code, set `"stream": true` for long outputs, since proxies cut idle non-streaming requests at about 100 seconds. ## Next steps Using a terminal agent too? See the [Claude Code](https://oneaiapikey.com/docs/guides/claude-code) and [Codex CLI](https://oneaiapikey.com/docs/guides/codex-cli) guides, or browse all [tool guides](https://oneaiapikey.com/docs/guides). Prices for every model are on the [pricing page](https://oneaiapikey.com/pricing). ## Other OpenAI format tools - [Codex CLI CLI](https://oneaiapikey.com/docs/guides/codex-cli) - [Cline Extension](https://oneaiapikey.com/docs/guides/cline) - [Kilo Code Extension](https://oneaiapikey.com/docs/guides/kilo-code) --- # Cline setup: connect the OpenAI Compatible provider > Configure Cline in VS Code with the OpenAI Compatible provider: base URL, API key and model id for Claude, GPT, Grok or DeepSeek, then verify it and fix errors. Source: https://oneaiapikey.com/docs/guides/cline Base URL: `https://api.oneaiapikey.com/v1` Cline is an autonomous coding agent that runs inside VS Code (and compatible editors). It supports any service that implements the OpenAI Chat Completions format through its **OpenAI Compatible** provider. Point that provider at our gateway and Cline can drive Claude, GPT, Grok or DeepSeek with a single key, charged to your prepaid balance at the published per-token rates. ## What you need - VS Code (or a VS Code-based editor) with the Cline extension installed. - A funded account; top-ups start at $3: [create yours](https://oneaiapikey.com/console/sign-up). - An API key from the [API keys page](https://oneaiapikey.com/console/keys). It starts with `sk-`. ## Step 1: open Cline’s provider settings Click the Cline icon in the editor’s activity bar to open the Cline panel, then click the **settings (gear) icon** at the top of the panel. The API configuration section is where you choose a provider. ## Step 2: choose OpenAI Compatible and fill in the fields Set **API Provider** to **OpenAI Compatible**, then enter: Cline provider settings: ```text API Provider: OpenAI Compatible Base URL: https://api.oneaiapikey.com/v1 API Key: sk-your-key-here Model ID: claude-sonnet-5 ``` - **Base URL** includes `/v1`. Cline adds `/chat/completions` on its own. - **API Key** is the `sk-` key from your console. - **Model ID** is any id from the table below, typed exactly. Click **Done** (or close settings) to save. If your version of Cline offers separate models for **Plan** and **Act** modes, you can give each mode its own model id from the same provider: for example a flagship for planning and a cheaper model for edits. ## Step 3: optional model configuration Below the main fields, Cline has a **Model Configuration** section. It controls how Cline itself behaves, not what the gateway does: - **Supports images**: enable it for models that accept image input (the Claude and GPT models do) if you want to paste screenshots. - **Context window size** and **max output tokens**: if you’re unsure, leave the defaults. Setting a context window larger than the model supports causes errors on very long tasks. - **Input / output price**: fill these in from the table below if you want Cline’s running cost estimate to match what we actually charge. Your console’s usage log is always the source of truth. ## Recommended models Cline relies heavily on tool calling and long, multi-step plans, so stronger models pay for themselves in fewer retries. Prices are USD per 1M tokens: | Model id | Good for | Input | Output | | --- | --- | --- | --- | | `claude-sonnet-5` | Daily coding, Code review, Writing | $2.20 | $11 | | `claude-opus-5-5` | Agentic coding, Large refactors, Complex reasoning | $4.40 | $22 | | `gpt-6.1-sol` | Codex CLI, Tool calling, Long context | $2.20 | $11 | | `deepseek-v4-flash` | Budget coding, Sub-agents, Batch jobs | $0.24 | $0.96 | A sensible split: `claude-sonnet-5` for day-to-day Act mode, `claude-opus-5-5` when a task needs deep reasoning, and `deepseek-v4-flash` for cheap bulk edits. All models are listed on the [models page](https://oneaiapikey.com/models). ## Step 4: verify it works Start a new Cline task with something harmless, such as “List the files in this folder and summarise the project in two sentences”. Cline should respond and ask permission for its first tool call. Within a few seconds the request shows up in the usage log in your console. To check the key independently of Cline, list the models available to it: curl: ```bash curl -sS https://api.oneaiapikey.com/v1/models -H "Authorization: Bearer sk-your-key-here" ``` A JSON list of model ids means the key and URL are good, so any remaining problem lies in the Cline settings. ## Troubleshooting **401 Unauthorized / invalid API key.** Re-paste the key from the console without spaces, make sure it hasn’t been disabled, and confirm your balance is above zero. **404 Not Found.** The base URL is missing `/v1`, or it includes a full endpoint path. Use exactly `https://api.oneaiapikey.com/v1`. **“Model not found” or “no available channel”.** The Model ID doesn’t match a catalogue id. Ids are lowercase and use the exact dots and dashes shown above, with no provider prefix. **The task stalls or ends with a timeout on long outputs.** Cline streams responses, which keeps long generations alive through proxies. In your own scripts, send `"stream": true` for long outputs, because proxies cut idle non-streaming requests at roughly 100 seconds. If a very long task keeps failing, start a fresh task so the context is smaller. **Tool calls fail or the model ignores Cline’s instructions.** Smaller models sometimes struggle with Cline’s tool format. Switch to `claude-sonnet-5` or `gpt-6.1-sol` and retry. **Context window exceeded.** Lower the context window in Model Configuration to the model’s real limit, or start a new task. ## Next steps Setting up a teammate on another editor? See the [Kilo Code](https://oneaiapikey.com/docs/guides/kilo-code) and [Zed](https://oneaiapikey.com/docs/guides/zed) guides, or the full list of [tool guides](https://oneaiapikey.com/docs/guides). The [quickstart](https://oneaiapikey.com/docs/quickstart) covers calling the API directly, and current rates are on the [pricing page](https://oneaiapikey.com/pricing). ## Other OpenAI format tools - [Codex CLI CLI](https://oneaiapikey.com/docs/guides/codex-cli) - [Cursor IDE](https://oneaiapikey.com/docs/guides/cursor) - [Kilo Code Extension](https://oneaiapikey.com/docs/guides/kilo-code) --- # Kilo Code setup: add a custom OpenAI-compatible provider > Add our gateway to Kilo Code as a custom OpenAI Compatible provider in VS Code or in kilo.jsonc for the CLI, with model ids, auto-fetched models and fixes. Source: https://oneaiapikey.com/docs/guides/kilo-code Base URL: `https://api.oneaiapikey.com/v1` Kilo Code is an open-source coding agent available as a VS Code extension and a CLI. It has first-class support for custom providers, including any endpoint that speaks the OpenAI Chat Completions format. Add our gateway once and every model in the catalogue becomes available in Kilo, billed from one prepaid balance. ## What you need - Kilo Code installed (the VS Code extension, the CLI, or both). - A funded account; top-ups start at $3: [create an account](https://oneaiapikey.com/console/sign-up). - An API key from the [API keys page](https://oneaiapikey.com/console/keys) (it starts with `sk-`). ## Option A: VS Code extension 1. Open Kilo Code’s **Settings** (gear icon) and go to the **Providers** tab. 2. Scroll to the bottom and click **Custom provider**. 3. Fill in the dialog: Custom provider dialog: ```text Provider ID: relaykit Display name: Relaykit Provider API: OpenAI Compatible Base URL: https://api.oneaiapikey.com/v1 API key: sk-your-key-here Models: pick from the fetched list, or add ids manually ``` 4. Click **Submit** to save. A few details matter here: - **Provider API** must be **OpenAI Compatible**. Kilo also offers “OpenAI Responses” and “Anthropic Messages” options; stick with OpenAI Compatible for this setup, since it works for every model we serve, Claude included. - **Base URL** is the `/v1` root, not a full endpoint such as `/v1/chat/completions`. - Once the URL and key are valid, Kilo **fetches the model list** from our `/v1/models` endpoint automatically. If the list stays empty, fix the key or URL and click **Fetch models** again. ## Option B: CLI and config file The CLI (and any extra model tuning) uses a `kilo.jsonc` config file. Put the provider in your **global** config at `~/.config/kilo/kilo.jsonc`: ~/.config/kilo/kilo.jsonc: ```jsonc { "$schema": "https://app.kilo.ai/config.json", "model": "relaykit/claude-sonnet-5", "provider": { "relaykit": { "npm": "@ai-sdk/openai-compatible", "name": "Relaykit", "options": { "baseURL": "https://api.oneaiapikey.com/v1", "apiKey": "{env:RELAYKIT_API_KEY}" }, "models": { "claude-sonnet-5": { "name": "Claude Sonnet 5", "tool_call": true }, "gpt-6.1-sol": { "name": "GPT-6.1 Sol", "tool_call": true }, "deepseek-v4-flash": { "name": "DeepSeek V4 Flash", "tool_call": true } } } } } ``` Then store the key in the environment variable the config refers to: macOS / Linux / Git Bash: ```bash export RELAYKIT_API_KEY="sk-your-key-here" ``` Windows (open a new terminal afterwards): ```powershell setx RELAYKIT_API_KEY "sk-your-key-here" ``` The `model` key uses the `provider-id/model-id` format. Keep the `{env:...}` reference in the global file: Kilo doesn’t resolve environment references in a project-level `kilo.jsonc`, and you should never commit a raw key to a repository anyway. ## Recommended models Prices are USD per 1M tokens and come straight from our catalogue: | Model id | Good for | Input | Output | | --- | --- | --- | --- | | `claude-sonnet-5` | Daily coding, Code review, Writing | $2.20 | $11 | | `claude-opus-5-5` | Agentic coding, Large refactors, Complex reasoning | $4.40 | $22 | | `gpt-6.1-sol` | Codex CLI, Tool calling, Long context | $2.20 | $11 | | `grok-4.6` | Fast answers, Research, Chat | $2.20 | $6.60 | | `deepseek-v4-flash` | Budget coding, Sub-agents, Batch jobs | $0.24 | $0.96 | Kilo’s agents lean on tool calls, so mark models with `"tool_call": true` in the config file. `claude-sonnet-5` is a dependable default; `deepseek-v4-flash` is the budget pick for sub-tasks. The full list lives on the [models page](https://oneaiapikey.com/models). ## Verify it works In VS Code, select one of the gateway’s models in the Kilo model picker and ask a simple question about your project. On the CLI, start Kilo and send a short prompt. A normal reply, plus a matching entry in your console’s usage log, confirms the setup. To test the key on its own: curl: ```bash curl -sS https://api.oneaiapikey.com/v1/models -H "Authorization: Bearer sk-your-key-here" ``` ## Troubleshooting **401 Unauthorized.** The key is wrong, disabled, or (in the CLI) the environment variable isn’t set in the terminal that launched Kilo. Re-copy the key and open a fresh terminal after `setx`. **No models appear in the picker.** Auto-fetch failed, which nearly always means a bad key or base URL. Fix it and click **Fetch models**, or add the ids manually. **404 Not Found.** Check the base URL is exactly `https://api.oneaiapikey.com/v1`, with no trailing slash. **“Model not found” or “no available channel”.** The model id doesn’t match the catalogue. In the config file the key under `models` must be the exact id, such as `claude-sonnet-5`. **Request timed out.** Kilo streams responses, so long generations normally stay alive. For your own scripts, use `"stream": true` on long outputs, because proxies cut idle non-streaming requests at roughly 100 seconds. **Wrong provider API selected.** If you picked “OpenAI Responses” or “Anthropic Messages” by mistake, requests go to the wrong path. Edit the provider and switch it to OpenAI Compatible. ## Next steps See the [Cline](https://oneaiapikey.com/docs/guides/cline) and [OpenCode](https://oneaiapikey.com/docs/guides/opencode) guides for similar tools, or browse every [tool guide](https://oneaiapikey.com/docs/guides). Current per-model rates are on the [pricing page](https://oneaiapikey.com/pricing). ## Other OpenAI format tools - [Codex CLI CLI](https://oneaiapikey.com/docs/guides/codex-cli) - [Cursor IDE](https://oneaiapikey.com/docs/guides/cursor) - [Cline Extension](https://oneaiapikey.com/docs/guides/cline) --- # OpenCode setup: custom provider with @ai-sdk/openai-compatible > Add our gateway to OpenCode as a custom provider in opencode.json with @ai-sdk/openai-compatible, store the key via /connect, pick models and fix errors. Source: https://oneaiapikey.com/docs/guides/opencode Base URL: `https://api.oneaiapikey.com/v1` OpenCode is an open-source coding agent for the terminal. It builds on the AI SDK, which means any OpenAI-compatible service can be plugged in as a custom provider through the `@ai-sdk/openai-compatible` package. Our gateway is one of those, so a short JSON block gives OpenCode access to every model in our catalogue on one prepaid balance. ## What you need - OpenCode installed and starting normally in your terminal. - A funded account (top-ups start at $3): [sign up](https://oneaiapikey.com/console/sign-up). - An API key from the [API keys page](https://oneaiapikey.com/console/keys), starting with `sk-`. ## Step 1: save your key with /connect Start `opencode`, run the `/connect` command, scroll to **Other**, and enter a provider ID. Use `relaykit`, because the config in the next step refers to the same ID. Paste your `sk-` key when asked. OpenCode stores the credential in its local auth file (`~/.local/share/opencode/auth.json`), outside your project, so it never ends up in git. Prefer environment variables? Skip `/connect` and reference a variable from the config instead (shown in step 2). ## Step 2: describe the provider in opencode.json OpenCode reads a global config at `~/.config/opencode/opencode.json` and an optional `opencode.json` in your project root. The global file is the right place for a provider you’ll use everywhere: ~/.config/opencode/opencode.json: ```json { "$schema": "https://opencode.ai/config.json", "model": "relaykit/claude-sonnet-5", "provider": { "relaykit": { "npm": "@ai-sdk/openai-compatible", "name": "Relaykit", "options": { "baseURL": "https://api.oneaiapikey.com/v1" }, "models": { "claude-sonnet-5": { "name": "Claude Sonnet 5" }, "claude-opus-5-5": { "name": "Claude Opus 5.5" }, "gpt-6.1-sol": { "name": "GPT-6.1 Sol" }, "deepseek-v4-flash": { "name": "DeepSeek V4 Flash" } } } } } ``` How the pieces fit: - The key under `provider` must match the ID you used with `/connect`. - `npm: "@ai-sdk/openai-compatible"` tells OpenCode to talk to the Chat Completions endpoint. - `options.baseURL` **includes `/v1`**; the SDK adds `/chat/completions` itself. - Each key under `models` is an exact model id from our catalogue. The `name` is only a display label. - The top-level `model` uses the `provider/model` format and becomes your default. If you went the environment variable route, add an `apiKey` reference to `options` and set the variable in your shell: options block with an env reference: ```json "options": { "baseURL": "https://api.oneaiapikey.com/v1", "apiKey": "{env:RELAYKIT_API_KEY}" } ``` Optionally, give each model a `limit` object with `context` and `output` token counts so OpenCode can manage long sessions accurately. ## Recommended models Rates are USD per 1M tokens, pulled from our catalogue: | Model id | Good for | Input | Output | | --- | --- | --- | --- | | `claude-sonnet-5` | Daily coding, Code review, Writing | $2.20 | $11 | | `claude-opus-5-5` | Agentic coding, Large refactors, Complex reasoning | $4.40 | $22 | | `gpt-6.1-sol` | Codex CLI, Tool calling, Long context | $2.20 | $11 | | `deepseek-v4-flash` | Budget coding, Sub-agents, Batch jobs | $0.24 | $0.96 | `claude-sonnet-5` is a solid default for agentic work. Add `deepseek-v4-flash` for cheap, quick tasks and `claude-opus-5-5` for the hard ones. Switch between them any time with the `/models` command. Browse the rest on the [models page](https://oneaiapikey.com/models). ## Step 3: verify it works Run a one-shot prompt from your shell: Smoke test: ```bash opencode run "Reply with the single word: ready" ``` If the reply comes back, the provider works. Inside the interactive UI, open `/models` and confirm the gateway’s models are listed under the provider name. Each request also appears in your console’s usage log within a few seconds. ## Troubleshooting **401 Unauthorized.** OpenCode didn’t send a valid key. Re-run `/connect` with the same provider ID, or check that the environment variable named in `{env:...}` is set in the shell that launched OpenCode. **Provider or models missing from /models.** Usually a JSON syntax error or a mismatched provider ID. Validate the file (the `$schema` line enables editor validation) and make sure the ID matches the one in `/connect`. **404 Not Found.** The `baseURL` is wrong. Use `https://api.oneaiapikey.com/v1`, no trailing slash, no endpoint path. **“Model not found” or “no available channel”.** A key under `models` doesn’t match a catalogue id. Ids are lowercase and use the exact punctuation shown above. **Long responses time out.** OpenCode streams by default, which keeps long generations alive. In your own scripts, send `"stream": true` for long outputs, since proxies cut idle non-streaming requests at about 100 seconds. **Balance exhausted.** Requests fail at zero credit. [Top up](https://oneaiapikey.com/console/billing) in the console; no config changes needed. ## Next steps Also using an editor? See the [Kilo Code](https://oneaiapikey.com/docs/guides/kilo-code) and [Zed](https://oneaiapikey.com/docs/guides/zed) guides, or the full list of [tool guides](https://oneaiapikey.com/docs/guides). The [quickstart](https://oneaiapikey.com/docs/quickstart) covers direct API calls, and the [pricing page](https://oneaiapikey.com/pricing) lists every rate. ## Other OpenAI format tools - [Codex CLI CLI](https://oneaiapikey.com/docs/guides/codex-cli) - [Cursor IDE](https://oneaiapikey.com/docs/guides/cursor) - [Cline Extension](https://oneaiapikey.com/docs/guides/cline) --- # Aider setup: OPENAI_API_BASE and the openai/ model prefix > Run Aider through our OpenAI-compatible endpoint: set OPENAI_API_BASE and OPENAI_API_KEY, launch with openai/, save it in .aider.conf.yml and fix errors. Source: https://oneaiapikey.com/docs/guides/aider Base URL: `https://api.oneaiapikey.com/v1` Aider is a terminal pair-programmer that edits files in your git repo and commits as it goes. It reaches models through LiteLLM, which supports any OpenAI-compatible endpoint. Two environment variables and a model prefix are all it takes to run Aider on our gateway, with every token billed from your prepaid balance. ## What you need - Aider installed (for example with `python -m pip install aider-install` followed by `aider-install`, or your preferred method) and a git repository to work in. - A funded account; top-ups start at $3: [create one](https://oneaiapikey.com/console/sign-up). - An API key from the [API keys page](https://oneaiapikey.com/console/keys) (it begins with `sk-`). ## Step 1: point Aider at the gateway Aider reads the standard OpenAI variables. Set the base URL **with** `/v1`: macOS / Linux / Git Bash: ```bash export OPENAI_API_BASE=https://api.oneaiapikey.com/v1 export OPENAI_API_KEY=sk-your-key-here ``` Windows: ```powershell setx OPENAI_API_BASE "https://api.oneaiapikey.com/v1" setx OPENAI_API_KEY "sk-your-key-here" # then close this window and open a new terminal ``` `setx` writes the variables permanently but only for terminals opened afterwards, so restart your terminal before the next step. To keep the variables only for the current PowerShell window, use `$env:OPENAI_API_BASE = "..."` instead. ## Step 2: launch with the openai/ prefix The `openai/` prefix is important. It tells Aider (via LiteLLM) to send the request in OpenAI format to whatever `OPENAI_API_BASE` says, instead of trying to reach a vendor’s own API. Everything after the prefix is the exact model id from our catalogue: Start Aider: ```bash aider --model openai/claude-sonnet-5 ``` Aider uses a second, cheaper “weak” model for commit messages and chat summaries. Point it at a fast model so those calls don’t run on your flagship. You can also pass the base URL as a flag rather than an environment variable: Main and weak model: ```bash aider --model openai/claude-sonnet-5 \ --weak-model openai/claude-haiku-4-5 \ --openai-api-base https://api.oneaiapikey.com/v1 ``` ## Step 3: save it in .aider.conf.yml Typing flags every time gets old. Aider reads `.aider.conf.yml` from your home directory and from the root of the current repo, so put your defaults there: .aider.conf.yml: ```yaml # ~/.aider.conf.yml (or .aider.conf.yml in your repo root) openai-api-base: https://api.oneaiapikey.com/v1 model: openai/claude-sonnet-5 weak-model: openai/claude-haiku-4-5 # keep the key in OPENAI_API_KEY rather than in this file ``` Keep `OPENAI_API_KEY` in your environment (or a `.env` file that is gitignored). Don’t commit keys to a repository config. ## Recommended models Aider’s edit formats reward strong instruction following. Rates are USD per 1M tokens: | Aider model | Good for | Input | Output | | --- | --- | --- | --- | | `openai/claude-sonnet-5` | Daily coding, Code review, Writing | $2.20 | $11 | | `openai/claude-opus-5-5` | Agentic coding, Large refactors, Complex reasoning | $4.40 | $22 | | `openai/gpt-6.1-sol` | Codex CLI, Tool calling, Long context | $2.20 | $11 | | `openai/claude-haiku-4-5` | Sub-agents, Extraction, Chat | $1.10 | $5.50 | | `openai/deepseek-v4-flash` | Budget coding, Sub-agents, Batch jobs | $0.24 | $0.96 | Use `/model openai/` inside a session to switch on the fly. The full catalogue is on the [models page](https://oneaiapikey.com/models). ## Step 4: verify it works Launch Aider in a repo and ask something read-only, such as “What does this project do?”. A normal answer means the connection works, and the request appears in your console’s usage log. Aider’s own startup banner shows the main and weak model names, which is a quick way to confirm the prefix was applied. ## About the “unknown model” warning Because our model ids aren’t in LiteLLM’s built-in database, Aider may print a warning that it doesn’t know the model’s context window or cost. The model still works. You can silence the warning with `--no-show-model-warnings`, or register the model’s limits and prices in a `.aider.model.metadata.json` file as described in Aider’s documentation on model warnings. Remember that the cost Aider prints is only an estimate: your console’s usage log shows what was actually charged. ## Troubleshooting **401 / AuthenticationError.** `OPENAI_API_KEY` is missing or wrong in the shell that started Aider. Run `echo $OPENAI_API_KEY` (or `$env:OPENAI_API_KEY` in PowerShell) to check, re-copy the key from the console, and open a new terminal after `setx`. **Requests go to OpenAI instead of the gateway.** `OPENAI_API_BASE` isn’t set in this shell, or a config file overrides it. Pass `--openai-api-base` explicitly to rule that out. **404 Not Found.** The base URL lacks `/v1` or includes an endpoint path. Use exactly `https://api.oneaiapikey.com/v1`. **“LLM Provider NOT provided” or model not found.** You left off the `openai/` prefix, or the id after it doesn’t match the catalogue. It must look like `openai/claude-sonnet-5`. **Timeouts on large edits.** Aider streams by default; if you disabled it with `--no-stream`, re-enable streaming. Proxies cut idle non-streaming requests at roughly 100 seconds, so streaming matters for long outputs. ## Next steps Want a more autonomous agent? Try [Claude Code](https://oneaiapikey.com/docs/guides/claude-code) or [OpenCode](https://oneaiapikey.com/docs/guides/opencode), or see all [tool guides](https://oneaiapikey.com/docs/guides). Per-model rates are on the [pricing page](https://oneaiapikey.com/pricing). ## Other OpenAI format tools - [Codex CLI CLI](https://oneaiapikey.com/docs/guides/codex-cli) - [Cursor IDE](https://oneaiapikey.com/docs/guides/cursor) - [Cline Extension](https://oneaiapikey.com/docs/guides/cline) --- # Zed setup: add an OpenAI-compatible provider for the Agent Panel > Add our gateway to Zed via language_models.openai_compatible in settings.json, enter your key, pick Claude, GPT, Grok or DeepSeek in the Agent Panel and fix errors. Source: https://oneaiapikey.com/docs/guides/zed Base URL: `https://api.oneaiapikey.com/v1` Zed is a fast, native code editor with a built-in Agent Panel. Besides its own hosted models, Zed lets you register any OpenAI-compatible service as a language model provider. Add our gateway once and you can drive Zed’s agent with Claude, GPT, Grok or DeepSeek using one key and one prepaid balance. ## What you need - A recent version of Zed (OpenAI-compatible providers are configured through `language_models.openai_compatible`). - A funded account; top-ups start at $3: [create an account](https://oneaiapikey.com/console/sign-up). - An API key from the [API keys page](https://oneaiapikey.com/console/keys) (it begins with `sk-`). ## Step 1: open settings.json Open the command palette and run **zed: open settings file**. (Plain **zed: open settings** opens the graphical Settings Editor instead, which is handy for most options but not for this block.) The file lives at `~/.config/zed/settings.json` on macOS and Linux and at `%APPDATA%\Zed\settings.json` on Windows. ## Step 2: add the provider Merge the `language_models` block below into your settings. If you already have a `language_models` key, add the `openai_compatible` entry inside it rather than creating a second one. settings.json: ```jsonc { "language_models": { "openai_compatible": { "Relaykit": { "api_url": "https://api.oneaiapikey.com/v1", "available_models": [ { "name": "claude-sonnet-5", "display_name": "Claude Sonnet 5", // conservative context budget; raise it if you know the model's limit "max_tokens": 200000, "max_output_tokens": 32000, "capabilities": { "tools": true, "images": true, "parallel_tool_calls": false, "prompt_cache_key": false, "chat_completions": true } }, { "name": "deepseek-v4-flash", "display_name": "DeepSeek V4 Flash", "max_tokens": 128000, "max_output_tokens": 16000, "capabilities": { "tools": true, "images": false, "parallel_tool_calls": false, "prompt_cache_key": false, "chat_completions": true } } ] } } } } ``` What the fields mean: - The key inside `openai_compatible` (here `Relaykit`) is the provider name shown in Zed. - `api_url` **includes `/v1`**. - Each entry in `available_models` needs a `name` that matches a catalogue id exactly. `display_name` is just the label in the picker. - `max_tokens` is the context budget Zed plans around and `max_output_tokens` caps a single reply. The numbers above are conservative placeholders, so adjust them if you know a model’s real limits. - `capabilities.tools` must be `true` for agentic use, because the Agent Panel works through tool calls. Set `images` to `true` only for models that accept images. - `chat_completions: true` keeps Zed on the widely supported Chat Completions endpoint. Settings files in Zed accept comments, so the `//` line in the example is fine. ## Step 3: enter your API key Open the Agent Panel, go to its settings view, find your provider under the LLM providers list, and paste your `sk-` key. Zed stores it in your system keychain rather than in `settings.json`. Alternatively, set an environment variable before launching Zed. Zed derives the variable name from the provider name in uppercase, which for this config is `RELAYKIT_API_KEY`. The provider’s settings view shows the exact name it expects. macOS / Linux: ```bash export RELAYKIT_API_KEY="sk-your-key-here" ``` ## Recommended models Add any of these to `available_models`. Rates are USD per 1M tokens: | Model id | Good for | Input | Output | | --- | --- | --- | --- | | `claude-sonnet-5` | Daily coding, Code review, Writing | $2.20 | $11 | | `claude-opus-5-5` | Agentic coding, Large refactors, Complex reasoning | $4.40 | $22 | | `gpt-6.1-sol` | Codex CLI, Tool calling, Long context | $2.20 | $11 | | `grok-4.6` | Fast answers, Research, Chat | $2.20 | $6.60 | | `deepseek-v4-flash` | Budget coding, Sub-agents, Batch jobs | $0.24 | $0.96 | `claude-sonnet-5` is a strong default for the agent; `deepseek-v4-flash` is a low-cost option for quick questions and inline assists. See every model on the [models page](https://oneaiapikey.com/models). ## Step 4: verify it works Open the Agent Panel, click the model selector, and choose one of the models listed under your provider name. Ask a simple question about the open project. A normal reply means the provider works, and you’ll see the request in your console’s usage log a few seconds later. To check the key independently of Zed: curl: ```bash curl -sS https://api.oneaiapikey.com/v1/models -H "Authorization: Bearer sk-your-key-here" ``` ## Troubleshooting **The provider or models don’t show up.** Zed couldn’t parse your settings, or the block is nested in the wrong place. Zed highlights JSON errors in the settings file; fix them and the provider appears without a restart. **401 Unauthorized.** No key, or a wrong one, is stored for this provider. Re-enter it in the Agent Panel settings. If you rely on the environment variable, make sure Zed was launched from a shell where it’s set. **404 Not Found.** `api_url` must be exactly `https://api.oneaiapikey.com/v1`: no trailing slash, no endpoint path. **“Model not found” or “no available channel”.** A `name` in `available_models` doesn’t match a catalogue id. Ids are lowercase with exact dots and dashes. **The agent answers but never edits files.** Tool calling is off for that model. Set `capabilities.tools` to `true`. **Long replies stop or time out.** Zed streams responses, which keeps long generations alive. If you call the API from your own code, use `"stream": true` for long outputs: proxies cut idle non-streaming requests at about 100 seconds. ## Next steps Pair Zed with a terminal agent like [Claude Code](https://oneaiapikey.com/docs/guides/claude-code) or [OpenCode](https://oneaiapikey.com/docs/guides/opencode), or browse every [tool guide](https://oneaiapikey.com/docs/guides). Current rates for all models are on the [pricing page](https://oneaiapikey.com/pricing). ## Other OpenAI format tools - [Codex CLI CLI](https://oneaiapikey.com/docs/guides/codex-cli) - [Cursor IDE](https://oneaiapikey.com/docs/guides/cursor) - [Cline Extension](https://oneaiapikey.com/docs/guides/cline) --- # SDKs and frameworks > Use the official OpenAI and Anthropic SDKs, LangChain, the Vercel AI SDK and LiteLLM with the gateway. The base URL and key settings for each, with working code. Source: https://oneaiapikey.com/docs/sdks Any library that lets you set a base URL for the OpenAI or Anthropic API works with the gateway. You change two settings: the **base URL** and the **API key**. Everything else (request shapes, streaming, tools, errors) stays as the library documents it. OpenAI SDKs `https://api.oneaiapikey.com/v1` Anthropic SDKs `https://api.oneaiapikey.com` > **The one difference to remember:** OpenAI-format clients take the base URL **with** `/v1`. Anthropic SDKs take it **without** `/v1`, because they add `/v1/messages` themselves. The Vercel AI SDK’s Anthropic provider is the exception: it expects `/v1`. ## OpenAI SDK Works with every model, in the [chat completions](https://oneaiapikey.com/docs/chat-completions) format. The SDK also reads `OPENAI_BASE_URL` and `OPENAI_API_KEY` from the environment if you prefer not to pass them in code. Python: ```python # pip install openai import os from openai import OpenAI client = OpenAI(base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"]) reply = client.chat.completions.create( model="claude-haiku-4-5", messages=[{"role": "user", "content": "Say hello in five words."}], ) print(reply.choices[0].message.content) ``` Node: ```js // npm install openai import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY }); const reply = await client.chat.completions.create({ model: "claude-haiku-4-5", messages: [{ role: "user", content: "Say hello in five words." }], }); console.log(reply.choices[0].message.content); ``` ## Anthropic SDK For Claude models in Anthropic’s own format, with [prompt caching](https://oneaiapikey.com/docs/prompt-caching), extended thinking and every Messages feature. It also reads `ANTHROPIC_BASE_URL` and `ANTHROPIC_API_KEY`. Python: ```python # pip install anthropic import os import anthropic client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) message = client.messages.create( model="claude-sonnet-5-5", max_tokens=512, messages=[{"role": "user", "content": "Say hello in five words."}], ) print(message.content[0].text) ``` Node: ```js // npm install @anthropic-ai/sdk import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://api.oneaiapikey.com", apiKey: process.env.RELAYKIT_API_KEY }); const message = await client.messages.create({ model: "claude-sonnet-5-5", max_tokens: 512, messages: [{ role: "user", content: "Say hello in five words." }], }); console.log(message.content[0].text); ``` ## LangChain `ChatOpenAI` works with every model; `ChatAnthropic` with Claude models. Leave `use_responses_api` off on `ChatOpenAI` unless the model is a GPT model, because the [Responses API](https://oneaiapikey.com/docs/responses) is fully supported only there. Python: ```python # pip install langchain-openai langchain-anthropic import os from langchain_openai import ChatOpenAI from langchain_anthropic import ChatAnthropic llm = ChatOpenAI( model="gpt-5.6-terra", base_url="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"], ) print(llm.invoke("Say hello in five words.").content) claude = ChatAnthropic( model="claude-sonnet-5-5", base_url="https://api.oneaiapikey.com", # no /v1 api_key=os.environ["RELAYKIT_API_KEY"], ) ``` JS: ```js // npm install @langchain/openai import { ChatOpenAI } from "@langchain/openai"; const llm = new ChatOpenAI({ model: "gpt-5.6-terra", apiKey: process.env.RELAYKIT_API_KEY, configuration: { baseURL: "https://api.oneaiapikey.com/v1" }, }); const reply = await llm.invoke("Say hello in five words."); console.log(reply.content); ``` ## Vercel AI SDK Use the OpenAI-compatible provider for any model. The plain `@ai-sdk/openai` provider also works, but `openai("id")` calls the Responses API; use `openai.chat("id")` to send chat completions instead. AI SDK: ```ts // npm install ai @ai-sdk/openai-compatible @ai-sdk/anthropic import { generateText, streamText } from "ai"; import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; import { createAnthropic } from "@ai-sdk/anthropic"; // Any model, OpenAI format (chat completions) const gateway = createOpenAICompatible({ name: "gateway", baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY, includeUsage: true, // token counts on streamed responses }); const { text } = await generateText({ model: gateway("claude-haiku-4-5"), prompt: "Say hello in five words.", }); // Claude models, Anthropic format. This provider expects /v1 in the base URL. const anthropic = createAnthropic({ baseURL: "https://api.oneaiapikey.com/v1", apiKey: process.env.RELAYKIT_API_KEY, }); const result = streamText({ model: anthropic("claude-sonnet-5-5"), prompt: "Write a haiku about latency." }); for await (const chunk of result.textStream) process.stdout.write(chunk); ``` ## LiteLLM Prefix the model id with `openai/` and set `api_base` to the OpenAI base URL. LiteLLM SDK: ```python # pip install litellm import os from litellm import completion reply = completion( model="openai/claude-haiku-4-5", # "openai/" tells LiteLLM to use the OpenAI format api_base="https://api.oneaiapikey.com/v1", api_key=os.environ["RELAYKIT_API_KEY"], messages=[{"role": "user", "content": "Say hello in five words."}], ) print(reply.choices[0].message.content) ``` The same settings work in the LiteLLM proxy: LiteLLM proxy: ```yaml # config.yaml for the LiteLLM proxy model_list: - model_name: claude-haiku-4-5 litellm_params: model: openai/claude-haiku-4-5 api_base: https://api.oneaiapikey.com/v1 api_key: os.environ/RELAYKIT_API_KEY ``` ## Other languages Libraries for Go, Java, .NET, Ruby, Rust and PHP that target the OpenAI API usually have a base URL or endpoint option. Set it to the OpenAI base URL above and pass your key as the API key. With plain HTTP, send `Authorization: Bearer ` to `POST https://api.oneaiapikey.com/v1/chat/completions`; the [API reference](https://oneaiapikey.com/docs/api-reference) lists every endpoint. ## Chat apps and coding tools - Chat apps: [Open WebUI](https://oneaiapikey.com/docs/apps/open-webui), [LibreChat](https://oneaiapikey.com/docs/apps/librechat), [LobeChat](https://oneaiapikey.com/docs/apps/lobechat), [SillyTavern](https://oneaiapikey.com/docs/apps/sillytavern), [Chatbox](https://oneaiapikey.com/docs/apps/chatbox). - Coding agents and editors: see the [coding tools overview](https://oneaiapikey.com/docs/guides). --- # Open WebUI > Connect a self-hosted Open WebUI to the gateway as an OpenAI-compatible connection, so every model your key can use appears in its model picker. Source: https://oneaiapikey.com/docs/apps/open-webui [Open WebUI](https://openwebui.com) is a self-hosted chat interface. It talks to the gateway as an OpenAI-compatible connection and reads the model list from `GET /v1/models`, so you do not have to type model ids. Checked against Open WebUI v0.11.4. You need an API key from [API keys](https://oneaiapikey.com/console/keys) and this base URL: Base URL `https://api.oneaiapikey.com/v1` ## Add the connection 1. Sign in to Open WebUI as an admin and open **Admin Panel → Settings → Connections**. 2. Next to **OpenAI API**, click **+** (Add Connection). 3. Set **URL** to the base URL above, including `/v1`, and paste your key as the API key. 4. Save. Open WebUI fetches the model list; the models your key can use appear in the model picker of a new chat. 5. Start a chat, pick a model such as `claude-haiku-4-5`, and send a message. The request shows up on your [usage page](https://oneaiapikey.com/console/usage). ## Or set it at first start With Docker, you can pass the connection as environment variables. Open WebUI reads them only the first time it starts; after that, the settings saved in the admin panel take over. First start with Docker: ```bash docker run -d -p 3000:8080 \ -e OPENAI_API_BASE_URL=https://api.oneaiapikey.com/v1 \ -e OPENAI_API_KEY=$RELAYKIT_API_KEY \ -v open-webui:/app/backend/data \ --name open-webui ghcr.io/open-webui/open-webui:main ``` ## Keep costs down - Open WebUI makes extra background requests to title chats and suggest tags. Under **Admin Panel → Settings → Interface**, set the task model to a cheap, fast model so those calls do not run on an expensive one. - To show only some models, create a key whose model list contains just those models (see [per-key settings](https://oneaiapikey.com/docs/authentication#per-key-settings)). The picker then lists only them. - Give Open WebUI its own key with a spend limit, separate from your other apps. ## Troubleshooting - **No models in the picker**: the URL must end in `/v1`, and the key must be valid. Check the key with the request on [Authentication](https://oneaiapikey.com/docs/authentication#check-that-a-key-works). - **A model fails with 403**: the key’s model list does not include it, or your balance is empty. See [Errors](https://oneaiapikey.com/docs/errors#access-denied). > **Source:** Steps follow Open WebUI’s guide for [OpenAI-compatible providers](https://docs.openwebui.com/getting-started/quick-start/connect-a-provider/starting-with-openai-compatible/). Menu names can change between releases; check there if yours differ. --- # LibreChat > Add the gateway to LibreChat as a custom endpoint in librechat.yaml, with the key in your .env file and the model list fetched from the API. Source: https://oneaiapikey.com/docs/apps/librechat [LibreChat](https://www.librechat.ai) is a self-hosted chat interface configured with a `librechat.yaml` file. The gateway goes in as a custom endpoint. Checked against LibreChat v0.8.8. You need an API key from [API keys](https://oneaiapikey.com/console/keys) and this base URL: Base URL `https://api.oneaiapikey.com/v1` ## Add the endpoint 1. Put the key in LibreChat’s `.env` file: .env: ```bash RELAYKIT_API_KEY= ``` 2. Add the endpoint under `endpoints.custom` in `librechat.yaml`. Keep the rest of your file as it is. librechat.yaml: ```yaml endpoints: custom: - name: "Relaykit" apiKey: "${RELAYKIT_API_KEY}" baseURL: "https://api.oneaiapikey.com/v1" models: default: ["claude-haiku-4-5", "claude-sonnet-5-5"] fetch: true titleConvo: true titleModel: "claude-haiku-4-5" modelDisplayLabel: "Relaykit" ``` 3. Restart LibreChat. With Docker, make sure `librechat.yaml` is mounted into the container (LibreChat’s docs use a `docker-compose.override.yml` for this). 4. Choose **Relaykit** in the endpoint menu, pick a model and send a message. The request shows up on your [usage page](https://oneaiapikey.com/console/usage). ## What the settings do - `apiKey: “${RELAYKIT_API_KEY}”` reads the key from `.env`, so it never sits in the YAML file. - `baseURL` must end in `/v1`. - `fetch: true` loads the model list from the gateway; `default` is the list shown if fetching fails. - `titleModel` is the model that names each conversation. A fast, cheap model is enough, and saves money on every new chat. ## Troubleshooting - **The endpoint does not appear**: LibreChat did not load the file. Check its startup log for YAML errors and that the file is mounted. - **401 errors**: the variable name in `apiKey` does not match the one in `.env`, or the key was revoked. - **A model fails with 403**: the key’s model list does not include it, or your balance is empty. See [Errors](https://oneaiapikey.com/docs/errors#access-denied). > **Source:** Settings follow LibreChat’s reference for [custom endpoints](https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/custom_endpoint). Check there for every other option. --- # LobeChat > Point LobeChat (LobeHub) at the gateway through its OpenAI provider settings, or with environment variables when you host it yourself. Source: https://oneaiapikey.com/docs/apps/lobechat [LobeChat](https://lobehub.com), now part of LobeHub, is a chat interface you can use hosted or run yourself. It reaches the gateway through its OpenAI provider with a custom API address. Checked against LobeHub v2.2.19. You need an API key from [API keys](https://oneaiapikey.com/console/keys) and this base URL: Base URL `https://api.oneaiapikey.com/v1` ## In the app 1. Open **Settings → AI Providers → OpenAI**. 2. Paste your key as the API key, and set the API proxy address to the base URL above, including `/v1`. 3. Click **Check** to test the connection. 4. Fetch or add the models you want, then pick one in a chat and send a message. The request shows up on your [usage page](https://oneaiapikey.com/console/usage). ## When you host it yourself Set the provider with environment variables instead, so every user of your instance gets it: Environment: ```bash OPENAI_API_KEY= OPENAI_PROXY_URL=https://api.oneaiapikey.com/v1 # Optional: show only these models OPENAI_MODEL_LIST=-all,+claude-haiku-4-5,+claude-sonnet-5-5 ``` `OPENAI_MODEL_LIST` uses LobeHub’s syntax: `-all` hides the default OpenAI models and each `+id` adds one of ours. ## Troubleshooting - **Check fails**: the address must end in `/v1`, not in `/chat/completions`. - **A model is listed but fails**: the id must match the [models table](https://oneaiapikey.com/docs/models#current-models) exactly, and the key’s model list must include it. > **Source:** Variable names follow LobeHub’s [model provider environment variables](https://github.com/lobehub/lobehub/blob/main/docs/self-hosting/environment-variables/model-provider.mdx). Menu names can change between releases. --- # SillyTavern > Connect SillyTavern to the gateway with a Custom (OpenAI-compatible) chat completion source, and keep long role-play contexts from running up costs. Source: https://oneaiapikey.com/docs/apps/sillytavern [SillyTavern](https://sillytavern.app) is a locally run interface for character chat and role-play. It connects to the gateway as a custom OpenAI-compatible chat completion source. Checked against SillyTavern 1.19.0. You need an API key from [API keys](https://oneaiapikey.com/console/keys) and this base URL: Base URL `https://api.oneaiapikey.com/v1` ## Connect 1. Open **API Connections** (the plug icon in the top bar). 2. Set **API** to **Chat Completion**, and **Chat Completion Source** to **Custom (OpenAI-compatible)**. 3. Set **Custom Endpoint (Base URL)** to the base URL above, including `/v1`. Do not add `/chat/completions`. 4. Paste your key into **Custom API Key**. 5. Click **Connect**, then pick a model from the list or type its id, for example `claude-haiku-4-5`. 6. Send a message. The request shows up on your [usage page](https://oneaiapikey.com/console/usage). ## Keep costs down Every message re-sends the character card, the world info and the chat history, so long chats get more expensive with each reply. - Set the **context size** to what the story actually needs rather than the model’s maximum. - Set **max response length** to a sensible number of tokens. - Use a fast, low-cost model for everyday chat and switch to a larger one for important scenes. - Give SillyTavern its own key with a spend limit (see [Cost control](https://oneaiapikey.com/docs/cost-control#limit-what-a-key-can-spend)). ## Troubleshooting - **Connect fails or returns 404**: the URL must end in `/v1` with nothing after it. - **401**: the key was pasted with extra spaces, or it was revoked. - **The reply stops mid-sentence**: the max response length was reached. Raise it, or ask for shorter replies. > **Source:** Steps follow SillyTavern’s guide to [OpenAI-compatible API connections](https://docs.sillytavern.app/usage/api-connections/openai/). Labels can change between releases. --- # Chatbox > Add the gateway to the Chatbox desktop and mobile app as an OpenAI API compatible model provider, then add the model ids you want to use. Source: https://oneaiapikey.com/docs/apps/chatbox [Chatbox](https://chatboxai.app) is a chat app for desktop and mobile. It connects to the gateway as an OpenAI-compatible model provider. Checked against Chatbox v1.23.5. Chatbox splits the address into a host and a path, so you enter the host **without** `/v1`: | API Host | https://api.oneaiapikey.com | | --- | --- | | API Path | /v1/chat/completions | ## Add the provider 1. Create an API key under [API keys](https://oneaiapikey.com/console/keys). 2. In Chatbox, open **Settings → Model Provider** and add a provider. 3. Give it a name (for example “Relaykit”) and choose **OpenAI API Compatible** as the API mode. 4. Enter the API host and API path from the table above, and paste your key. 5. Add the model ids you want, for example `claude-haiku-4-5` and `claude-sonnet-5-5`. Copy them from the [models table](https://oneaiapikey.com/docs/models#current-models). 6. Click **Check**, then start a chat with one of the models. The request shows up on your [usage page](https://oneaiapikey.com/console/usage). ## Troubleshooting - **404 on check**: the host already ends in `/v1` and the path adds it again. Remove `/v1` from the host. - **Model not found**: the id must match the models table exactly, including dots and dashes. - **401**: the key was pasted with extra spaces, or it was revoked. > **Source:** Steps follow Chatbox’s [model provider guide](https://docs.chatboxai.app/en/guides/providers). Labels can differ slightly between the desktop and mobile apps. --- # API reference > Reference for OpenAI- and Anthropic-compatible endpoints: base URLs, auth headers, chat, responses, messages, models, streaming, tool calls and usage. Source: https://oneaiapikey.com/docs/api-reference The gateway implements the public OpenAI and Anthropic HTTP APIs, so official SDKs and most third-party clients work unchanged. This page lists what is supported, the fields that matter, and where behaviour differs from calling a provider directly. ## Base URLs | Format | Base URL | Used by | | --- | --- | --- | | OpenAI | `https://api.oneaiapikey.com/v1` | OpenAI SDKs, Codex CLI, Cursor, Cline, most editors | | Anthropic | `https://api.oneaiapikey.com` | Anthropic SDKs, Claude Code | All endpoints use HTTPS, accept and return JSON (UTF-8), and stream with server-sent events when `stream` is true. ## Authentication Create keys under [API keys](https://oneaiapikey.com/console/keys) in the console. A key works for both formats; send it in whichever header your client uses: | Header | Format | Notes | | --- | --- | --- | | `Authorization: Bearer sk-…` | Both | Standard for OpenAI clients. Claude Code sends this when you set `ANTHROPIC_AUTH_TOKEN`. | | `x-api-key: sk-…` | Anthropic | What the Anthropic SDKs send by default. | | `anthropic-version: 2023-06-01` | Anthropic | Required by the Messages API. SDKs add it automatically. | > **Per-key controls:** Under [API keys](https://oneaiapikey.com/console/keys) you can give each key a name, a spending cap, an expiry date and an allowed-model list. Use one key per app or teammate so you can revoke a single key without downtime elsewhere. ## Endpoints - [POST`/v1/chat/completions`Chat with any model. The most widely supported endpoint. (OpenAI format)](https://oneaiapikey.com/docs/api-reference#chat-completions) - [POST`/v1/responses`OpenAI Responses API, used by Codex CLI and newer OpenAI SDK features. (OpenAI format)](https://oneaiapikey.com/docs/api-reference#responses) - [POST`/v1/messages`Anthropic Messages API, used by Claude Code and the Anthropic SDKs. (Anthropic format)](https://oneaiapikey.com/docs/api-reference#messages) - [GET`/v1/models`List the model ids your key can call. (OpenAI format)](https://oneaiapikey.com/docs/api-reference#models) - [POST`/v1/embeddings`Vector embeddings, when an embedding model is enabled. (OpenAI format)](https://oneaiapikey.com/docs/api-reference#embeddings) ## Chat completions POST `https://api.oneaiapikey.com/v1/chat/completions` (OpenAI format) The universal endpoint. Every model in the catalogue that supports the OpenAI format can be called here, including Claude models, which the gateway translates for you. ### Request body `model`stringrequired Exact model id, for example `gpt-5.6-terra`. See [models by format](https://oneaiapikey.com/docs/api-reference#model-formats). `messages`arrayrequired Conversation so far. Each item has a `role` (`system`, `user`, `assistant`, `tool`) and `content`. `stream`boolean Send tokens as server-sent events. Recommended for long outputs. `stream_options`object `{"include_usage": true}` adds a final chunk with token usage. `max_tokens / max_completion_tokens`integer Upper bound on generated tokens. Reasoning models prefer `max_completion_tokens`. `temperature, top_p`number Sampling controls. Some reasoning models ignore or reject them. `tools, tool_choice`array, string | object Function calling. See [tool calling](https://oneaiapikey.com/docs/api-reference#tool-calling). `response_format`object Ask for JSON output (`{"type": "json_object"}` or a JSON schema) on models that support it. Not forwarded to Claude models; see [structured output](https://oneaiapikey.com/docs/structured-output). `stop`string | array Stop generating when one of these strings appears. Other standard OpenAI parameters are forwarded when the target model supports them. Unsupported parameters are either ignored or rejected with a `400` by the upstream model. The full field list, and what changes for Claude models, is on [Chat completions](https://oneaiapikey.com/docs/chat-completions). Example request: ```bash curl https://api.oneaiapikey.com/v1/chat/completions \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "messages": [ {"role": "system", "content": "You are a concise assistant."}, {"role": "user", "content": "Give me three names for a CLI tool."} ], "max_tokens": 200 }' ``` ### Response 200 OK: ```json { "id": "chatcmpl-9f2c…", "object": "chat.completion", "created": 1760000000, "model": "gpt-5.6-terra", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1. shipit\n2. tern\n3. relay" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 27, "completion_tokens": 14, "total_tokens": 41 } } ``` `finish_reason` is `stop` for a natural end, `length` when `max_tokens` was hit, and`tool_calls` when the model wants you to run a tool. ## Responses API POST `https://api.oneaiapikey.com/v1/responses` (OpenAI format) OpenAI's newer request shape (`input` instead of `messages`, typed output items). Codex CLI uses it by default. It is fully supported for GPT-family models. Claude models are translated, but the stateful fields (`previous_response_id`, `conversation`) are rejected; for other families use chat completions. Responses are not stored, so there is no `GET` or `DELETE` on `/v1/responses/{id}`. See [Responses API](https://oneaiapikey.com/docs/responses). curl: ```bash curl https://api.oneaiapikey.com/v1/responses \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-terra", "input": "Summarise the Unix philosophy in one line." }' ``` Python: ```python resp = client.responses.create( model="gpt-5.6-terra", input="Summarise the Unix philosophy in one line.", ) print(resp.output_text) ``` Node: ```js const resp = await client.responses.create({ model: "gpt-5.6-terra", input: "Summarise the Unix philosophy in one line.", }); console.log(resp.output_text); ``` ## Messages (Anthropic) POST `https://api.oneaiapikey.com/v1/messages` (Anthropic format) The native Anthropic format, used by Claude Code and the Anthropic SDKs. Use it for models marked [Anthropic format](https://oneaiapikey.com/docs/api-reference#model-formats). Prefer it for Claude when you need Anthropic-specific features such as prompt caching controls, extended thinking blocks or exact `stop_reason` values. Required fields: `model`, `max_tokens` and `messages`. Optional: `system`, `stream`, `temperature`, `stop_sequences`, `tools`, `tool_choice`,`thinking`, `output_config`. `anthropic-beta` headers are forwarded to the upstream.`anthropic-version` defaults to `2023-06-01` when you leave it out. Token counting (`/v1/messages/count_tokens`) is not available. See [Anthropic Messages](https://oneaiapikey.com/docs/anthropic-messages). curl: ```bash curl https://api.oneaiapikey.com/v1/messages \ -H "x-api-key: $RELAYKIT_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-5-5", "max_tokens": 1024, "system": "You are a senior code reviewer.", "messages": [{"role": "user", "content": "Review: def add(a, b): return a - b"}] }' ``` Python: ```python import os import anthropic client = anthropic.Anthropic(base_url="https://api.oneaiapikey.com", api_key=os.environ["RELAYKIT_API_KEY"]) msg = client.messages.create( model="claude-sonnet-5-5", max_tokens=1024, system="You are a senior code reviewer.", messages=[{"role": "user", "content": "Review: def add(a, b): return a - b"}], ) print(msg.content[0].text) ``` Node: ```js import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://api.oneaiapikey.com", apiKey: process.env.RELAYKIT_API_KEY }); const msg = await client.messages.create({ model: "claude-sonnet-5-5", max_tokens: 1024, system: "You are a senior code reviewer.", messages: [{ role: "user", content: "Review: def add(a, b): return a - b" }], }); console.log(msg.content[0].text); ``` 200 OK: ```json { "id": "msg_01H…", "type": "message", "role": "assistant", "model": "claude-sonnet-5-5", "content": [ { "type": "text", "text": "Bug: the function subtracts instead of adding. Use `return a + b`." } ], "stop_reason": "end_turn", "usage": { "input_tokens": 31, "output_tokens": 19, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0 } } ``` ## List models GET `https://api.oneaiapikey.com/v1/models` (OpenAI format) Returns the model ids your key is allowed to call. If a key has an allowed-model list, only those models appear. Sent with`x-api-key` and `anthropic-version` headers (as the Anthropic SDKs do), the same route answers in Anthropic's list format. Request: ```bash curl https://api.oneaiapikey.com/v1/models \ -H "Authorization: Bearer $RELAYKIT_API_KEY" ``` 200 OK (shortened): ```json { "object": "list", "data": [ { "id": "claude-opus-5-5", "object": "model" }, { "id": "claude-sonnet-5", "object": "model" }, { "id": "claude-haiku-4-5", "object": "model" } ] } ``` ## Embeddings POST `https://api.oneaiapikey.com/v1/embeddings` (OpenAI format) Accepts the standard OpenAI embeddings request. It only works with embedding models; chat models return an error here. Check `GET /v1/models` to see whether an embedding model is enabled for your account before building on it. Example request: ```bash curl https://api.oneaiapikey.com/v1/embeddings \ -H "Authorization: Bearer $RELAYKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "", "input": ["first document", "second document"] }' ``` ## Streaming (SSE) Set `"stream": true` and read the response as `text/event-stream`. Official SDKs handle parsing for you. If you parse by hand, split on blank lines, read the `data:` field, and ignore event types you do not recognise (for example keep-alive `ping` events). OpenAI format: ```text data: {"id":"chatcmpl-1","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]} data: {"id":"chatcmpl-1","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hel"}}]} data: {"id":"chatcmpl-1","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"lo"}}]} data: {"id":"chatcmpl-1","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: {"id":"chatcmpl-1","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":2,"total_tokens":14}} data: [DONE] ``` Anthropic format: ```text event: message_start data: {"type":"message_start","message":{"id":"msg_1","role":"assistant","content":[],"usage":{"input_tokens":12,"output_tokens":1}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":2}} event: message_stop data: {"type":"message_stop"} ``` - OpenAI format ends with `data: [DONE]`. Add `"stream_options": {"include_usage": true}` to receive token usage in the last chunk. - Anthropic format ends with `message_stop`; token usage arrives in `message_start` and `message_delta`. - An error that happens mid-stream arrives as an error event; treat the partial output as incomplete. ## Tool calling Function and tool calling work on models that support them upstream (current Claude and GPT models do). Send tool definitions in the format of the endpoint you are calling; the gateway translates between formats when a Claude model is called through the OpenAI endpoint. OpenAI format: ```json { "model": "gpt-5.6-terra", "messages": [ { "role": "user", "content": "What is the weather in Pune?" } ], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Current weather for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": [ "city" ] } } } ], "tool_choice": "auto" } ``` Anthropic format: ```json { "model": "claude-sonnet-5-5", "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in Pune?" } ], "tools": [ { "name": "get_weather", "description": "Current weather for a city", "input_schema": { "type": "object", "properties": { "city": { "type": "string" } }, "required": [ "city" ] } } ] } ``` 1. The model replies with `tool_calls` (OpenAI) or a `tool_use` content block (Anthropic). 2. Your code runs the tool and sends the result back: a `tool` role message with the matching `tool_call_id`, or a `tool_result` block with the matching `tool_use_id`. 3. The model continues with the result. Each round trip is a separate, separately billed request. > **Best fidelity:** Cross-format translation covers text, images, PDFs, tools, streaming and `reasoning_effort`. It does not carry over `response_format` or Anthropic `cache_control` markers, so for structured output and prompt caching on Claude call it through `/v1/messages`. ## Usage & billing Every successful response reports the tokens you were charged for. Your prepaid balance is reduced by _input tokens × input rate + output tokens × output rate_, using the per-million-token rates on the [models page](https://oneaiapikey.com/models). Cached input tokens are billed at the lower cached rate where a model has one. | Meaning | OpenAI format | Anthropic format | | --- | --- | --- | | Input tokens | `usage.prompt_tokens` | `usage.input_tokens` | | Output tokens | `usage.completion_tokens` | `usage.output_tokens` | | Cached input read | `usage.prompt_tokens_details.cached_tokens` | `usage.cache_read_input_tokens` | | Cache write | n/a | `usage.cache_creation_input_tokens` | | Reasoning / thinking | Included in `completion_tokens` | Included in `output_tokens` | **Worked example.** A Claude Sonnet 5.5 request with 12,000 input tokens and1,500 output tokens costs $0.0264 for input ($2.20 per 1M) plus$0.0165 for output ($11 per 1M), so **$0.0429** in total. The console shows your live [balance](https://oneaiapikey.com/console/billing) and a [per-request usage log](https://oneaiapikey.com/console/usage) with model, tokens and cost, so you can reconcile any charge. Requests rejected by the gateway (invalid key, no balance, unknown model) are not charged. ## Rate limits & timeouts - **Gateway limits.** There is no per-account request rate limit on `/v1/*`. Per-model limits are off by default; when one applies, the `429` message says "Rate limit reached: at most N requests per M minutes". - **Upstream limits.** Providers throttle a model under heavy load. These surface as `429` or `503` and are safe to retry with backoff. The gateway does not retry for you. - **Streaming timeout.** A stream is closed after **300 seconds** without data from the model. - **Non-streaming requests.** The gateway does not cut them off, but your client or a proxy may. Use `stream: true` for long outputs and reasoning models, and set your HTTP client timeout to several minutes. - **Request size.** Request bodies are limited to 128 MB (`413` above that). Details and retry code: [Rate limits and retries](https://oneaiapikey.com/docs/rate-limits). Planning sustained high volume (batch jobs, many parallel agents)? Email [support@relaykit.dev](mailto:support@relaykit.dev) before launch so we can confirm capacity. ## Errors Errors use the OpenAI envelope, `{"error": {"message", "type", "code"}}`, with an HTTP status that tells you what to do: `400` fix the request, `401` check the key, `403` balance, key limits or model access, `404` unknown model or route, `429` and `5xx` retry with backoff. Errors from Claude models on `/v1/messages` use Anthropic's `{"type": "error", "error": {...}}` shape. Every response carries an `X-Request-Id` header to quote to support. See [Errors](https://oneaiapikey.com/docs/errors) for every message. ## Not available These parts of the OpenAI and Anthropic APIs are not offered. Requests to them fail with `404` or `501`: - Files, fine-tuning, image variations, Assistants, Threads and Batch (OpenAI). - Stored responses: `GET` / `DELETE /v1/responses/{id}`. - Token counting, Message Batches and the Files API (Anthropic). ## Models by format Exact ids to send in the `model` field, and which endpoint family each one accepts. Rates are USD per 1M tokens. | Model id | Endpoints | In / Out | | --- | --- | --- | | [claude-opus-5-5](https://oneaiapikey.com/models/claude-opus-5-5)Claude | openaianthropic | $4.40 / $22 | | [claude-sonnet-5](https://oneaiapikey.com/models/claude-sonnet-5)Claude | openaianthropic | $2.20 / $11 | | [claude-haiku-4-5](https://oneaiapikey.com/models/claude-haiku-4-5)Claude | openaianthropic | $1.10 / $5.50 | | [gpt-6.1-sol](https://oneaiapikey.com/models/gpt-6-1-sol)GPT | openai | $2.20 / $11 | | [gpt-5.6-sol](https://oneaiapikey.com/models/gpt-5-6-sol)GPT | openai | $5.50 / $33 | | [gpt-5.6-terra](https://oneaiapikey.com/models/gpt-5-6-terra)GPT | openai | $2.20 / $13.20 | | [grok-4.6](https://oneaiapikey.com/models/grok-4-6)Grok | openai | $2.20 / $6.60 | | [claude-sonnet-5-5](https://oneaiapikey.com/models/claude-sonnet-5-5)Claude | openaianthropic | $2.20 / $11 | | [grok-4.7](https://oneaiapikey.com/models/grok-4-7)Grok | openai | $2.20 / $6.60 | | [deepseek-v4-flash](https://oneaiapikey.com/models/deepseek-v4-flash)DeepSeek | openai | $0.24 / $0.96 | | [deepseek-v4-pro](https://oneaiapikey.com/models/deepseek-v4-pro)DeepSeek | openai | $1.32 / $3.96 | --- # Docs for AI tools > Give coding assistants and chatbots these docs as plain Markdown, through llms.txt, llms-full.txt, a .md version of every page, or a short project rules file. Source: https://oneaiapikey.com/docs/llms AI coding assistants write better integration code when they can read the real docs instead of guessing. Every page here is available as plain Markdown, in the forms below. ## llms.txt [`https://oneaiapikey.com/llms.txt`](https://oneaiapikey.com/llms.txt) is a short index of every docs page with a one-line summary and a link to its Markdown version, following the [llms.txt](https://llmstxt.org) convention. Give this URL to a tool that can follow links. ## llms-full.txt [`https://oneaiapikey.com/llms-full.txt`](https://oneaiapikey.com/llms-full.txt) is the whole documentation in one Markdown file. Use it when a tool takes one document as context, such as a project knowledge file in a chat app. It is large; for a single task, one page is usually enough. ## One page as Markdown - Add `.md` to any docs URL, for example [`/docs/streaming.md`](https://oneaiapikey.com/docs/streaming.md). - Or use **Copy as Markdown** at the top of any page and paste it into your assistant. Code examples in the Markdown versions use the same base URLs, model ids and environment variable names as the pages. ## A rules file for your project Coding agents read a project rules file (`AGENTS.md`, `CLAUDE.md`, `.cursor/rules` and similar) on every task. Paste this block into yours so the agent uses the right base URL, key variable and model ids: AGENTS.md: ```markdown ## Relaykit API - OpenAI format: base URL https://api.oneaiapikey.com/v1, header "Authorization: Bearer $RELAYKIT_API_KEY". - Anthropic format (Claude models only): base URL https://api.oneaiapikey.com (no /v1; SDKs add /v1/messages), header "x-api-key". - Read the key from the RELAYKIT_API_KEY environment variable. Never hard-code it or print it. - Model ids: claude-haiku-4-5 (fast), gpt-5.6-terra, claude-sonnet-5-5. Full list: https://oneaiapikey.com/docs/models.md - For Claude, response_format is ignored in chat completions: use a forced tool call or /v1/messages. - Docs index: https://oneaiapikey.com/llms.txt ``` ## Related - [Coding tools overview](https://oneaiapikey.com/docs/guides): set up each agent and editor to use the gateway. - [API reference](https://oneaiapikey.com/docs/api-reference): every endpoint.