Anthropic Messages
Anthropic Messages requests, for Claude Code and the Anthropic SDKs.
POST https://api.aimodelsrouter.ai/v1/messages takes an Anthropic Messages API request and runs it on the same models, prices, holds, key limits and failover as Chat Completions. Use it for Claude Code and the Anthropic SDKs.
- Base URL:
https://api.aimodelsrouter.ai, without/v1: Anthropic clients add/v1/messagesthemselves. - Key: your
sk-amr-v1-…key, asx-api-keyorAuthorization: Bearer. - Headers:
anthropic-versionandanthropic-betaare accepted and ignored.
model to one of our model slugs; a claude-… id gets 404 not_found_error that names the setting to change.Request body
| Field | Type | Description |
|---|---|---|
model | string, required | One of our model slugs. claude-* ids get 404 not_found_error: we don't serve Anthropic's models. |
max_tokens | integer, required | Upper bound on generated tokens. Also lowers the hold placed on your balance. |
messages | array, required | user and assistant turns (system turns are accepted too). Consecutive turns with the same role are combined, as Anthropic does. |
system | string | text blocks | System prompt, sent to the model first. |
tools | array | Custom tools with an input_schema. Tools Anthropic runs or defines (web_search_*, code_execution_*, bash_*, text_editor_*, computer_*…) get 400. A tool with defer_loading stays out of the model's context until a tool_reference in a tool_result names it. |
tool_choice | object | auto, any, tool (with name) or none; disable_parallel_tool_use is honoured. |
stop_sequences | string[] | Stop sequences. stop_reason is stop_sequence when the model provider says which one matched. |
temperature, top_p, top_k | number | Sampling parameters, passed through to the model. |
thinking | object | enabled or adaptive returns the model's reasoning as thinking blocks; without it, reasoning is not returned (it is still billed as output, as Anthropic bills thinking). display: omitted returns empty thinking text. |
output_config.effort, thinking.budget_tokens | Sent as reasoning_effort to models with the reasoning capability (max is sent as high; a budget under 4,096 tokens as low, under 16,384 as medium). | |
output_config.format | object | json_schema structured output, for models with the structured_outputs capability. |
stream | boolean | Stream the response as server-sent events. Default false. |
metadata, cache_control, service_tier, context_management | Accepted and ignored. Nothing is stored or passed to the model provider. |
Content blocks
| Block | Notes |
|---|---|
text | Text. |
image | A base64 or url source. Images inside a tool_result are shown to the model in a user turn right after the results. |
tool_use / tool_result | Tool calls and their results (text, images, documents, is_error). |
thinking | Reasoning we returned earlier. Send it back, as Claude Code does, and the model gets its own earlier reasoning again; some models need it on tool turns. redacted_thinking is ignored. |
document | Text sources are sent as text. A PDF or other file is replaced by a note telling the model it was attached but couldn't be read. |
# pip install anthropic
import os
import anthropic
client = anthropic.Anthropic(base_url="https://api.aimodelsrouter.ai", api_key=os.environ["AMR_API_KEY"])
message = client.messages.create(
model="deepseek/deepseek-v4-flash",
max_tokens=512,
system="You are concise.",
messages=[{"role": "user", "content": "Explain idempotency keys in one paragraph."}],
)
print(message.content[0].text)
print(message.usage.model_extra["cost"]) # USD chargedResponse
idis our generation id (gen-…), also in theX-Generation-Idheader.content: athinkingblock when thinking is on and the model reasoned, thetext, and atool_useblock per tool call.stop_reason:end_turn,max_tokens,tool_use,stop_sequenceorrefusal.usage:input_tokens(not counting cache reads, as Anthropic counts),cache_read_input_tokens,cache_creation_input_tokens(always 0),output_tokensandcost, the amount charged in USD. Prompt caching happens at the model provider when it supports it;cache_controlis not needed.
Streaming
With "stream": true you get Anthropic's events: message_start, then per block content_block_start, content_block_delta (text_delta, thinking_delta, signature_delta or input_json_delta) and content_block_stop, then message_delta with the stop_reason and the final usage and cost, and message_stop. While the model is still thinking we send ping events.
message_start carries an estimate of input_tokens; the real count, as charged, is in message_delta.
Count tokens
POST https://api.aimodelsrouter.ai/v1/messages/count_tokens takes the same body without max_tokens and returns {"input_tokens": N}. It is an estimate (about four bytes of prompt per token, a typical figure per image), it is free, and nothing is sent to a model.
Errors
Errors use Anthropic's envelope and status codes:
{"type": "error", "error": {"type": "billing_error", "message": "Insufficient credits. This request can cost up to $0.004100 (prompt plus the maximum output for `deepseek/deepseek-v4-flash`). Add credits, or set a lower `max_tokens` to reduce the hold."}}| HTTP | error.type | When |
|---|---|---|
| 400, 405 | invalid_request_error | Malformed request, an unsupported field or tool, or a parameter the model rejected. |
| 401 | authentication_error | Missing, unknown, disabled, revoked or expired key. |
| 402 | billing_error | Insufficient credits, a key spending limit (the message says which and when it resets), or a model available only after your first credit purchase (the message names one you can use now). |
| 403 | permission_error | Account suspended. |
| 404 | not_found_error | Unknown or claude-* model; the message says which variable to set. |
| 408 | timeout_error | The model did not respond in time. Nothing was charged. |
| 413 | request_too_large | Body over 8 MB. |
| 429 | rate_limit_error | Key rate, concurrency or daily token limit, or the model is at capacity. Retry-After is set. |
| 500 | api_error | Something failed on our side. Nothing was charged. |
| 503 | overloaded_error | Every route for the model failed or is paused. Nothing was charged. |
After a stream has started, a failure arrives as an error event (for example overloaded_error when the model provider failed). You pay only for the output already sent.