Messages (Anthropic)
POST /v1/messagesAccepts requests in the Anthropic Messages API format. Requests are translated to the internal OpenAI-compatible format, processed, and the response is translated back to the Anthropic format before returning.
This lets you use the Anthropic SDK with ai& without changing your code.
from anthropic import Anthropic
client = Anthropic( api_key="sk-your-api-key", base_url="https://api.aiand.com",)
message = client.messages.create( model="google/gemma-4-31b-it", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}],)
print(message.content[0].text)import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: "sk-your-api-key", baseURL: "https://api.aiand.com",});
const message = await client.messages.create({ model: "google/gemma-4-31b-it", max_tokens: 1024, messages: [{ role: "user", content: "Hello!" }],});
console.log(message.content[0].text);How it works
Section titled “How it works”- Your request arrives in Anthropic Messages format
- ai& translates it to OpenAI format and runs inference
- The response is translated back to Anthropic format
Streaming is supported. Authentication uses the same Authorization: Bearer header as all other endpoints.
Supported parameters
Section titled “Supported parameters”Standard Anthropic Messages API parameters are accepted. Parameters with no equivalent in the underlying model are silently ignored, with the exceptions below.
tools— client tools (typecustom, or no type) are translated to function tools. Anthropic’s server-side tools (web_search_*,web_fetch_*,code_execution_*,tool_search_*, …) run inside Anthropic’s API and cannot be served here. Anthropic-defined client tools (bash_*,text_editor_*,computer_*, …) carry noinput_schemafor a non-Anthropic model to follow; send them as custom tools instead. A request that includes any Anthropic-defined tool type is rejected with400 invalid_request_errornaming the tool.tool_choice—auto,any,noneandtoolare mapped;disable_parallel_tool_use: truebecomesparallel_tool_calls: false.output_config.effortandthinking—output_config.effortsets the reasoning level, andthinking: { "type": "disabled" }turns reasoning off on a named reasoning model that listsnone; see Reasoning for the exceptions.tool_resultimages — on vision-capable models, images inside a tool result are handed to the model in a user message that follows the turn’s tool results, each labelled with itstool_use_id; on text-only models the tool text carries an[Image omitted…]placeholder.
Errors
Section titled “Errors”Errors on this endpoint use Anthropic’s envelope, {"type":"error","error":{"type":"…","message":"…"},"request_id":"…"}, with Anthropic’s type names (invalid_request_error, authentication_error, permission_error, not_found_error, rate_limit_error, api_error, …). A request that exceeds the model’s context window is a 400 whose message begins prompt is too long and carries the capability_rejected: prompt_too_long token, which is what Claude Code’s automatic compaction looks for.
Keep-alive
Section titled “Keep-alive”A streaming response that stays idle for 15 seconds receives a keep-alive: an SSE comment line before message_start, a ping event after it. Clients that count bytes to detect a stalled stream (Claude Code aborts after 300 seconds of silence) keep the connection through long reasoning spans.
Reasoning
Section titled “Reasoning”The thinking block does not set the reasoning level — budget_tokens names nothing on our effort scale, and neither does enabled, so depth is otherwise left to the model’s own default. To control the level, send reasoning_effort with one of the model’s published reasoning_efforts, or Anthropic’s own output_config: { "effort": "…" }, which Claude Code sends for /effort. An explicit reasoning_effort wins when both are present. Either is checked exactly as on /v1/chat/completions: a level the model doesn’t list runs at the next listed level up, or the highest, which can cost more than the level you sent, a value outside the effort vocabulary is a 400 naming the field you sent, and the applied level comes back in X-Reasoning-Effort.
thinking: { "type": "disabled" } is the one exception, because it does state something the scale can express: no reasoning at all. We apply it as reasoning_effort: "none" and return X-Reasoning-Effort: none to confirm it. It wins over output_config.effort, so a request that disables thinking is not billed at the effort in that field. On a reasoning model that publishes no none the request is a 400 naming thinking, with the same sentence an explicit reasoning_effort none gets. The error text is on Errors. Send reasoning_effort as well and that is what applies, leaving the block to control the response shape alone. A model without the reasoning capability ignores it, having nothing to suppress.
With the reserved auto model the block sets no level either way. auto accepts no effort at all, so name a model directly if you need reasoning off. Claude Code’s output_config.effort is ignored under auto rather than rejected.
What thinking does control is whether that reasoning is returned to you. Send thinking: { "type": "enabled" } — or "adaptive", which is accepted identically — and the reasoning comes back as a thinking content block ahead of the text block, streaming as thinking_delta events. Omit it and you get the answer alone.
The thinking blocks we return carry no signature — nothing upstream signs them, so one replayed to Anthropic’s own API is rejected. Echoing one back to ai& is safe: a thinking block on a request is dropped before it reaches the model.
Some reasoning models put their whole answer inside the reasoning and leave the message content empty. Those turns come back as an ordinary text block whether or not you asked for thinking, so a successful response always carries at least one content block.
Image input
Section titled “Image input”Image content blocks are supported on vision-capable models. All three Anthropic source types work — base64, url, and file (referencing a file_id from POST /v1/files). See Anthropic’s content blocks docs for the wire format. Requests against models without the vision capability are rejected with 400 invalid_request_error before inference. A url source must meet the URL requirements.
Document input
Section titled “Document input”PDF document blocks are supported on vision-capable models, with a base64 source (media_type: "application/pdf") or a file source referencing a purpose: "document" upload. Each page reaches the model as an image, and a PDF inside a tool_result is handled like a tool-result image. A base64 PDF is stored as a document file and reused, with the same limits as an inline PDF on Chat Completions, except under zero data retention, where it is rendered in memory, not stored, and held to its own limits. text and content sources are passed to the model as text. A url source is not fetched, but it must still meet the URL requirements, or the request is rejected.