Asale

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 URLhttps://gw.asale.ai
Auth headerx-api-key: sk-asale-...
SDK env varANTHROPIC_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

MethodPathNotes
POST/v1/messagesThe main one.
POST/v1/messages/count_tokensAn 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

  • system passes 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_result pass through in their native shape.
  • thinking is supported where the model supports it. A thinking block's signature is 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_tokens is 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/models on the OpenAI path lists them; the market shows which have supply.
  • Request bodies are capped at 16 MiB.