SaveRouter API
SaveRouter - a unified, Anthropic-compatible API to top Claude models. Strictly pay-as-you-go by tokens, instant start, no minimums and no fixed plans. Compatible with the Anthropic Messages API - works with any client: Anthropic SDK, OpenClaw, curl, any HTTP.
Overview
SaveRouter gives you a unified, Anthropic-compatible API to top Claude models. You pay only for the tokens you use - no minimums, no fixed plans. Compatibility with the Anthropic Messages API means zero migration: any existing Anthropic SDK code works after changing the base_url.
Single endpoint
One URL and one format for every Claude model. No per-model plumbing.
Anthropic compatible
The same Messages API. Anthropic SDK, curl, any HTTP client work without code changes.
Pay per token
You pay strictly for the tokens you consume. No minimums, no fixed plans - instant start.
High availability
Built for stable operation under load with fast response times.
Quickstart
Three ways to connect in a minute. Base URL is https://api.saverouter.com, path is /v1/messages, header is x-api-key.
Direct API (curl)
A minimal HTTP request - nothing but curl:
# request curl https://api.saverouter.com/v1/messages \ -H "x-api-key: sk-sr-v1-YOUR_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-opus-4-8", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hi!"}] }'
Anthropic SDK
The official Anthropic SDK (Python / Node) - only base_url changes:
from anthropic import Anthropic client = Anthropic( api_key="sk-sr-v1-YOUR_KEY", base_url="https://api.saverouter.com", ) msg = client.messages.create( model="claude-opus-4-8", max_tokens=1024, messages=[{"role": "user", "content": "Hi!"}], ) print(msg.content[0].text)
import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic({ apiKey: 'sk-sr-v1-YOUR_KEY', baseURL: 'https://api.saverouter.com', }); const msg = await client.messages.create({ model: 'claude-opus-4-8', max_tokens: 1024, messages: [{ role: 'user', content: 'Hi!' }], }); console.log(msg.content[0].text);
OpenClaw agent
Connect via a provider in OpenClaw (see the “OpenClaw agent” section):
// ~/.openclaw/openclaw.json (JSON5 — comments allowed) { "models": { "providers": { "saverouter": { "baseUrl": "https://api.saverouter.com", "api": "anthropic-messages", "apiKey": "sk-sr-v1-YOUR_KEY", "authHeader": false, "headers": { "x-api-key": "sk-sr-v1-YOUR_KEY" }, "models": [ { "id": "claude-opus-4-8", "name": "Opus 4.8" }, { "id": "claude-sonnet-4-6", "name": "Sonnet 4.6" } ] } } }, "agents": { "defaults": { "model": { "primary": "saverouter/claude-opus-4-8" } } } }
Authentication
Requests are authenticated with the x-api-key header carrying your key in the sk-sr-v1-... format. Keys are issued in your dashboard (Telegram login).
- Key header:
x-api-key: sk-sr-v1-xxxxx - API version:
anthropic-version: 2023-06-01 - Content type:
content-type: application/json
Authorization: Bearer - this is the most common integration mistake (returns 403). Tip: keep the key in an environment variable, never commit it.Endpoint
All requests use a single method:
POST https://api.saverouter.com/v1/messages
The request body is the standard Anthropic Messages API format. The response is JSON in the same format (see “Response format”).
Request parameters
Body fields for /v1/messages (Anthropic Messages API format):
claude-opus-4-8.{role, content} objects, roles user/assistant.true, the response is streamed in chunks via SSE.Response format
A successful response is JSON in the Anthropic Messages format. The model text is in content[].text:
{
"id": "msg_01ABC...",
"type": "message",
"role": "assistant",
"model": "claude-opus-4-8",
"content": [
{ "type": "text", "text": "Hi! How can I help?" }
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": { "input_tokens": 12, "output_tokens": 8 }
}input_tokens / output_tokens) is what billing is based on. The sum of request and response tokens is charged against your balance at the model rate. stop_reason values: end_turn, max_tokens, stop_sequence, tool_use.Streaming responses
Streaming (SSE) is supported, just like the Anthropic API. Add "stream": true to the body - the response arrives as a stream of text/event-stream events: message_start, a series of content_block_delta, then message_stop. The SDKs provide a convenient client.messages.stream(...) helper.
curl https://api.saverouter.com/v1/messages \ -H "x-api-key: sk-sr-v1-YOUR_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-opus-4-8", "max_tokens": 1024, "stream": true, "messages": [{"role": "user", "content": "Write a haiku"}] }'
from anthropic import Anthropic client = Anthropic( api_key="sk-sr-v1-YOUR_KEY", base_url="https://api.saverouter.com", ) with client.messages.stream( model="claude-opus-4-8", max_tokens=1024, messages=[{"role": "user", "content": "Hi!"}], ) as stream: for text in stream.text_stream: print(text, end="", flush=True)
Working with images (vision)
The Opus and Sonnet models accept images. Add a type:"image" block to the message content with a base64 source alongside a text block. You can mix text and images in a single message.
{
"model": "claude-opus-4-8",
"max_tokens": 1024,
"messages": [{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "/9j/4AAQSkZJRg..."
}
},
{ "type": "text", "text": "What is in this image?" }
]
}]
}source.data, with the type in media_type.Tool calling
★Function calling - works
Pass a tools array describing your functions. If the model decides to call a tool, it returns a type:"tool_use" block with name and input, and stop_reason becomes tool_use. Your client executes the tool and sends the result back as a type:"tool_result" block.
{
"model": "claude-opus-4-8",
"max_tokens": 1024,
"tools": [{
"name": "get_weather",
"description": "Current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}],
"messages": [
{ "role": "user", "content": "Weather in Dubai?" }
]
}{
"role": "assistant",
"stop_reason": "tool_use",
"content": [
{
"type": "tool_use",
"id": "toolu_01ABC...",
"name": "get_weather",
"input": { "city": "Dubai" }
}
]
}tool_use, append the assistant message and a new user message with a tool_result block (same tool_use_id) - then repeat the request.tool_choice:{type:"tool"}) is incompatible with thinking and not recommended for agentic tasks. For strict JSON, use tool_choice:{type:"auto"} together with thinking - the model will still call your tool and return a valid tool_use. Non-reasoning modes (forced-tool without thinking, especially on large prompts) are not guaranteed and may hit short-window limits.Limits & reliability
The service is built for high availability. On a transient error (429 or 5xx) an automatic retry with exponential backoff is recommended on the client side. The official Anthropic SDKs do this out of the box; for a custom HTTP client, a simple example is below.
import time, httpx def call_with_retry(payload, tries=5): for i in range(tries): r = httpx.post("https://api.saverouter.com/v1/messages", headers={"x-api-key": KEY, "anthropic-version": "2023-06-01"}, json=payload, timeout=60) if r.status_code not in (429, 500, 502, 503): return r time.sleep(2 ** i) # 1, 2, 4, 8, 16s return r
Error codes
x-api-key header.Authorization: Bearer instead of x-api-key.Pricing
Strictly per-token pricing (USD per 1M tokens):
Connecting an OpenClaw agent
★Most important for agents
The easiest path is the OpenClaw CLI (it writes the provider into ~/.openclaw/openclaw.json for you):
# 1) add provider + key (edits openclaw.json for you) openclaw models auth login --provider saverouter \ --base-url https://api.saverouter.com \ --api anthropic-messages \ --api-key sk-sr-v1-YOUR_KEY # 2) set it as the primary model openclaw models set saverouter/claude-opus-4-8 # 3) verify the model is listed openclaw models list
Or edit the config manually
Add the provider to ~/.openclaw/openclaw.json (the models.providers section) and set the primary model under agents.defaults.model:
// ~/.openclaw/openclaw.json (JSON5 — comments allowed) { "models": { "providers": { "saverouter": { "baseUrl": "https://api.saverouter.com", "api": "anthropic-messages", "apiKey": "sk-sr-v1-YOUR_KEY", "authHeader": false, "headers": { "x-api-key": "sk-sr-v1-YOUR_KEY" }, "models": [ { "id": "claude-opus-4-8", "name": "Opus 4.8" }, { "id": "claude-sonnet-4-6", "name": "Sonnet 4.6" } ] } } }, "agents": { "defaults": { "model": { "primary": "saverouter/claude-opus-4-8" } } } }
Then in openclaw.json set the primary model to saverouter/claude-opus-4-8.
headers: { x-api-key } with authHeader: false - otherwise the gateway sends Authorization: Bearer and gets a 403.~/.openclaw/openclaw.json → models.providers, not the generated agents/main/agent/models.json; (2) an allowlist agents.defaults.models is set - add saverouter/claude-opus-4-8 to it; (3) the gateway wasn't restarted. Check with: openclaw models list.Connecting agents
SaveRouter is an Anthropic-compatible endpoint, so it plugs into any agent that can talk to the Anthropic API: just switch the baseURL to https://api.saverouter.com and pass your key in the x-api-key: sk-sr-v1-... header. Below are examples for four popular coding agents.
SaveRouter works with any tool that speaks the Anthropic Messages API. Verified clients: Cursor, Cline, Roo Code, Kilo Code, Aider, Zed, Continue, OpenCode, Crush, Droid, Pi, Hermes, OpenClaw, plus the official Anthropic SDKs (Python / TypeScript), LangChain, LlamaIndex and any HTTP client. The principle is always the same: baseURL https://api.saverouter.com + the x-api-key header.
OCPlatform
Add a provider to models.json, then set the primary model to saverouter/claude-opus-4-8 in openclaw.json. The key is passed explicitly via headers: { x-api-key } with authHeader: false (see the “OpenClaw agent” section).
// ~/.openclaw/openclaw.json (JSON5 — comments allowed) { "models": { "providers": { "saverouter": { "baseUrl": "https://api.saverouter.com", "api": "anthropic-messages", "apiKey": "sk-sr-v1-YOUR_KEY", "authHeader": false, "headers": { "x-api-key": "sk-sr-v1-YOUR_KEY" }, "models": [ { "id": "claude-opus-4-8", "name": "Opus 4.8" }, { "id": "claude-sonnet-4-6", "name": "Sonnet 4.6" } ] } } }, "agents": { "defaults": { "model": { "primary": "saverouter/claude-opus-4-8" } } } }
Pi
Pi and Claude-compatible clients pick up the endpoint from environment variables. Since SaveRouter mirrors the Anthropic API, any client that reads ANTHROPIC_BASE_URL + x-api-key works with no code changes:
export ANTHROPIC_BASE_URL="https://api.saverouter.com" export ANTHROPIC_API_KEY="sk-sr-v1-YOUR_KEY" # Pi / Claude-compatible clients pick up the endpoint automatically
Hermes
In the Hermes provider settings point the Anthropic-compatible baseURL and the x-api-key header at SaveRouter. The exact field names depend on your Hermes version - the essence is an Anthropic-compatible baseURL + key:
provider: type: anthropic base_url: https://api.saverouter.com api_key: sk-sr-v1-YOUR_KEY # sent as the x-api-key header model: claude-opus-4-8
Kilo Code
In Kilo Code (VS Code) settings pick the “Anthropic” provider (or “Anthropic-compatible / Custom”), set Base URL https://api.saverouter.com, API Key sk-sr-v1-... and model claude-opus-4-8. Example settings.json fragment (setting names may differ by version):
{
"kilocode.apiProvider": "anthropic",
"kilocode.anthropicBaseUrl": "https://api.saverouter.com",
"kilocode.apiKey": "sk-sr-v1-YOUR_KEY",
"kilocode.model": "claude-opus-4-8"
}https://api.saverouter.com, the x-api-key header with an sk-sr-v1-... key, and a model name (e.g. claude-opus-4-8).FAQ
base_url = https://api.saverouter.com. No code changes needed.sk-sr-v1-... and are passed in the x-api-key header.HTTP 402. Top up your balance and access is restored immediately.tools, images via a type:"image" block.