Skip to main content
POST /v1/messages speaks the Anthropic Messages request and response grammar. If your application already uses the Anthropic SDK, point it at RunInfra by changing two values, the base URL and the key, and leave the rest of your code alone. The endpoint is a translation layer, not a separate inference path. Requests run through the same serving pipeline as POST /v1/chat/completions, so billing, prepaid credit settlement, idempotent retries, rate limits, request limits, and cached input pricing all behave exactly as they do there.
Tool use, images, and extended thinking are not available on this endpoint yet. Every unsupported field is refused with a 400 that names it, never silently ignored. See What this endpoint does not do.

Authentication

Send your RunInfra API key in either header on this endpoint. Both accept the same keys. If both are present, x-api-key wins.
x-api-key is accepted on /v1/messages only. Every other endpoint, including GET /v1/models, takes Authorization: Bearer, so an Anthropic client’s models.list() will not authenticate. List the model ids your key can reach with a Bearer request instead:
anthropic-version is accepted and echoed back on the response. It does not select a behavior: this endpoint publishes one grammar, described on this page.

Minimal request

The Anthropic SDK appends /v1 itself, so base_url is the bare host, https://api.runinfra.ai. The OpenAI SDK does not, which is why the /v1/chat/completions examples elsewhere in these docs use https://api.runinfra.ai/v1.

Request fields

Content blocks

A message’s content is a string, or an array of {"type":"text","text":"..."} blocks. Multiple text blocks, in system or in a message, are concatenated in order with no separator inserted: nothing you did not write is added, and nothing you wrote is dropped. An empty or whitespace-only system value means no system prompt, and no system turn is sent to the model.

Response

stop_reason is derived from how generation ended: stop_sequence is always null on this endpoint. Your stop_sequences are still honored by the model, but the matched sequence is not reported back, so we return null rather than name a sequence we did not measure. usage.input_tokens is the full prompt token count, including any part served from a cached prefix, and it is exactly what the request was billed on. This endpoint does not report cache_read_input_tokens or cache_creation_input_tokens, so unlike Anthropic there is nothing to add to input_tokens to get the total. Cached input pricing still applies, and is visible on POST /v1/chat/completions and in your usage dashboard.

Streaming

Set stream: true to receive Server-Sent Events in the Anthropic event grammar. The Anthropic SDK’s client.messages.stream(...) works unchanged.
Two details worth knowing if you read the raw stream rather than using the SDK:
  • message_start reports zero usage. The prompt token count is not known until generation ends, and we would rather send a zero you can see than a number we guessed. The authoritative counts arrive on message_delta, which is where the Anthropic SDK’s own stream accumulator takes final usage from, so stream.finalMessage() reports the right totals.
  • message_delta.usage.input_tokens can be null in the rare case the upstream reported no usage at all. It is never a stand-in zero.
If generation fails after the stream has opened, the stream ends with an Anthropic error event and no message_stop:
The Anthropic SDK raises this as an APIError, keyed on error.type.

Errors

Every 4xx and 5xx uses the Anthropic error envelope, including refusals raised before the model is reached:
request_id also rides on the x-request-id response header. Quote it in any support request.

Idempotent retries

Send an Idempotency-Key header exactly as you would on POST /v1/chat/completions. The key is bound to the Anthropic request body you sent, so retrying the same Messages request replays the original outcome instead of running and billing a second generation. See Idempotent retries.

What this endpoint does not do

Each of these answers a 400 that names the field. Nothing is dropped silently. Two behaviors also differ from Anthropic and are worth planning around:
  • Reasoning is not returned, but it is still generated and still billed. Anthropic’s carrier for reasoning is a thinking block, and both the block and its streamed form require a cryptographic signature over the reasoning text that only Anthropic can issue. Rather than ship an empty or invented signature, this endpoint returns the answer alone. The reasoning still spends your max_tokens, so a small budget can be used up thinking and return an empty content array with stop_reason: "max_tokens", billed for those output tokens. Give the models room, or use Chat completions where both channels are visible.
  • Consecutive same-role turns are not merged. Anthropic combines consecutive user or assistant turns into one before the model sees them. This endpoint forwards your turns exactly as written, so a model that rejects a non-alternating conversation gives you a clear error rather than a prompt we quietly rewrote.

Chat completions

The OpenAI-shaped endpoint, with tools.

Streaming

The OpenAI-shaped stream, delta by delta.

List models

The model ids your key can reach.