SDK Compatibility

ModelRelay provides drop-in compatibility endpoints for the OpenAI and Anthropic APIs. If you have existing code using these providers’ SDKs, you can point them at ModelRelay with a base URL change to get unified billing, observability, and access to any model.

Why Use Compatibility Endpoints?

  • Minimal code changes - Point existing code at ModelRelay with a base URL change
  • Unified billing - Track usage across all providers through ModelRelay
  • Provider flexibility - Use any model through the same API interface
  • Gradual migration - Move to ModelRelay without rewriting your application

OpenAI Responses API

Endpoint: POST /v1/responses

Point the OpenAI SDK at ModelRelay by changing the base URL and using your ModelRelay API key.

Configuration

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.MODELRELAY_API_KEY,
  baseURL: "https://api.modelrelay.ai/v1",
});

const response = await client.responses.create({
  model: "claude-sonnet-5", // Use any ModelRelay model
  input: "What is the capital of France?",
});

console.log(response.output_text);
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["MODELRELAY_API_KEY"],
    base_url="https://api.modelrelay.ai/v1",
)

response = client.responses.create(
    model="claude-sonnet-5",  # Use any ModelRelay model
    input="What is the capital of France?",
)

print(response.output_text)
curl -X POST https://api.modelrelay.ai/v1/responses \
  -H "Authorization: Bearer $MODELRELAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "input": "What is the capital of France?"
  }'

Streaming

Enable streaming by setting stream: true:

const stream = await client.responses.create({
  model: "claude-sonnet-5",
  input: "Write a haiku about programming.",
  stream: true,
});

for await (const event of stream) {
  if (event.type === "response.output_text.delta") {
    process.stdout.write(event.delta);
  }
}
stream = client.responses.create(
    model="claude-sonnet-5",
    input="Write a haiku about programming.",
    stream=True,
)

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)

Authentication

Use Authorization: Bearer <mr_sk_...> with your ModelRelay secret key. The compatibility layer maps this to X-ModelRelay-Api-Key internally.

Field Mapping

OpenAI Field ModelRelay Behavior
model Passed through to ModelRelay
input String or array of messages
instructions Mapped to system message
stream Enables SSE streaming
tools Passed through
tool_choice Passed through

OpenAI Chat Completions API

Endpoint: POST /v1/chat/completions

Existing client.chat.completions.create(...) calls can use ModelRelay for non-streaming text completions. The initial surface accepts model, text messages, max_completion_tokens or its mutually exclusive legacy max_tokens alias, reasoning_effort, optional temperature, and stream when omitted or false.

The adapter is strict: streaming, functional tool/audio/refusal metadata, non-text modalities, unrecognized fields, and ambiguous message shapes return an error instead of being ignored. Null or empty metadata emitted when an SDK replays a text assistant completion is accepted and ignored. temperature is limited to resolved models without mapped reasoning controls and cannot be combined with reasoning_effort. See the Chat Completions API reference for the exact role, content, finish-reason, usage, and rejection mappings.

Anthropic Messages API

Endpoint: POST /v1/messages

Point the Anthropic SDK at ModelRelay by changing the base URL and using your ModelRelay API key.

Configuration

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  apiKey: process.env.MODELRELAY_API_KEY,
  baseURL: "https://api.modelrelay.ai/v1",
});

const message = await client.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "What is the capital of France?" }
  ],
});

console.log(message.content[0].text);
import os
import anthropic

client = anthropic.Anthropic(
    api_key=os.environ["MODELRELAY_API_KEY"],
    base_url="https://api.modelrelay.ai/v1",
)

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "What is the capital of France?"}
    ],
)

print(message.content[0].text)
curl -X POST https://api.modelrelay.ai/v1/messages \
  -H "x-api-key: $MODELRELAY_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "What is the capital of France?"}
    ]
  }'

Streaming

Enable streaming by setting stream: true:

const stream = await client.messages.stream({
  model: "claude-sonnet-5",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "Write a haiku about programming." }
  ],
});

for await (const event of stream) {
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
    process.stdout.write(event.delta.text);
  }
}
with client.messages.stream(
    model="claude-sonnet-5",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Write a haiku about programming."}
    ],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

Authentication

The Anthropic compatibility endpoint supports two authentication styles:

  • x-api-key: <mr_sk_...> - Anthropic SDK default
  • Authorization: Bearer <mr_sk_...> - Alternative style

Both are mapped to X-ModelRelay-Api-Key internally.

Every executed Messages request returns X-ModelRelay-Execution-Id. The UUID
identifies the immutable content-free pricing and settlement evidence available
from GET /api/v1/responses/executions/{execution_id}/evidence.

Field Mapping

Anthropic Field ModelRelay Behavior
model Passed through to ModelRelay
messages Converts user and assistant roles; also accepts beta-gated mid-conversation system roles
system Mapped to system message
max_tokens Required, passed through
temperature Passed through
stop_sequences Passed through
tools Converted to ModelRelay tool format
tool_choice Passed through
stream Enables SSE streaming

Mid-conversation system messages

The Messages compatibility endpoint supports system role items inside the ordered messages array. This feature requires the exact beta token below in the anthropic-beta request header:

anthropic-beta: mid-conversation-system-2026-04-07

Without that token, a system role inside messages returns 400. The role accepts string content or an array of text blocks. Its content position is preserved for both streaming and non-streaming requests. The top-level system field continues to provide the leading system prompt and does not require this beta.

System-level priority is preserved when the selected model and message placement support native mid-conversation system messages. The current native contract is limited to the claude-opus-5 family, including dated/versioned IDs: in the messages actually sent to Anthropic, the system item must follow a user turn and must either end the message array or be followed by an assistant turn. For other opted-in request shapes, including claude-sonnet-5 and a user / system / user sequence, ModelRelay sends the system item’s text at the same position as a user-role message. This compatibility representation avoids moving the instruction ahead of earlier conversation content, but it has user-level rather than system-level priority; consecutive user-role messages may also be combined by the provider.

This representation is scoped to beta-opted Anthropic Messages requests. Chat Completions, Responses, RLM, workflows, and CLI requests keep their existing system-message behavior.

Content Block Types

The Anthropic adapter supports all standard content block types:

Type Description
text Plain text content
image Base64-encoded images
tool_use Tool call from assistant
tool_result Tool execution result

Model Flexibility

The compatibility endpoints allow you to use any model available in ModelRelay, not just models from the corresponding provider:

// Use OpenAI SDK to call an Anthropic model
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.MODELRELAY_API_KEY,
  baseURL: "https://api.modelrelay.ai/v1",
});

await client.responses.create({
  model: "claude-opus-5", // Anthropic model via OpenAI API format
  input: "Hello!",
});
// Use Anthropic SDK to call an OpenAI model
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  apiKey: process.env.MODELRELAY_API_KEY,
  baseURL: "https://api.modelrelay.ai/v1",
});

await client.messages.create({
  model: "gpt-5.2", // OpenAI model via Anthropic API format
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello!" }],
});

Error Handling

Errors are returned in the format expected by each API:

OpenAI Format

{
  "error": {
    "message": "Invalid API key",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}

Anthropic Format

{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key"
  }
}

Limitations

The compatibility endpoints provide high-fidelity API translation, but some provider-specific features may not be available:

  • Anthropic caching - cache_control blocks are not supported
  • Provider-specific extensions - Custom headers or fields may be ignored
  • Batch endpoints - Use ModelRelay’s native /responses/batch instead
  • Chat Completions streaming and tools - The initial /v1/chat/completions surface is non-streaming and text-only

For full access to ModelRelay features like state handles and built-in tools,
use the ModelRelay SDK or native API.

Next Steps