> ## Documentation Index
> Fetch the complete documentation index at: https://docs.routerbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Chat Completions

> OpenAI-compatible chat completions endpoint. Synchronous, with optional streaming.

## Endpoint

```
POST https://routerbase.com/v1/chat/completions
```

Drop-in compatible with the OpenAI [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). Just point your existing OpenAI client at `https://routerbase.com/v1` with your RouterBase API key.

## Request Headers

| Header          | Value                       |
| --------------- | --------------------------- |
| `Authorization` | `Bearer sk-rb-xxxxxxxxxxxx` |
| `Content-Type`  | `application/json`          |

## Request Body

<ParamField body="model" type="string" required>
  Model ID. See the [Model Overview](/overview) for the current featured chat model set, or use the live [Models API](/api-reference/models) for the full list.
</ParamField>

<ParamField body="messages" type="array" required>
  OpenAI message array: `[{role: "user", content: "..."}]`. Supports multimodal content parts for vision-capable models.
</ParamField>

<ParamField body="temperature" type="number">
  Sampling temperature, 0–2. Default model-dependent.
</ParamField>

<ParamField body="max_tokens" type="integer">
  Maximum tokens to generate.
</ParamField>

<ParamField body="top_p" type="number">
  Nucleus sampling probability, 0–1.
</ParamField>

<ParamField body="stream" type="boolean">
  If `true`, response is a Server-Sent Events stream of `data: {chunk}\n\n` chunks terminated by `data: [DONE]`.
</ParamField>

<ParamField body="stop" type="string | array">
  Stop sequences.
</ParamField>

<ParamField body="seed" type="integer">
  Sampling seed for reproducible output.
</ParamField>

<ParamField body="response_format" type="object">
  e.g. `{ "type": "json_object" }` for JSON-mode models.
</ParamField>

<ParamField body="presence_penalty" type="number">
  Penalize new tokens by whether they appear in the text so far, -2 to 2.
</ParamField>

<ParamField body="frequency_penalty" type="number">
  Penalize new tokens by their frequency in the text so far, -2 to 2.
</ParamField>

<ParamField body="tools" type="array">
  Tool/function definitions for tool-calling models.
</ParamField>

<ParamField body="include_reasoning" type="boolean" default="true">
  RouterBase extension for **reasoning models** (Kimi K3 / K2.6, OpenAI
  o-series, GPT-5, DeepSeek-R1, …). These models return their reasoning
  trace separately in `choices[].message.reasoning_content` (see
  [Response](#response)). Set `false` to strip that field from the
  response. The model always *generates* the reasoning upstream — this
  only controls whether it is returned, so it does not reduce token usage
  or cost. Non-reasoning models ignore this field.
</ParamField>

<ParamField body="prompt_cache_key" type="string">
  Optional OpenAI-style cache partition key. Requests sharing a key route
  to the same prompt cache. RouterBase sets a **per-end-user** key
  automatically (see [Prompt caching](#prompt-caching)); pass your own
  only to override — e.g. to share a cache across users in a workspace.
  Anthropic and Gemini models ignore this field and use their own cache
  primitives (RouterBase handles those automatically too).
</ParamField>

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://routerbase.com/v1/chat/completions \
    -H "Authorization: Bearer sk-rb-xxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "google/gemini-2.5-flash",
      "messages": [{"role": "user", "content": "What is 2+2?"}]
    }'
  ```

  ```python Python theme={null}
  from openai import OpenAI

  client = OpenAI(
      api_key="sk-rb-xxxxxxxxxxxx",
      base_url="https://routerbase.com/v1",
  )

  resp = client.chat.completions.create(
      model="google/gemini-2.5-flash",
      messages=[{"role": "user", "content": "What is 2+2?"}],
  )
  print(resp.choices[0].message.content)
  ```

  ```javascript JavaScript theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: "sk-rb-xxxxxxxxxxxx",
    baseURL: "https://routerbase.com/v1",
  });

  const resp = await client.chat.completions.create({
    model: "google/gemini-2.5-flash",
    messages: [{ role: "user", content: "What is 2+2?" }],
  });
  console.log(resp.choices[0].message.content);
  ```
</CodeGroup>

## Response

```json theme={null}
{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "created": 1776245700,
  "model": "google/gemini-2.5-flash",
  "choices": [{
    "index": 0,
    "message": { "role": "assistant", "content": "4" },
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 9,
    "completion_tokens": 1,
    "total_tokens": 10
  }
}
```

On a cache hit, `usage` includes a `prompt_tokens_details` object:

```json theme={null}
"prompt_tokens_details": {
  "cached_tokens": 3968,
  "cache_read_input_tokens": 3968
}
```

### Reasoning models

For **reasoning models** (Kimi K3 / K2.6, OpenAI o-series, GPT-5,
DeepSeek-R1, …) the assistant message carries the model's reasoning trace
in a separate `reasoning_content` field, alongside the final answer in
`content`:

```json theme={null}
"message": {
  "role": "assistant",
  "content": "42",
  "reasoning_content": "The user asks 17 + 25. That is 42."
}
```

`content` is always the final answer; `reasoning_content` is the thinking
that led to it. Pass [`include_reasoning: false`](#request-body) to omit
`reasoning_content` from the response. The field is absent for models that
produce no reasoning.

* `cached_tokens` — OpenAI-standard count of tokens served from cache
  (cache *read*), billed at the model's discounted Cache Read rate.
* `cache_read_input_tokens` — Anthropic-style alias for the same number,
  surfaced for parity.
* `cache_creation_input_tokens` — tokens *written* to the cache this
  request (Anthropic only; OpenAI/Gemini have no write premium).

All three fields are omitted when zero. The same details appear on the
final streaming chunk's `usage`.

## Prompt caching

For models with upstream prompt caching, RouterBase caches automatically —
no flag required. Cached input tokens bill at the model's discounted
**Cache Read** rate; for Anthropic models, cache *writes* (the first
request that creates a cache entry) bill at a \~1.25× **Cache Write**
premium. Both rates, when defined, appear on each model's pricing page.

### Per-user isolation

Caching is **isolated per end-user**: your customers' cached prefixes are
never shared with, or readable by, another customer — even when they send
identical prompts through the same RouterBase account. RouterBase derives
a stable, opaque per-user token and wires it into whichever cache
primitive the upstream honours:

* **OpenAI / GPT-5** — standard `prompt_cache_key` field. Pass your own
  `prompt_cache_key` only if you want to override RouterBase's per-user
  partition (e.g. to share a cache across users in a workspace).
* **Anthropic / Claude** — `session-id` header partitioning, plus a
  `cache_control: ephemeral` breakpoint on the system block (Claude only
  caches with an explicit breakpoint and ignores `prompt_cache_key`).
* **Google / Gemini** — Gemini's implicit cache has no per-request key,
  so RouterBase prepends a per-user salt comment to the cached prefix.
  Each user gets a distinct content hash → a distinct cache partition.

Nothing to enable — it's automatic on every chat request. The mechanism
is described in detail in [design/18\_cache-architecture.md](https://github.com/RouterBase/RouterBase/blob/main/design/18_cache-architecture.md).

### Thresholds & TTL

Upstream caches typically require **≥1024 input tokens** to take effect.
Per-model exceptions:

* Anthropic Haiku 4.5, Opus 4.5 / 4.6 / 4.7 → ≥4096 tokens
* Anthropic Sonnet 4.6 → ≥2048 tokens
* Gemini 2.5 Pro → ≥4096 tokens

Cached prefixes live for \~5 minutes after their last use. Caching
benefits long, stable system prompts and tool definitions; place static
content at the start of the prompt and variable content at the end.

## Streaming

Set `"stream": true` to receive SSE chunks. Each chunk is a `chat.completion.chunk` object with a `delta` containing the incremental content. The stream ends with `data: [DONE]`.

```python theme={null}
stream = client.chat.completions.create(
    model="google/gemini-2.5-flash",
    messages=[{"role": "user", "content": "Write a haiku"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")
```

## Vision (multimodal)

For models that accept images, use OpenAI's content-parts shape:

```json theme={null}
{
  "model": "google/gemini-2.5-flash",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "What's in this image?" },
      { "type": "image_url", "image_url": { "url": "https://..." } }
    ]
  }]
}
```
