Wallaby.
( wallaby — api documentation )

API Docs

OpenAI-compatible · Kimi K3 live · prepaid USD, metered per token

Wallaby serves open-weight models behind a standard OpenAI-compatible endpoint. If your code already talks to the OpenAI API, you change two lines — the base_url and the key — and it works here.

POST https://api.wallabytoken.com/v1/chat/completions

Quickstart

  • Registercreate an account with an email. No card until you top up.
  • Get a key — in the console: Tokens → Add token. Keys start with sk-.
  • Top up — prepaid USD, from $20. Balance never expires.
  • Call — point any OpenAI-compatible client at https://api.wallabytoken.com/v1 with your key.

Authentication

Every request carries your API key in the Authorization header:

Authorization: Bearer sk-your-key
Content-Type: application/json
Keep keys out of frontend code and public repos. A leaked key spends your balance — revoke it in the console and issue a new one.

First call (curl)

curl https://api.wallabytoken.com/v1/chat/completions \
  -H "Authorization: Bearer $WALLABY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kimi-k3",
    "max_tokens": 256,
    "messages": [
      {"role": "user", "content": "Give me three names for a pet wallaby."}
    ]
  }'

Python SDK

Use the official openai package (pip install openai) with a custom base URL.

Non-streaming

from openai import OpenAI

client = OpenAI(
    base_url="https://api.wallabytoken.com/v1",
    api_key="sk-your-key",
)

resp = client.chat.completions.create(
    model="kimi-k3",
    max_tokens=256,
    messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)

Streaming

stream = client.chat.completions.create(
    model="kimi-k3",
    max_tokens=512,
    messages=[{"role": "user", "content": "Explain token caching in two sentences."}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta
    if delta and delta.content:
        print(delta.content, end="", flush=True)

Reading the reasoning stream

Kimi K3 is a reasoning model: it thinks before it answers. The thinking arrives in a separate field, reasoning_content, alongside content — in both streaming deltas and the final non-streaming message. Render it collapsed, stream it live, or drop it; the answer is always in content.

for chunk in stream:
    delta = chunk.choices[0].delta
    if not delta:
        continue
    if getattr(delta, "reasoning_content", None):
        # thinking — show it dimmed, or hide it
        print(delta.reasoning_content, end="")
    if delta.content:
        # the actual answer
        print(delta.content, end="", flush=True)
Billing note: thinking is billed as output tokens — same as the official Kimi API. Every Wallaby receipt itemises input, cached input, output and thinking separately, so nothing hides in a lump sum.

Tool use (function calling)

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

resp = client.chat.completions.create(
    model="kimi-k3",
    max_tokens=256,
    messages=[{"role": "user", "content": "What's the weather in Sydney?"}],
    tools=tools,
)
msg = resp.choices[0].message
if msg.tool_calls:
    call = msg.tool_calls[0]
    print(call.function.name, call.function.arguments)
    # → get_weather {"city": "Sydney"}

Append the tool result as a {"role": "tool", "tool_call_id": ..., "content": ...} message and call again to get the final answer.

Structured outputs

Both JSON modes work. json_object guarantees valid JSON; json_schema with strict: true pins the shape.

resp = client.chat.completions.create(
    model="kimi-k3",
    max_tokens=256,
    messages=[{"role": "user",
               "content": "List 3 planets with diameter in km and whether they have rings."}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "planet_list",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "planets": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "name": {"type": "string"},
                                "diameter_km": {"type": "number"},
                                "has_rings": {"type": "boolean"},
                            },
                            "required": ["name", "diameter_km", "has_rings"],
                            "additionalProperties": False,
                        },
                    }
                },
                "required": ["planets"],
                "additionalProperties": False,
            },
        },
    },
)
print(resp.choices[0].message.content)  # guaranteed-schema JSON

Models

ModelContextNotes
kimi-k31M tokensOpen-weight reasoning model. Thinking billed as output. Pricing: price list · machine-readable pricing.json

The live model list is also queryable:

curl https://api.wallabytoken.com/v1/models \
  -H "Authorization: Bearer $WALLABY_API_KEY"

More open-weight models land as we verify them. DeepSeek V4 Flash / Pro are temporarily unavailable while we reprice against the new official list.

Supported parameters

ParameterStatusBehaviour
model✓ requiredkimi-k3
messages✓ requiredStandard chat roles: system / user / assistant / tool
streamSSE. Deltas carry reasoning_content then content; final chunk includes usage
max_tokens / max_completion_tokensBoth aliases accepted. Caps generated tokens; thinking counts toward output
stopString or array of stop sequences
seed✓ acceptedPassed through; best-effort determinism, not guaranteed
tools / tool_choiceFunction calling — see tool use
response_formatjson_object and json_schema (strict) — see structured outputs
temperaturenormalisedAny value accepted. Requests are served at 1 (upstream model constraint); the value is normalised automatically
top_pnormalisedAny value accepted; served at 0.95 (upstream constraint)
presence_penalty / frequency_penaltynormalisedAny value accepted; served at 0 (upstream constraint)
nnormalisedSingle completion only; values > 1 are normalised to 1
Why "normalised"? The model's serving stack fixes its sampling parameters. Rather than rejecting your request with a 400, the gateway rewrites those values to the supported ones — client code from any SDK keeps working unchanged.

Errors

HTTPMeaningWhat to do
400Invalid request bodyCheck JSON shape against this page; the message field names the offending parameter
401Invalid token — key missing, wrong, or revokedVerify the Authorization: Bearer header; issue a new key in the console
403Key has no access to that model, or balance is exhausted (insufficient_user_quota)Check the key's model scope; top up in the console
429Rate limited, or upstream briefly saturatedBack off with jitter and retry; persistent 429s — email us
5xxGateway or upstream faultRetry with exponential backoff; check status.wallabytoken.com for live incidents

Errors follow the OpenAI shape: {"error": {"message": "...", "type": "...", "code": ...}}, with a request id in the message — quote it when contacting support.

Balance & usage

  • Balance — console → wallet panel; settles in seconds after top-up.
  • Per-request spend — console → logs: every call is itemised (input / cached input / output / thinking).
  • Statements — console → billing; itemised receipts for every top-up.

Usage-reporting API endpoints are on the roadmap. Today the console is the source of truth.

House rules