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
| Field | Type | Req? | Description |
|---|---|---|---|
| model | string | ✓ | Model slug from our catalog. See GET /v1/models. |
| messages | array | ✓ | Conversation history. Each item has role (system / user / assistant / tool) and content (string or array for vision). |
| max_tokens | integer | — | Cap on completion tokens. Default = model’s context budget. |
| temperature | number | — | Sampling temperature (0–2). Default 1.0. Set 0 for deterministic-ish output. |
| top_p | number | — | Nucleus sampling (0–1). Use with or instead of temperature. |
| stream | boolean | — | Stream tokens via SSE. See streaming guide. |
| tools | array | — | Function definitions the model may call. Same shape as OpenAI’s. |
| tool_choice | string / object | — | "auto" (default), "none", or a specific function. |
| response_format | object | — | {"type": "json_object"} for JSON mode. |
| seed | integer | — | Deterministic sampling seed. Not all upstream providers honor it. |
| stop | string / array | — | Up to 4 stop sequences. |
| presence_penalty | number | — | −2 to 2. Encourages new topics. |
| frequency_penalty | number | — | −2 to 2. Discourages repetition. |
| user | string | — | Opaque end-user identifier — surfaces in traces for per-user attribution. |
| metadata | object | — | Free-form key/value tags (max 16 keys) attached to the trace. |
Anoman request headers
Optional headers
| Header | Values | Effect |
|---|---|---|
| x-anoman-realtime | 1 / true | Force realtime even if customer’s key prefers batch. |
| x-anoman-prefer-batch | 1 / true | Opt this request into batch routing (cheaper, longer SLA). |
| x-anoman-no-cache | 1 / true | Bypass semantic cache for this request (still bills full cost). |
| anoman-session-id | string | Group requests into an agent session — surfaces in dashboard Sessions view. |
| anoman-agent-id | string | Identifier 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
| Header | Example | Description |
|---|---|---|
| x-anoman-region | ID | Region that processed the request. |
| x-anoman-cache | none / provider / semantic | Cache hit type. |
| x-anoman-guardrail-injection | pass score=0.02 | Prompt injection scan result. |
| x-anoman-guardrail-pii | pass redacted 2 entities | PII detector result. |
| x-anoman-rate-limit-rpm | 120 | RPM burst-guard cap for your tier. |
| x-anoman-rate-remaining-rpm | 86 | Requests left in the current minute. |
| x-anoman-burst-active | true / (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..."
},
},
],
},
],
)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
- 400 —
model_not_found,context_length_exceeded,vision_not_supported - 401/403 — auth failures (see authentication) or guardrail blocks (
prompt_injection,tool_denied,content_violation) - 402 —
budget_exceeded - 429 — rate-limit or inflight cap (see rate limits)
- 503/504 — provider issues (retryable)
Full reference at /docs/errors.