HTTP API Reference
August 22, 2026 · View on GitHub
routatic-proxy exposes an Anthropic-compatible API. Claude Code connects to it as if it were the Anthropic API.
Endpoints
POST /v1/messages
The primary endpoint. Accepts Anthropic Messages API requests and returns responses in the same format.
Request body — standard Anthropic MessageRequest:
{
"model": "claude-sonnet-4-20250514",
"max_tokens": 4096,
"system": "You are a helpful assistant.",
"messages": [
{
"role": "user",
"content": "Hello, world!"
}
],
"stream": true,
"tools": []
}
Response — Anthropic MessageResponse (non-streaming) or SSE stream (streaming).
Routing behavior:
- If
modelmatches an entry inmodel_overrides(exact match), that model is used as primary with a scenario-derived safety net - Otherwise, if
modelcontains a family keyword configured inmodel_family_overrides(opus/sonnet/haiku, case-insensitive substring), that mapped model is used as primary with a scenario-derived safety net - Otherwise, scenario-based routing selects the model based on request content and token count
- Set
respect_requested_model: falsein config to force scenario routing regardless of themodelfield
Headers:
| Header | Value |
|---|---|
X-Request-ID | Unique request identifier (generated or forwarded from client) |
Content-Type | application/json (non-streaming) or text/event-stream (streaming) |
POST /v1/messages/count_tokens
Counts tokens for a message array without generating a response.
Request body:
{
"system": "System prompt text",
"messages": [
{ "role": "user", "content": "Hello" }
]
}
Response:
{
"input_tokens": 42
}
GET /v1/models
Returns the set of model identifiers a client may request, in the OpenAI
/v1/models envelope. Two consumers use it:
- CC-Switch — its "Fetch Models" button populates a model dropdown from this endpoint.
- Claude Code gateway model discovery — when enabled, Claude Code calls
GET /v1/models?limit=1000and adds the results to its/modelpicker (labeled "From gateway"). It reads thedisplay_namefield and only surfaces models whoseidbegins withclaudeoranthropic— all other ids are silently filtered from the picker (see Claude Code model picker).
The listing merges config models aliases, model_overrides keys, and — when
a catalog is available — catalog canonical names (provider/model). Any value
in the list is valid in the model field of POST /v1/messages.
Response:
{
"object": "list",
"data": [
{ "id": "default", "object": "model", "owned_by": "opencode-go" },
{ "id": "claude-sonnet-4-5-20250929", "object": "model", "owned_by": "opencode-zen" },
{ "id": "opencode-go/kimi-k2.6", "object": "model", "owned_by": "opencode-go", "name": "Kimi K2.6", "display_name": "Kimi K2.6" }
]
}
The limit query parameter (sent by Claude Code) is accepted and currently
ignored — the full list is always returned. Only GET is allowed; other
methods return 405.
GET /health
Returns server health status.
Response:
{
"status": "ok",
"version": "1.2.3",
"models_configured": 6,
"uptime": "2h30m"
}
GET /statusline
Returns compact status for TUI integration (statusline, tmux bar).
Response:
{
"status": "running",
"version": "1.2.3",
"uptime": "2h30m"
}
Analytics endpoints (SQLite only)
These three routes are registered only when SQLite storage is available — that
is, when the storage block is configured and the database opens successfully
(internal/server/server.go). Without storage they are absent and return 404.
All three accept an optional days query parameter (positive integer, default
30; invalid values fall back to 30).
| Route | Returns |
|---|---|
GET /api/analytics/summary | summary (token KPIs), models (per-model breakdown), providers (per-provider breakdown), generated_at |
GET /api/analytics/tokens/trend | days plus trend — daily token totals |
GET /api/analytics/latency | days plus stats — per-model latency statistics |
Example:
curl 'http://127.0.0.1:3456/api/analytics/summary?days=7'
Error Responses
Errors follow Anthropic's error format:
{
"type": "error",
"error": {
"type": "api_error",
"message": "description of what went wrong"
}
}
HTTP status codes:
| Code | Meaning |
|---|---|
| 400 | Invalid request body |
| 405 | Method not allowed (non-POST on /v1/messages) |
| 413 | Request body too large (>100MB) |
| 429 | Rate limited |
| 500 | Internal error (routing failed, transform error) |
| 502 | All upstream models failed |
Streaming
Streaming responses use Server-Sent Events (SSE) with Anthropic's event format:
event: message_start
data: {"type":"message_start","message":{"id":"msg_...","type":"message","role":"assistant","content":[],"model":"...","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":42,"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":"Hello"}}
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":15}}
event: message_stop
data: {"type":"message_stop"}
Heartbeat: keepalive comments (:keepalive\n\n) are sent every 3 seconds during streaming.
Rate Limiting
The proxy applies per-IP rate limiting (default: 100 requests/minute). Rate-limited requests receive HTTP 429.