Skip to content
OpenRelay is in early access, and the /v1 API is stable. New capabilities ship in the changelog.
Inference API

Messages (Anthropic)

POST /v1/messages, the Anthropic-compatible Messages endpoint, its request parameters, response shape, and cache-token billing.

Generate a model response using the Anthropic Messages API shape. This is a second API family alongside Chat Completions, for models that support it. Use it as a drop-in for the Anthropic SDKs and for Claude Code.

POST https://inference.openrelay.inc/v1/messages

The endpoint implements the Anthropic Messages API format; Anthropic features, including tool use, work as documented.

Which models support this

Not every model serves the Messages family. Check a model's supported_apis field from GET /v1/models rather than assuming:

{
  "id": "openrelay/glm-5.2",
  "supported_apis": ["chat_completions", "messages"]
}

openrelay/glm-5.2 is the current example: it supports both chat_completions and messages on the same model id. A request to a model that doesn't list messages in supported_apis returns 400 unsupported_api.

Authentication

Send your vl_ API key as x-api-key (the Anthropic SDK convention). The gateway also accepts Authorization: Bearer. See Authentication for both header forms.

curl https://inference.openrelay.inc/v1/messages \
  -H "x-api-key: $OPENRELAY_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

Request

The body follows the Anthropic Messages shape. Three fields matter most:

FieldTypeDescription
modelstring (required)An OpenRelay public model id, e.g. openrelay/glm-5.2. See Models.
max_tokensinteger (required)Upper bound on tokens to generate. Unlike Chat Completions, Messages requires this field.
messagesarray (required)The conversation so far: { "role": "user" | "assistant", "content": "…" } objects. Do not put a system role here; use the top-level system field instead.
FieldTypeDescription
systemstring or arrayThe system prompt, as a top-level field, not a message with role: "system".
temperaturenumberSampling temperature.
top_pnumberNucleus-sampling probability mass.
stop_sequencesarraySequence(s) that halt generation.
streambooleanStream the response as event-typed Server-Sent Events. See Streaming below.
toolsarrayAnthropic-shape tool definitions.

The endpoint accepts the Anthropic Messages request format, including tools and tool_choice. Parameters a model does not support are rejected with a standard error.

Example request

curl https://inference.openrelay.inc/v1/messages \
  -H "x-api-key: $OPENRELAY_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openrelay/glm-5.2",
    "max_tokens": 256,
    "system": "You are a terse assistant.",
    "messages": [
      { "role": "user", "content": "Name three primary colors." }
    ]
  }'

Response

A unary (non-streaming) response is an Anthropic message object:

{
  "id": "msg_…",
  "type": "message",
  "role": "assistant",
  "model": "openrelay/glm-5.2",
  "content": [
    { "type": "text", "text": "Red, green, and blue." }
  ],
  "stop_reason": "end_turn",
  "usage": {
    "input_tokens": 24,
    "output_tokens": 7,
    "cache_read_input_tokens": 0,
    "cache_creation_input_tokens": 0
  }
}
FieldDescription
idUnique id for this message.
modelAlways the public OpenRelay model id you requested.
contentAn array of content blocks (text, thinking, tool_use, …) making up the reply.
stop_reasonWhy generation stopped.
usageToken counts for the request. See Usage and cache billing below.

Response model id

The model field in every response is the OpenRelay model id you requested.

Streaming

Set "stream": true to receive the response as event-typed Server-Sent Events, matching the Anthropic dialect: each event carries an event: line naming the event type, followed by a data: line:

event: message_start
data: {"type":"message_start","message":{"id":"msg_…","model":"openrelay/glm-5.2","usage":{"input_tokens":24,"cache_read_input_tokens":0}}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Red"}}

event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":7}}

event: message_stop
data: {"type":"message_stop"}

This is different from the Chat Completions stream, which is data-only frames with no event: line. Key differences:

  • event: lines are load-bearing and always present, so the Anthropic SDKs dispatch on them normally.
  • Usage arrives in two places: message_start carries input_tokens and the cache counters, message_delta carries output_tokens. Combine both to get the full usage for the request.
  • There is no [DONE] sentinel. The stream ends at message_stop.

Usage and cache billing

Usage is reported using Anthropic's field names, which differ from the Chat Completions shape in one important way: input_tokens excludes cache reads. There is no subtraction to do yourself.

usage fieldBilling dimensionRate
input_tokensInput (fresh, excludes cache reads)Model's input rate
cache_read_input_tokensCached inputModel's discounted cached-input rate
cache_creation_input_tokensCache writeModel's cache-write rate
output_tokensOutputModel's output rate
{
  "usage": {
    "input_tokens": 28,
    "cache_read_input_tokens": 2496,
    "cache_creation_input_tokens": 0,
    "output_tokens": 83
  }
}

Cache write is Messages-only

cache_creation_input_tokens (the cache-write dimension) is only reported by the Messages family; Chat Completions has no equivalent field. See Pricing & billing for current per-model rates on all four dimensions.

Errors

Errors use the same status codes and error body as the rest of the gateway (see Errors): 401 for a missing or invalid key, 402 for insufficient balance, 403 for an account hold, 404 if the model doesn't exist, and 429/502/503 for transient errors. One Messages-specific case:

StatuscodeCause
400unsupported_apiThe model doesn't serve the Messages family. Check supported_apis on GET /v1/models.

Using the Anthropic SDKs

curl https://inference.openrelay.inc/v1/messages \
  -H "x-api-key: $OPENRELAY_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openrelay/glm-5.2",
    "max_tokens": 256,
    "messages": [{ "role": "user", "content": "Hello!" }]
  }'

Install the anthropic package, then point it at OpenRelay's host root (no /v1 suffix, the SDK appends its own path):

pip install anthropic
from anthropic import Anthropic

client = Anthropic(
    base_url="https://inference.openrelay.inc",
    api_key="vl_your_api_key",
)

resp = client.messages.create(
    model="openrelay/glm-5.2",
    max_tokens=256,
    messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.content[0].text)

Install @anthropic-ai/sdk:

npm install @anthropic-ai/sdk
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({
  baseURL: 'https://inference.openrelay.inc',
  apiKey: process.env.OPENRELAY_API_KEY, // 'vl_...'
});

const resp = await client.messages.create({
  model: 'openrelay/glm-5.2',
  max_tokens: 256,
  messages: [{ role: 'user', content: 'Hello!' }],
});
console.log(resp.content[0].type === 'text' ? resp.content[0].text : '');

Point Claude Code's environment variables at OpenRelay to run it on any Messages-capable model:

export ANTHROPIC_BASE_URL=https://inference.openrelay.inc
export ANTHROPIC_AUTH_TOKEN=$OPENRELAY_API_KEY
export ANTHROPIC_MODEL=openrelay/glm-5.2

claude            # agentic coding, now on openrelay/glm-5.2

Limits

The request body is capped at 10 MiB; a larger body is rejected with 413 Payload Too Large. See Errors.

On this page