Messages
The Anthropic Messages API, so an Anthropic client works against this base URL unchanged.
POST /v1/messages accepts Anthropic's request and answers in Anthropic's response shape. Construct an Anthropic SDK client with a new base URL and your key, change the model to a slug from GET /v1/models, and the rest of the integration is untouched, including the streaming event names, which is the part an Anthropic client is strictest about.
An Anthropic client
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "https://api.routehook.ai",
apiKey: process.env.ROUTEHOOK_API_KEY,
});
const message = await client.messages.create({
model: "anthropic/claude-sonnet-4",
max_tokens: 512,
system: "Answer in one sentence.",
messages: [{ role: "user", content: "Describe harbour fog." }],
});
console.log(message.content[0].text);
POST/v1/messagesAvailable
Anthropic's Messages API, so an Anthropic client works against this base URL unchanged.
Request parameters
| PARAMETER | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
| model | string | required | A slug from GET /v1/models. Anthropic/claude-sonnet-4, not a vendor date-stamped id. |
| messages | array | required | Anthropic's turns: role user or assistant, content a string or an array of blocks. |
| max_tokens | integer | required | Required here as it is in Anthropic's own API. There is no default to fall back on. |
| system | string | optional | The system prompt, sent apart from the turns rather than as one of them. |
| stream | boolean | optional | Emit Anthropic's event framing. Default false. |
| temperature | number | optional | 0 to 1: Anthropic's range, not the 0 to 2 chat completions accepts. Above 1 is 400 invalid_request. |
| top_p | number | optional | 0 to 1. Forwarded unchanged. |
| top_k | integer | optional | Forwarded. Models that do not offer it ignore it. |
| stop_sequences | string[] | optional | Up to 8 strings that end generation when produced. |
| tools | array | optional | Tool definitions in Anthropic's schema. Read the limits below before relying on these. |
| tool_choice | object | optional | Anthropic's tool selection, forwarded with the tools. Bear the tool_use limit below in mind. |
| metadata | object | optional | Accepted and carried with the request. |
max_tokens is required
Unlike POST /v1/chat/completions, this route will not run without max_tokens. That is Anthropic's rule and it is kept, because a client that relies on the requirement would otherwise start issuing unbounded generations the moment it pointed here. Omitting it is 400 invalid_request, before any upstream call is made and before any credit is reserved. It is also the ceiling the pre-flight reservation is sized on, so an honest number costs you less headroom than a large round one.
The translation
The request is translated into the gateway's internal chat request, routed through exactly the same chain as POST /v1/chat/completions (the same routing, the same failover, the same reservation and settlement), and translated back on the way out. Nothing about routing or billing is different; only the shapes at either end are.
| ANTHROPIC FIELD | WHAT HAPPENS TO IT |
|---|---|
| system | Becomes a system turn at the head of the message list, rather than staying beside it. |
| messages | Roles and content carry over one for one. A string content becomes the turn's text; a block array is flattened into it. |
| max_tokens | The output ceiling, and the size of the reservation. |
| stop_sequences | The stop list. |
| temperature, top_p, top_k | Forwarded under the same names to whatever serves the call. |
| metadata | Carried with the request; it does not change routing or price. |
How a generation ends
| WHY IT STOPPED | stop_reason |
|---|---|
| The model finished its answer | end_turn |
| It hit your max_tokens | max_tokens |
| It produced one of your stop_sequences | stop_sequence, with stop_sequence naming the string that matched |
The response
{
"id": "msg_5f81c0",
"type": "message",
"role": "assistant",
"model": "anthropic/claude-sonnet-4",
"content": [{ "type": "text", "text": "Fog swallowed the cranes one by one." }],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": { "input_tokens": 9, "output_tokens": 12 }
}
The key goes in x-api-key
An Anthropic SDK sends the key as x-api-key, and this route accepts it there, which is what lets a client be constructed with nothing but a new base URL. It is the same key and the same account as a bearer token; Authorization: Bearer sk_live_… works here too if you are calling by hand. Every other route on the platform reads the Authorization header only.
Streaming
stream: true emits Anthropic's framing, not OpenAI's: named events, each written as an event: line followed by its data: line. There is no [DONE] sentinel: message_stop ends the stream. The events come in one order: message_start, then content_block_start, a run of content_block_delta frames carrying text_delta, content_block_stop, then message_delta with the final stop_reason and output token count, then message_stop.
The event stream
event: message_start
data: {"type":"message_start","message":{"id":"msg_5f81c0","type":"message","role":"assistant","model":"anthropic/claude-sonnet-4","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":9,"output_tokens":0}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Fog"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" swallowed the cranes"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":12}}
event: message_stop
data: {"type":"message_stop"}
What is not supported
The compatibility is the Messages route and its translation, and nothing wider. Everything below is a real gap, not an omission from this page. Check it against your integration before you switch a base URL.
- Model ids are ours.
claude-sonnet-4-20250514is not a model here;anthropic/claude-sonnet-4is. An id this catalogue does not know is409 model_unavailable, which is the one line of a ported integration you will always have to change. - Content blocks come back as text. The response translation produces
textblocks, and a stream emitstext_deltaand nothing else.toolsare accepted and forwarded to the model, but a reply that is a tool call is not re-framed as atool_useblock, if your integration depends ontool_use, usePOST /v1/chat/completions, where tool calls come back in OpenAI's shape. - Errors are the gateway's envelope. Failures answer
{ error: { code, message, request_id } }with the eight codes used everywhere else here, not Anthropic's{ type: "error", error: { type } }. The HTTP status is right; code that branches on Anthropic's errortypeis not. - No other Anthropic route exists. Token counting, the Message Batches API, the Files API and the admin endpoints have no path here and answer 404.
POST /v1/messagesis the whole of the surface. - Prompt caching and extended thinking are not part of the translation.
cache_controlon a block andthinkingblocks are not modelled; where cached input is billed differently, the saving shows up in the charge rather than in this response shape.
Failures
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Missing max_tokens, an empty message list, or a malformed turn |
| 401 | invalid_api_key | Key missing from both x-api-key and Authorization |
| 402 | insufficient_credits | Refused before any upstream call |
| 409 | model_unavailable | The slug is unknown, or nothing live can serve it |
| 503 | upstream_unavailable | Upstream outage. Safe to retry |