API reference
Create message
POST /v1/messages generates the next assistant message for a conversation in the Anthropic Messages format, as JSON or as a stream. It is the endpoint Claude Code and the Anthropic SDKs call.
On this page
Generates the next assistant message for a conversation, in the Anthropic Messages format. This is the endpoint Claude Code and the Anthropic SDKs call. It runs the same model at the same prices as chat completions.
This is the Anthropic Messages API, so Claude Code and the official Anthropic SDKs work with a change of base URL and key. It runs the same model as chat completions, at the same prices, with the same limits, and is billed from the usage the model reports. To connect Claude Code, follow the Claude Code guide.
The Anthropic SDKs add /v1/messages to the base URL themselves, so give them https://api.larasrouter.com without /v1. Errors from this endpoint use the Anthropic error envelope, not the OpenAI one.
Request body
modelstringrequiredModel ID. Send
deepseek-flash, not a Claude model name. A trailing[1m]is ignored.max_tokensintegerrequiredOutput cap, 1 to 384,000. Required, as in the Anthropic API. A larger value is rejected, never silently reduced. Reasoning tokens count toward it.
messagesarrayrequiredThe conversation so far, oldest first. Each message has a
roleofuserorassistantandcontentas a string or an array of blocks:text,tool_use,tool_result, andthinking. Image and document blocks are rejected.systemstring | arrayThe system prompt, as a string or an array of text blocks.
streambooleanSend the response as Server-Sent Events. Defaults to
false.toolsarrayTools the model may call:
{name, description, input_schema}. Tools that run on Anthropic’s servers, such as web search, are ignored.tool_choiceobject{"type": "auto"},"any","none", or{"type": "tool", "name": …}.thinkingobject{"type": "enabled"}or{"type": "disabled"}turns reasoning on or off;budget_tokensis ignored.deepseek-flashreasons by default.output_configobject{"effort": "low" | "medium" | "high" | "xhigh" | "max"}sets the reasoning effort.temperature, top_p, stop_sequences, metadata—Sampling parameters and
metadata.user_id, forwarded to the model.top_kandcache_controlare accepted and ignored.
Example request
curl https://api.larasrouter.com/v1/messages \
-H "x-api-key: $LARAS_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-flash",
"max_tokens": 1024,
"system": "You are a concise assistant.",
"messages": [
{"role": "user", "content": "Name three uses of a hash map."}
]
}'
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://api.larasrouter.com",
api_key=os.environ["LARAS_API_KEY"],
)
message = client.messages.create(
model="deepseek-flash",
max_tokens=1024,
system="You are a concise assistant.",
messages=[{"role": "user", "content": "Name three uses of a hash map."}],
)
print(message.content[-1].text)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "https://api.larasrouter.com",
apiKey: process.env.LARAS_API_KEY,
});
const message = await client.messages.create({
model: "deepseek-flash",
max_tokens: 1024,
system: "You are a concise assistant.",
messages: [{ role: "user", content: "Name three uses of a hash map." }],
});
console.log(message.content.at(-1));
Response body
idstringMessage ID,
msg_followed by the billing request ID.typestringAlways
message.rolestringAlways
assistant.modelstringThe model that answered.
contentarrayThe answer as blocks, in order: an optional
thinkingblock with the model’s reasoning, thentextandtool_useblocks.stop_reasonstringend_turn,max_tokens(output cap reached), ortool_use.usageobjectToken counts the charge is computed from.
input_tokensintegerInput tokens that were not served from cache. As in the Anthropic API, cached tokens are not included.
cache_read_input_tokensintegerInput tokens served from cache, billed at the cache-hit rate.
cache_creation_input_tokensintegerAlways
0.output_tokensintegerOutput tokens, reasoning included.
{
"id": "msg_0f8fad5bd9cb469fa16570867728950e",
"type": "message",
"role": "assistant",
"model": "deepseek-flash",
"content": [
{ "type": "thinking", "thinking": "The user wants three uses…", "signature": "lr1-…" },
{ "type": "text", "text": "1. Caching results by key…" }
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 24,
"output_tokens": 180,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
}
}
The thinking block carries the model’s reasoning, which is billed as output. Send it back unchanged on the next turn of a conversation that uses tools: the model requires its earlier reasoning to continue a tool conversation. The signature is an opaque value; Laras Router does not verify it on the way back.
Tool use
Declare tools in tools. When the model calls one, the reply has stop_reason: "tool_use" and a tool_use block with an id, a name, and an input object. Run the tool and send the result in the next user message as a tool_result block that carries the same tool_use_id.
Streaming
With "stream": true the response is a stream of Anthropic events: message_start, then content_block_start, content_block_delta and content_block_stop for each block, ping while the model is busy, and finally message_delta and message_stop. Tool input arrives as input_json_delta fragments that concatenate into the input object.
message_delta carries the stop reason and the final usage. It and message_stop are sent only after the request’s usage has been recorded, so a client that has seen message_stop knows the charge is final. If the stream is cut short, it ends with an error event and no message_stop.
event: message_start
data: {"type":"message_start","message":{"id":"msg_…","type":"message","role":"assistant","model":"deepseek-flash","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_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":"1. Caching"}}
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":{"input_tokens":24,"output_tokens":180,"cache_creation_input_tokens":0,"cache_read_input_tokens":0}}
event: message_stop
data: {"type":"message_stop"}
What is not supported
- Image and document blocks are rejected with
400 invalid_request_error, because the model takes text only. - Tools that run on Anthropic’s servers, such as web search, are ignored. Your own tools work.
POST /v1/messages/count_tokensis not available. Claude Code falls back to an estimate.top_k,cache_control, andbudget_tokensare accepted and ignored.
Errors
Errors use the Anthropic envelope, with the type your SDK expects:
{
"type": "error",
"error": {
"type": "billing_error",
"message": "Insufficient balance. Top up to continue."
},
"request_id": "req_0f8fad5bd9cb469fa16570867728950e"
}
request_id is the billing identity of the request, present once the request reached billing. Branch on error.type and the HTTP status; the message is in English by default and in Bahasa Indonesia when the request carries Accept-Language: id. See Errors & retries.
Responses
stream is true.X-Laras-Request-IDstring — Billing identity of the request (UUID). Quote it when you contact support about a charge.
invalid_request_error): a missing max_tokens, an image or document block, or an output cap above the model’s. The message names the part of the request that was refused. Nothing was held or charged.Authorization and x-api-key carry different keys (authentication_error).billing_error). Rejected before the model is called; nothing is held.chat:write scope (permission_error).not_found_error). Claude Code sends Claude model names unless you set ANTHROPIC_MODEL; use deepseek-flash.request_too_large).rate_limit_error). Wait for Retry-After.Retry-Afterinteger — Seconds to wait before retrying.
api_error). Retry with backoff.api_error). Retry later. You were not charged.