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

# OpenAI compatibility

> Use the official OpenAI SDKs with NinjaChat — just change the base URL and API key.

NinjaChat exposes an OpenAI-compatible `/chat/completions` endpoint. Point any
OpenAI SDK at NinjaChat by setting two things:

* **Base URL:** `https://www.ninjachat.ai/api/v1`
* **API key:** your `nj_sk_...` key

Choose a NinjaChat model ID. Chat Completions supports OpenAI-style messages, streaming, tools and errors; available parameters depend on the model.

<Note>
  Starting a new integration? The [official NinjaChat SDKs](/sdks) add typed routing, images, video polling, usage, request traces, and webhook helpers. Keep the OpenAI SDK when you want the smallest possible migration.
</Note>

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

  client = OpenAI(
      base_url="https://www.ninjachat.ai/api/v1",
      api_key="nj_sk_YOUR_API_KEY",
  )

  resp = client.chat.completions.create(
      model="gpt-5.6-luna",
      messages=[{"role": "user", "content": "Hello!"}],
  )
  print(resp.choices[0].message.content)
  ```

  ```javascript Node.js theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    baseURL: "https://www.ninjachat.ai/api/v1",
    apiKey: "nj_sk_YOUR_API_KEY",
  });

  const resp = await client.chat.completions.create({
    model: "gpt-5.6-luna",
    messages: [{ role: "user", content: "Hello!" }],
  });
  console.log(resp.choices[0].message.content);
  ```
</CodeGroup>

### Env-var setup

The OpenAI SDKs also pick up `OPENAI_API_KEY` and `OPENAI_BASE_URL` from the
environment, so you can skip the constructor arguments entirely:

```bash theme={null}
export OPENAI_API_KEY='nj_sk_YOUR_API_KEY'
export OPENAI_BASE_URL='https://www.ninjachat.ai/api/v1'
```

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

client = OpenAI()  # reads OPENAI_API_KEY / OPENAI_BASE_URL
```

<Warning>
  Use `www.ninjachat.ai`, not the bare `ninjachat.ai` apex domain. The apex
  redirects and strips the `Authorization` header along the way, so every
  request would arrive unauthenticated.
</Warning>

## What works

* **Any model** — pass any [model ID](/models), or `ninja/auto` to let NinjaChat pick.
* **Streaming** — set `stream: true` for token-by-token server-sent events.
* **Tool calling** — standard `tools` / `tool_choice` with `function.parameters`.
* **Sampling** — `temperature`, `top_p`, `max_tokens` (or `max_completion_tokens`), `stop`, `seed`, `frequency_penalty`, `presence_penalty`.

```python theme={null}
# Streaming
for chunk in client.chat.completions.create(
    model="ninja/auto",
    messages=[{"role": "user", "content": "Write a haiku about the sea."}],
    stream=True,
):
    print(chunk.choices[0].delta.content or "", end="")
```

<Note>
  `/api/v1/chat/completions` is the OpenAI-compatible message interface. For new native integrations, use `/api/v1/responses`; both share authentication, routing, and billing.
</Note>

## What's rejected

The request schema is strict. Standard OpenAI SDK fields that have no effect on NinjaChat are dropped silently; anything that would change the meaning of the request fails with a `400` instead of being ignored:

| You send                                                       | What happens                                                                                                      |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| A field NinjaChat doesn't know                                 | `400 unsupported_parameter` — the message names the field                                                         |
| `max_tokens`                                                   | Folded into `max_completion_tokens`. If you send both they must match, or you get `400 conflicting_parameters`    |
| `n` other than `1`                                             | `400 unsupported_value` — send separate requests when you need several choices                                    |
| `logprobs: true`                                               | `400 unsupported_parameter` (`logprobs: false` is ignored)                                                        |
| Non-empty `logit_bias`                                         | `400 unsupported_parameter` (an empty object is ignored)                                                          |
| `metadata`                                                     | Ignored on `/chat/completions`, unless it isn't an object or has more than 32 keys — `400 invalid_parameter`      |
| `model: "auto"`                                                | Accepted as an alias of `ninja/auto`; the response reports the canonical id                                       |
| `model: "ensemble"`, `auto-fast`, `a>b` chains, `model:suffix` | `400 invalid_model` — retired syntax. Use `models` for ordered fallbacks and `routing` for strategy and providers |

## NinjaChat extras

The native [Responses API](/chat) adds optional features OpenAI doesn't have —
[smart routing](/smart-routing), [fallbacks](/fallback-chains),
[spend controls](/budget-routing), explicit provider policy, and per-response `cost_usd`
and request traces. Adopt the official NinjaChat SDK when you need those typed primitives.
