---
title: "API Compatibility"
description: "The endpoints we serve — OpenAI chat completions and the Anthropic Messages API — and the ones we do not"
canonical: "https://inference.boundless.network/docs/api-compatibility"
last-updated: "2018-10-20T01:46:40.000Z"
---

# API Compatibility

The endpoints we serve, and the ones we don't.


The API speaks three wire formats. Point an official OpenAI SDK at our base URL and existing chat-completions code works unchanged. The same service serves the Anthropic Messages API and the Responses subset Codex CLI uses. All three formats reach the same models on the same [rate card](/docs/pricing).

## Supported

| Endpoint | Notes |
| --- | --- |
| `POST /v1/chat/completions` | Streaming (`stream: true`) and non-streaming. Returns a `usage` object with exact token counts. |
| `POST /v1/responses` | Codex-compatible Responses requests: streaming, instructions, text input, function tools, tool results, and reasoning effort. |
| `POST /v1/messages` | Anthropic Messages format: streaming and non-streaming, `system`, and tool use. |
| `POST /v1/messages/count_tokens` | Counts the input tokens of a Messages request without running it. |
| `GET /v1/models` | The live model list, with `pricing` per million tokens and `context_length` on each entry. |
| `GET /v1/model/info` | The same lineup with metadata attached: rates per token including cached input, context window, and capability flags. What a harness reads to configure itself from the wire. |

## Not supported

We keep the surface small. The following are **not** available:

- Responses features outside the Codex-compatible subset, including hosted OpenAI tools and remote conversation storage
- Embeddings (`/v1/embeddings`)
- Legacy completions (`/v1/completions`)
- Batch API
- Fine-tuning
- Image generation and audio endpoints

If your workload depends on one of these, [tell us](/support). It helps us prioritize.

## Responses API and Codex CLI

`POST /v1/responses` serves the subset of the Responses API that clients such as [Codex CLI](/docs/coding-agents-and-harnesses#codex-cli) send: the streamed request lifecycle, instructions, text input, function tools and `function_call_output` items. Point Codex at it and it works; nothing about the request needs rewriting.

This does not turn an open-weight model into an OpenAI-hosted model. OpenAI-hosted tools, server-side conversation persistence, and other provider-native Responses features are not available. Codex uses local compaction with a custom provider, so `/v1/responses/compact` is not required.

## Reasoning control

On `POST /v1/chat/completions`, set `reasoning_effort` to `none` to disable thinking:

```json
{
  "model": "glm-5.2",
  "messages": [{ "role": "user", "content": "Reply with only the answer: 17 × 23" }],
  "reasoning_effort": "low"
}
```

`low`, `medium`, and `high` enable thinking at the requested effort. Leave the field unset to use
the model default. The setting is preserved if a request moves from a primary model server to a
standby.

On `POST /v1/responses`, put the same value inside the standard Responses object, for example `"reasoning": { "effort": "none" }`. The supported effort values are `none`, `low`, `medium`, and `high`.

## Output length

There is no separate output cap per model. `max_tokens` may go as high as the model's
[context length](/docs/pricing), because the prompt and the completion share that one window: a
request is admissible while `prompt + max_tokens` fits inside it. Ask for more and the request is
refused rather than truncated.

Two things bound it below that ceiling. Your team's [tokens-per-minute limit](/docs/rate-limits) is
checked against `prompt + max_tokens` before the request is dispatched, so your limit is also the
largest single request you can make. And a reasoning model spends the same budget on thinking as on
its reply, so a budget sized for the answer alone comes back empty with `length` as the finish
reason.

## Anthropic Messages API

Send your Boundless key as `x-api-key`, or as `Authorization: Bearer` if that is what your client already does, and use one of our [model IDs](/docs/pricing). Anthropic's own `claude-*` names are not served here.

```bash [cURL]
curl https://api.inference.boundless.network/v1/messages \
  -H "x-api-key: $BOUNDLESS_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "glm-5.2",
    "max_tokens": 1024,
    "messages": [{ "role": "user", "content": "ping" }]
  }'
```

```python [Python]
import os
from anthropic import Anthropic

client = Anthropic(
  api_key=os.environ["BOUNDLESS_API_KEY"],
  base_url="https://api.inference.boundless.network",
)

message = client.messages.create(
  model="glm-5.2",
  max_tokens=1024,
  messages=[{"role": "user", "content": "ping"}],
)
```

```typescript [TypeScript]
import Anthropic from '@anthropic-ai/sdk'

const anthropic = new Anthropic({
  apiKey: process.env.BOUNDLESS_API_KEY,
  baseURL: "https://api.inference.boundless.network",
})

const message = await anthropic.messages.create({
  model: "glm-5.2",
  max_tokens: 1024,
  messages: [{ role: "user", content: "ping" }],
})
```

The Anthropic SDKs append `/v1/messages` themselves, so they take the gateway root. An OpenAI SDK takes `https://api.inference.boundless.network/v1`.

Responses are Anthropic-shaped: `content` blocks, `stop_reason`, `tool_use`, and a `usage` object keyed `input_tokens` / `output_tokens`. With `"stream": true` you get the Anthropic SSE events (`message_start`, `content_block_delta`, ... `message_stop`).

`max_tokens` is required here, as it is on Anthropic's own API, and it has to cover a reasoning model's thinking as well as its reply. Thinking is not returned in `content`, so a budget too small for both comes back with an empty `content` array and `"stop_reason": "max_tokens"`. Give reasoning models 1024 or more.

## The `usage` object

Every chat-completions response (including the final chunk of a stream) carries token counts:

```json
{
  "usage": {
    "prompt_tokens": 42,
    "completion_tokens": 128,
    "total_tokens": 170
  }
}
```

These numbers are what you are billed on. Multiply them by the [rate card](/docs/pricing) to verify any charge. Messages responses carry the same counts under Anthropic's names, `input_tokens` and `output_tokens`.

## Request ids and per-request cost

Every inference response — `/v1/chat/completions`, `/v1/responses` and `/v1/messages`, streamed or not, the gateway's own refusals included — carries an `Inference-Id` header naming that exact request, and `X-Request-Id` with the same value under its conventional name. The utility routes (`/v1/models`, `/v1/messages/count_tokens`) carry neither, and nor does a rejected key, which is refused before an id is assigned. Log it if you reconcile usage per request, and quote it to support to name one exact request.

A completed non-streamed response also reports what it cost:

```http
Inference-Id: 789a9551-4e61-45d6-8857-fc08cb7e7f68
X-Request-Id: 789a9551-4e61-45d6-8857-fc08cb7e7f68
X-Response-Cost: 0.00007228
X-Key-Spend: 0.00012594
```

`X-Response-Cost` is that one request's charge in dollars; `X-Key-Spend` is the running total for the key that made it. Both are plain decimals, never scientific notation.

The cost header is **absent rather than zero** whenever the charge is not known as the response begins — a streamed response sends its headers before the first token, and a refusal is not charged — so a `0` you do see means genuinely free, such as a fully cached read. Multiplying the [`usage`](#the-usage-object) counts by the [rate card](/docs/pricing) reproduces the same number, and the [request log](/usage) holds it exactly either way.

## Versioning and deprecation

The wire version is in the URL path. Every inference endpoint is under `/v1`, and that prefix is what you pin. A breaking change to a request or a response shape takes a new prefix; it never lands on `/v1`.

The service is invite-only while we onboard the first accounts, and during that period the model lineup, prices and base URL may still change. What follows is how you will hear about it, not a promise that nothing will move.

**Nothing is withdrawn silently.** When an endpoint, a field, or a model identifier is deprecated:

1. Its responses start carrying a `Deprecation` header the day the decision takes effect.
2. Once a removal date is set, a [`Sunset`](https://www.rfc-editor.org/rfc/rfc8594.html) header carries it.
3. This page describes the replacement and how to move.

Both headers are declared in the [OpenAPI document](/openapi.json), so a generated client can read them. Neither appears on any response today, because nothing is deprecated.

A model identifier is retired the same way. `GET /v1/models` is the live list; the [catalog](/docs/pricing) is the published lineup, and the machine-readable form of both is [`GET /api/catalog`](/api/catalog).

## Rate limits on the public endpoints

The [throughput cap](/docs/rate-limits) on your team is counted in tokens and enforced by the gateway; a refusal there is a 429 carrying `Retry-After`, and nothing refused is charged. Separately, the endpoints anyone may call without a key — `/api/catalog`, the MCP endpoints, `/a2a/v1` and `/ask` — are capped per caller in requests.

Those responses carry the IETF `RateLimit` fields, and a refusal carries `Retry-After`:

```http
RateLimit-Policy: "agent";q=120;w=60
RateLimit: "agent";r=118;t=42
```

`q` is the quota and `w` the window in seconds; `r` is what is left and `t` the seconds until it resets. Pace against them rather than treating them as a contract — the draft defines these fields as advisory, and the service may admit more or fewer.
