Zro

API reference

Endpoints, authentication, streaming, tool calling, reasoning, caching, and regions.

The API exposes three surfaces from the same host, authenticated with the same per-user API keys: Chat Completions, the Responses API, and the Messages API.

APIBase URLEndpoint
Chat Completionshttps://zro.moonmath.ai/v1POST /chat/completions
Responseshttps://zro.moonmath.ai/v1POST /responses
Messageshttps://zro.moonmath.aiPOST /v1/messages

Authentication

Create an API key from the API keys page and send it as a bearer token:

Authorization: Bearer sk-...

Keys are scoped to an invoice-able account, carry optional spend limits and expiry, and select which regions may process their requests. They can be created, revoked, and inspected at any time from the API keys page. See Key controls and Regions.

Endpoints

MethodPathDescription
GET/v1/modelsList the models available to the key.
POST/v1/chat/completionsChat Completions API.
POST/v1/responsesResponses API.
POST/v1/messagesMessages API.

Chat completions

from openai import OpenAI
 
client = OpenAI(base_url="https://zro.moonmath.ai/v1", api_key="sk-...")
 
response = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[
        {"role": "system", "content": "You are a helpful coding assistant."},
        {"role": "user", "content": "Explain what a KV cache is."},
    ],
    temperature=0.2,
)
print(response.choices[0].message.content)

Streaming

Set stream: true to receive server-sent events. All models stream, and usage is included in the final chunk.

stream = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[{"role": "user", "content": "Write a haiku about GPUs."}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)

Responses API

The Responses API is available at POST /v1/responses for clients built for it (for example Codex). It takes input instead of messages:

response = client.responses.create(
    model="deepseek-v4.1-flash",
    input="Explain what a KV cache is.",
)
print(response.output_text)
curl https://zro.moonmath.ai/v1/responses \
  -H "Authorization: Bearer $ZRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash",
    "input": "Explain what a KV cache is."
  }'

Messages API

Claude Code and other Messages clients point at the endpoint root (not /v1).

export ANTHROPIC_BASE_URL="https://zro.moonmath.ai"
export ANTHROPIC_AUTH_TOKEN="$ZRO_API_KEY"
export ANTHROPIC_MODEL="deepseek-v4.1-flash"
import anthropic
 
client = anthropic.Anthropic(
    base_url="https://zro.moonmath.ai",
    api_key="sk-...",
)
 
message = client.messages.create(
    model="deepseek-v4.1-flash",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Summarize this diff."}],
)

Tool calling

The standard tools / tool_choice interface and the full tool-call loop are supported. This is what lets coding agents run shell commands, edit files, and call MCP tools.

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]
 
response = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[{"role": "user", "content": "Weather in Lisbon, Portugal?"}],
    tools=tools,
)
print(response.choices[0].message.tool_calls)

Reasoning

Reasoning models accept an effort control, shaped per API:

APIField
Chat Completionsreasoning_effort, reasoning, or thinking
Responsesreasoning
Messagesthinking, or output_config.effort

On Chat Completions, when more than one is sent, precedence is reasoning_effort > reasoning > thinking.

response = client.chat.completions.create(
    model="glm-5.3",
    messages=[{"role": "user", "content": "Prove that sqrt(2) is irrational."}],
    reasoning_effort="max",
)
response = client.responses.create(
    model="glm-5.3",
    input="Prove that sqrt(2) is irrational.",
    reasoning={"effort": "max"},
)

The reasoning object accepts {"effort": "<level>"} or the boolean toggle {"enabled": true|false}. The supported levels differ per model. See the reasoning chips on the Models page. Unrecognized reasoning fields are ignored rather than rejected.

Errors

Errors follow the standard error shape ({ "error": { "message", "type", ... } }).

StatusMeaning
400Malformed request or unsupported parameter value.
401Missing, invalid, revoked, or expired API key.
403Key is not permitted to use the requested model.
402Spend quota or plan budget exhausted.
404Unknown model ID.
429Rate limit exceeded. Retry with backoff.
5xxUpstream/model error. Retry with backoff; if it persists, contact support.

Compatibility notes

  • Standard request fields (temperature, top_p, max_tokens, frequency_penalty, presence_penalty, stop, seed) are accepted.
  • Unsupported provider parameters are dropped instead of erroring. Clients built for other providers keep working.
  • Both the bare model ID and its zro/ alias are accepted on every endpoint.

On this page