Anthropic-compatible API
The Messages API against the Asale gateway, with the official Anthropic SDKs. Change the base URL and the key; your request bodies stay as they are.
The Anthropic dialect is what Claude Code and every anthropic SDK speak. Point the client's base URL at the gateway and the rest of your code is untouched.
Read the overview first for keys, models, billing and errors; everything here is the Anthropic-specific part.
Connection
| Base URL | https://gw.asale.ai |
| Auth header | x-api-key: sk-asale-... |
| SDK env var | ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL |
The base URL stops at the host. Anthropic's clients append /v1/messages themselves, so giving them https://gw.asale.ai/v1 produces /v1/v1/messages and a 404.
Authorization: Bearer is accepted here too, for clients that only know that header.
Endpoints
| Method | Path | Notes |
|---|---|---|
| POST | /v1/messages | The main one. |
| POST | /v1/messages/count_tokens | An estimate, not a vendor tokenizer count — see below. |
Send anthropic-version: 2023-06-01 as you would upstream. The SDKs do it for you.
Call it
curl https://gw.asale.ai/v1/messages \
-H "x-api-key: $ASALE_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello"}]
}'
# pip install anthropic
import os
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["ASALE_API_KEY"],
base_url="https://gw.asale.ai",
)
msg = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(msg.content[0].text)
// npm i @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: process.env.ASALE_API_KEY,
baseURL: "https://gw.asale.ai",
});
const msg = await client.messages.create({
model: "claude-sonnet-4-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
});
console.log(msg.content[0].text);
Streaming
stream: true returns the Anthropic event sequence — message_start, content_block_delta, message_delta, message_stop — as server-sent events, unchanged.
with client.messages.stream(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Count to five"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
System prompts, tools and thinking
systempasses through, including a cache breakpoint at the end of the prompt — the one every agent client sets, and the one worth the most.tools/tool_use/tool_resultpass through in their native shape.thinkingis supported where the model supports it. A thinking block'ssignatureis only handed back when the request was served over the same wire format; a publisher serving it through another vendor produces no signature rather than a forged one.
Counting tokens
POST /v1/messages/count_tokens answers with a heuristic estimate, weighted by character class (roughly 4 characters per token for Latin text, ~1.5 for CJK) plus per-message framing:
{ "input_tokens": 1234 }
It is there so a client that refuses to send without a count can send. Do not budget against it to the token — an exact figure needs the vendor's own tokenizer, which the gateway does not run. What you are billed on is measured usage, not this number.
Practical notes
max_tokensis required by the Messages API, and it is also what the balance hold is computed from. A large value on a small reply costs nothing extra — the difference is returned when the response finishes.- Model names are the platform's.
GET /v1/modelson the OpenAI path lists them; the market shows which have supply. - Request bodies are capped at 16 MiB.