# 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.
