anoman
POST · /v1/chat/completions

Chat completions.

Full OpenAI-compatible chat endpoint. Streaming, vision, tool calling, batching, and Anoman's response annotations all on a single route.

Quick example

Make your first request

from openai import OpenAI

client = OpenAI(base_url="https://api.anoman.io/v1", api_key="anm-sk-...")

response = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "What is the capital of Indonesia?"},
    ],
    temperature=0.7,
    max_tokens=200,
)
print(response.choices[0].message.content)

Identical request shape to OpenAI’s /v1/chat/completions. The only difference is the base URL.

Request body

Parameters

FieldTypeReq?Description
modelstringModel slug from our catalog. See GET /v1/models.
messagesarrayConversation history. Each item has role (system / user / assistant / tool) and content (string or array for vision).
max_tokensintegerCap on completion tokens. Default = model’s context budget.
temperaturenumberSampling temperature (0–2). Default 1.0. Set 0 for deterministic-ish output.
top_pnumberNucleus sampling (0–1). Use with or instead of temperature.
streambooleanStream tokens via SSE. See streaming guide.
toolsarrayFunction definitions the model may call. Same shape as OpenAI’s.
tool_choicestring / object"auto" (default), "none", or a specific function.
response_formatobject{"type": "json_object"} for JSON mode.
seedintegerDeterministic sampling seed. Not all upstream providers honor it.
stopstring / arrayUp to 4 stop sequences.
presence_penaltynumber−2 to 2. Encourages new topics.
frequency_penaltynumber−2 to 2. Discourages repetition.
userstringOpaque end-user identifier — surfaces in traces for per-user attribution.
metadataobjectFree-form key/value tags (max 16 keys) attached to the trace.

Anoman request headers

Optional headers

HeaderValuesEffect
x-anoman-realtime1 / trueForce realtime even if customer’s key prefers batch.
x-anoman-prefer-batch1 / trueOpt this request into batch routing (cheaper, longer SLA).
x-anoman-no-cache1 / trueBypass semantic cache for this request (still bills full cost).
anoman-session-idstringGroup requests into an agent session — surfaces in dashboard Sessions view.
anoman-agent-idstringIdentifier for the agent making the call (e.g. support-bot-v2).

200 response

Response body

Identical to OpenAI’s response shape, plus an _anoman extension with our guardrail results, routing decisions, cache status, and cost accounting.

{
  "id": "chatcmpl-9k4LpQ8mZx7TbF2Vn",
  "object": "chat.completion",
  "created": 1740000123,
  "model": "claude-sonnet-4-6",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The capital of Indonesia is Jakarta."
      },
      "finish_reason": "stop",
      "logprobs": null
    }
  ],
  "usage": {
    "prompt_tokens": 24,
    "completion_tokens": 9,
    "total_tokens": 33,
    "prompt_tokens_details": {
      "cached_tokens": 0
    }
  },
  "_anoman": {
    "guardrails": {
      "injection":          { "status": "pass", "score": 0.02 },
      "pii":                { "status": "pass" },
      "content":            { "status": "pass" },
      "policy":             { "status": "pass" },
      "response_content":   { "status": "pass" }
    },
    "routing": {
      "mode":             "realtime",
      "region":           "id",
      "provider_type":    "cloud_direct",
      "provider_region":  "ID"
    },
    "cache": {
      "hit":  false,
      "type": "none"
    },
    "weighted_tokens":   1240,
    "cost_usd":          "0.000041",
    "burst":             { "active": false },
    "billing":           null
  }
}

Want to use a strict OpenAI SDK that rejects unknown fields? Set request header x-anoman-strip-extension: 1 and the _anoman block is removed from the body (data still reaches headers + traces).

Response headers

What every 200 carries

HeaderExampleDescription
x-anoman-regionIDRegion that processed the request.
x-anoman-cachenone / provider / semanticCache hit type.
x-anoman-guardrail-injectionpass score=0.02Prompt injection scan result.
x-anoman-guardrail-piipass redacted 2 entitiesPII detector result.
x-anoman-rate-limit-rpm120RPM burst-guard cap for your tier.
x-anoman-rate-remaining-rpm86Requests left in the current minute.
x-anoman-burst-activetrue / (absent)Whether a burst credit is currently multiplying your limits.

See rate limits for the full list of rate headers.

Vision

Image input

Wrap user content as an array with type: text and type: image_url entries. The URL can be public or a base64-encoded data URL. Only vision-capable models accept image input — see the residency badge + /models Modality filter to find them.

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What's in this chart?"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://example.com/chart.png",
                        # Or pass a data URL:
                        # "url": "data:image/png;base64,iVBORw0..."
                    },
                },
            ],
        },
    ],
)
Data URLs. Max 5 MB per image. PNG, JPEG, and WebP accepted. Animated GIFs are downsampled to a single frame.

Tool calling

Functions / tools

Pass tool definitions and the model may emit a tool_calls message instead of free text. Anoman’s policy enforcement can deny specific tool names per API key — see the policies page in the dashboard.

response = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "What's the weather in Jakarta?"}],
    tools=[
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "Get current weather for a city",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city":  {"type": "string"},
                        "units": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                    },
                    "required": ["city"],
                },
            },
        },
    ],
    tool_choice="auto",  # or {"type": "function", "function": {"name": "get_weather"}}
)
# The model decides whether to call the tool. If so:
tool_call = response.choices[0].message.tool_calls[0]
print(tool_call.function.name, tool_call.function.arguments)
# get_weather  {"city": "Jakarta", "units": "celsius"}

Batch routing

Opt into batch (cheaper, longer SLA)

Set x-anoman-prefer-batch: true and the same endpoint returns a 202 with a poll_url. Batch jobs cost ~50% less for non-interactive workloads (data analysis, overnight extraction, eval runs).

# Same endpoint — opt in via header. 202 means queued; poll the
# returned poll_url to retrieve when complete.
curl https://api.anoman.io/v1/chat/completions \
  -H "Authorization: Bearer anm-sk-..." \
  -H "Content-Type: application/json" \
  -H "x-anoman-prefer-batch: true" \
  -d '{
    "model": "deepseek-v3",
    "messages": [{"role": "user", "content": "Summarize this 50-page doc..."}]
  }'

Full batch lifecycle in /v1/batch endpoint reference.

Streaming

Set stream:true

stream = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Tell me a short story."}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)

Full wire format + mid-stream error handling in the streaming guide.

Errors

Possible non-200 responses

  • 400model_not_found, context_length_exceeded, vision_not_supported
  • 401/403 — auth failures (see authentication) or guardrail blocks (prompt_injection, tool_denied, content_violation)
  • 402budget_exceeded
  • 429 — rate-limit or inflight cap (see rate limits)
  • 503/504 — provider issues (retryable)

Full reference at /docs/errors.

Try it from the playground.

3-pane compare with live streaming output.