---
title: OpenAI Compatibility
path: api-guides/openai-compatibility
status: published
---

# OpenAI Compatibility

ScaiGrid exposes an OpenAI-compatible API surface at `/oai/v1/`. Point any OpenAI SDK at ScaiGrid by changing the base URL — no code changes required.

This page covers what works, what differs, and how to migrate.

## Why use the OpenAI layer

- **Drop-in migration.** Existing codebases that target `api.openai.com` switch to ScaiGrid by changing one config value.
- **Third-party tool compatibility.** Libraries that assume the OpenAI shape (LangChain, LlamaIndex, agent frameworks) work unmodified.
- **SDK maturity.** OpenAI's SDKs are the most battle-tested. Use them; get ScaiGrid's routing, accounting, and multi-tenancy transparently.

## When NOT to use the OpenAI layer

- **New integrations.** The native `/v1/` API has more features — multimodal content handling, structured sessions, extended model metadata — that don't fit OpenAI's shape. New code should use `/v1/`.
- **Tight ScaiLabs integrations.** Other ScaiLabs products (ScaiBot, ScaiBunker, etc.) use `/v1/` exclusively.

See [Philosophy](../01-introduction/02-philosophy.md) for the reasoning.

## Base URLs

Two equivalent ways to reach the compat layer:

```
https://openai.scailabs.ai/v1          (preferred — dedicated vhost)
https://scaigrid.scailabs.ai/oai/v1    (same routes on the main domain)
```

Both serve the exact same endpoints. `openai.scailabs.ai` exists so your configuration says `base_url = "https://openai.scailabs.ai/v1"` and looks like what the OpenAI SDK expects.

## Using with the OpenAI Python SDK

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://openai.scailabs.ai/v1",
    api_key="sgk_your_scaigrid_key",  # your ScaiGrid API key, not an OpenAI key
)

response = client.chat.completions.create(
    model="scailabs/poolnoodle-omni",  # ScaiGrid model slug
    messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
```

Everything works — streaming, tool calls, embeddings, images, audio. The SDK doesn't know it's not talking to OpenAI.

## Using with the OpenAI Node SDK

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://openai.scailabs.ai/v1",
  apiKey: process.env.SCAIGRID_API_KEY,
});

const response = await client.chat.completions.create({
  model: "scailabs/poolnoodle-omni",
  messages: [{ role: "user", content: "Hello!" }],
});
console.log(response.choices[0].message.content);
```

## Supported endpoints

| OpenAI endpoint | ScaiGrid compat | Notes |
|-----------------|-----------------|-------|
| `POST /chat/completions` | ✓ | Full support, including streaming and tool calls |
| `GET /models` | ✓ | Returns ScaiGrid frontend models with context_window, max_output_tokens extensions |
| `GET /models/{id}` | ✓ | |
| `POST /embeddings` | ✓ | |
| `POST /images/generations` | ✓ | |
| `POST /audio/transcriptions` | ✓ | |
| `POST /audio/speech` | ✓ | |
| `POST /batches` | ✓ | |
| `GET /batches/{id}` | ✓ | |
| `POST /batches/{id}/cancel` | ✓ | |
| `POST /responses` | ✓ | |
| `GET /responses/{id}` | ✓ | |
| `POST /fine-tuning/jobs` | Via [ScaiMind](/docs/scaigrid/scaimind) | Use the native ScaiMind API, not compat |
| `POST /files` | Via [ScaiDrive](/docs/scaigrid/scaimatrix) or [ScaiMatrix](/docs/scaigrid/scaimatrix) | |
| `POST /assistants` | Via [ScaiCore](/docs/scaigrid/modules/scaicore) | Different mental model, no compat layer |

## Differences from real OpenAI

### Model names

OpenAI uses model IDs like `gpt-4`, `gpt-4o`, `gpt-3.5-turbo`. ScaiGrid uses frontend model slugs like `openai/gpt-4o`, `scailabs/poolnoodle-omni`, `anthropic/claude-opus`. The slug tells routing which upstream to use.

```python
# OpenAI-SDK style, targeting OpenAI directly
client.chat.completions.create(model="gpt-4o", ...)

# Same SDK, targeting ScaiGrid
client.chat.completions.create(model="openai/gpt-4o", ...)

# Or use a different provider entirely — same SDK
client.chat.completions.create(model="anthropic/claude-opus", ...)
```

List available models:

```python
for m in client.models.list():
    print(m.id)
```

### Extended fields on `/models`

ScaiGrid adds fields OpenAI's response doesn't:

```json
{
  "id": "scailabs/poolnoodle-omni",
  "object": "model",
  "created": 1713888000,
  "owned_by": "scaigrid",
  "display_name": "Poolnoodle Omni",
  "context_window": 256000,
  "max_output_tokens": 32768,
  "modality": "chat"
}
```

`context_window`, `max_output_tokens`, and `modality` are additive — OpenAI SDK users ignore them. Custom clients can use them for capacity planning.

### Streaming error frames

When a stream errors out mid-completion, the OpenAI-compat layer emits:

```
event: error
data: {"code": "BACKEND_ERROR", "message": "..."}

data: [DONE]
```

This differs from OpenAI's behavior (which silently truncates or returns a plain `data: {"error": ...}` line). If your client doesn't listen for the `error` event type, you'll still see `[DONE]` but miss the error details.

Most OpenAI SDKs handle this gracefully — they surface the error via the usual error callback. Custom SSE parsers may need updates.

### Request IDs

Every response has an `x-scaigrid-request-id` header. For support, quote this ID. The OpenAI SDK doesn't expose response headers by default — access them through the SDK's response object (varies by SDK version).

### Authentication

Always a ScaiGrid API key (`sgk_`) or ScaiGrid-issued JWT. Never pass an actual OpenAI key — it won't work, and you don't want to anyway, since ScaiGrid handles the upstream authentication.

## Migrating a LangChain / LlamaIndex app

Both libraries construct their LLM client with a base URL parameter. Point it at ScaiGrid:

**LangChain:**

```python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    base_url="https://openai.scailabs.ai/v1",
    api_key=os.environ["SCAIGRID_API_KEY"],
    model="scailabs/poolnoodle-omni",
)
```

**LlamaIndex:**

```python
from llama_index.llms.openai import OpenAI

llm = OpenAI(
    api_base="https://openai.scailabs.ai/v1",
    api_key=os.environ["SCAIGRID_API_KEY"],
    model="scailabs/poolnoodle-omni",
)
```

Done. All downstream usage — agents, tools, RAG pipelines — inherits ScaiGrid's routing, accounting, and error handling.

## What the compat layer does not give you

- **Native ScaiGrid features.** Structured sessions (`/v1/sessions`), rooms, persona management, bunker provisioning, skill bindings, checkpoint resolution — all live under `/v1/modules/` and have no OpenAI equivalent.
- **Per-model ScaiGrid metadata in the SDK's type system.** `context_window` etc. come through as extra fields the SDK doesn't type.
- **ScaiGrid error codes in the SDK's exception types.** Errors show up as generic `openai.APIError` with ScaiGrid's message inside. Branch on the message or switch to native `/v1/` endpoints if you need typed error handling.

## What's next

- [Chat Completions](./01-chat-completions.md) — the native `/v1/` equivalent.
- [Models and Routing](../03-core-concepts/03-models-and-routing.md) — how frontend slugs work.
- [Errors](../03-core-concepts/07-errors.md) — full error code reference.
