Chat Completions
Chat Completions API — route to 10,000+ LLMs via /v1/chat/completions with 12ms median overhead, intelligent routing, and semantic caching.
Create completions with OpenAI-compatible request/response shapes plus optional Xantly orchestration controls.
- POST
/v1/chat/completions - HEAD
/v1/chat/completions(returns204 No Content; useful as a lightweight probe) - Auth:
Authorization: Bearer <token>
Recommended rollout: start with standard fields only (
model,messages,temperature,max_tokens), then add orchestration controls incrementally.
Quick start example
curl -sS https://api.xantly.com/v1/chat/completions \
-H "Authorization: Bearer $XANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "auto",
"messages": [
{"role": "user", "content": "Explain vector databases in 2 bullets."}
]
}'
Request body
Standard parameters (OpenAI-compatible)
| Field | Type | Required | Default | Validation / behavior |
|---|---|---|---|---|
model | string | Yes | , | A routing alias (auto, xantly/auto-speed, xantly/auto-value, xantly/auto-quality, xantly/auto-safety) or a catalog model id from GET /v1/models. See Choosing how the router picks. Unknown ids are rejected. |
messages | array<ChatMessage> | Yes | , | Must contain at least 1 message. |
models | array<string> | No | null | Ordered fallback chain, OpenRouter semantics. If the model in model cannot serve the request, the gateway tries these in order before its own routing. Each entry is a catalog slug. |
stream | boolean | No | false | Enables SSE output. |
n | integer | No | 1 | Allowed range: 1..=8. |
max_tokens | integer | No | provider/model dependent | Alias supported: max_completion_tokens. |
temperature | number | No | provider/model dependent | Validated range: 0.0..=2.0. |
top_p | number | No | provider/model dependent | Passed through to provider. |
frequency_penalty | number | No | provider/model dependent | Validated range: -2.0..=2.0. |
presence_penalty | number | No | provider/model dependent | Validated range: -2.0..=2.0. |
stop | string | array<string> | No | null | Passed through to provider. |
tools | array<object> | No | null | Tool definitions for function-calling workflows. |
tool_choice | string | object | No | provider dependent | Passed through to provider. |
parallel_tool_calls | boolean | No | provider dependent | Passed through to provider. |
response_format | object | No | null | Supports {"type":"json_object"} and {"type":"json_schema", "json_schema": {...}}. |
seed | integer (u64) | No | null | Determinism hint. |
user | string | No | null | End-user identifier, forwarded to provider. |
logprobs | boolean | No | null | Forwarded to provider. |
top_logprobs | integer (u8) | No | null | Validated max: 20. |
stream_options.include_usage | boolean | No | false | Forwarded to provider where supported. |
reasoning_effort | string | No | null | Accepted values: minimal, low, medium, high. Maps to each provider's native reasoning mechanism and selects the routing tier/price (low→cheap, high→premium). It outranks an xantly/auto-* alias: the most specific thing you said wins. The effort actually applied comes back in x-xantly-reasoning-effort. See Reasoning & Thinking. |
reasoning | object | No | null | Unified reasoning controls: { effort, max_tokens, summary, exclude, enabled }. Returns the model's reasoning text. See Reasoning & Thinking. |
service_tier | string | No | null | "batch" is accepted and treated as a cost-oriented routing hint. |
metadata | object<string,string> | No | null | Free-form metadata map used for tracing/routing context. |
ChatMessage object
| Field | Type | Required | Notes |
|---|---|---|---|
role | string | Yes | Common values: system, user, assistant, tool. |
content | string | array | Yes | String for text, array for multimodal content parts. |
name | string | No | Optional participant name. |
tool_calls | array<object> | No | Assistant tool call payloads. |
tool_call_id | string | No | Correlates tool output to a prior tool call. |
refusal | string | No | Optional refusal text. |
Proprietary parameters (Xantly)
These are optional. If omitted, the gateway uses defaults and automatic policy.
routing_hints (soft preferences)
| Field | Type | Value range / values | Backend status | Usage guidance |
|---|---|---|---|---|
mode | string | fast, balanced, quality, cost_optimized, free_models_only | Active | Coarse routing preset when explicit preference knobs are not set. |
preference_dial | number | Best used in 0.0..1.0 (values are clamped) | Active | Lower biases cost/speed; higher biases quality. |
prefer_latency | boolean | true/false | Active | true strongly biases low-latency execution. |
prefer_quality | boolean | true/false | Partial | Accepted; currently primarily suppresses mode preset behavior when explicit knobs are present. |
max_cost_per_token | number | -- | Rejected | Returns 400. It was never enforced, and accepting a cost ceiling that does nothing is worse than refusing it. Use preference_dial, mode: "cost_optimized", or an organization spend limit instead. |
max_latency_ms | integer | positive integer | Active | Sets latency budget; very low budgets bias faster lanes. |
max_tier | integer | 1, 2, 3 | Active | Tier guardrail hint. |
required_capabilities | array<string> | tools, tool_use, vision, json, structured_output | Active | Enforced as a hard pre-routing filter. Any other member returns 400 rather than being silently ignored. |
task_complexity | string | trivial, standard, complex, expert | Active | Influences tier floor/ceiling behavior. |
chain_routing | string | sticky, mixed | Active | mixed disables sticky-route continuation behavior. |
allow_free_fallback | boolean | true/false | Active | Passed into metadata as an explicit fallback preference signal. |
routing_override (harder overrides)
| Field | Type | Values | Backend status | Usage guidance |
|---|---|---|---|---|
force_tier | string | T1, T2, T3 (also tier-1, tier1, etc.) | Active | Forces selected tier mapping. |
force_lane | string | smart, turbo | Active | Forces lane. |
force_model | string | model slug/upstream id | Active | Pins model after routing. |
force_provider | string | provider identifier | Reserved | Accepted in schema; not directly applied in this chat handler flow today. |
allow_fallbacks | boolean | true / false | Active | false makes this request fail on its first pick instead of substituting another model. OpenRouter allow_fallbacks semantics. Default true. |
max_retries | integer | 0..=5 | Active | Transport retries of the primary model only: 429, 5xx, timeouts, network. Never applied to an answer the model chose to give. Default is one bounded retry. A request may tighten the organization's setting, never loosen it. |
max_fallbacks | integer | 0..=5 | Active | How many model substitutions the gateway may make. 0 is the same as allow_fallbacks: false. Defaults per request shape: 3 plain chat, 5 JSON, 6 tools, 2 voice. A request may tighten the organization's setting, never loosen it. |
timeout_ms | integer | 1..=900000 | Active | Wall-clock budget for the whole attempt chain, and the ceiling for a single attempt within it. The caller decides: this raises as well as lowers the gateway's own default, up to a 15 minute hard cap. Set it when you ask a reasoning model for a long answer. An organization-level timeout still applies as an administrator's ceiling. |
xantly orchestration block
| Field | Type | Values / range | Effective default | Backend status | Usage guidance |
|---|---|---|---|---|---|
quality_tolerance | number | 0.0 to 0.20 | 0.03, or your organization's setting | Active | How much measured accuracy you will trade for a cheaper model, in absolute points. 0 serves the best model the evidence knows; higher admits cheaper ones. A value outside the range returns 400. Overrides the organization setting, and the auto-quality / auto-value aliases are presets of the same dial. |
workflow_type | string | single_turn, execution_task, multi_step_conversational, long_horizon_autonomous, voice_simple, voice_complex, creative | auto-classified | Active | Explicitly sets workflow class when recognized. |
chain_id | string | UUID recommended | null | Active | Signals chain continuation; invalid UUID is ignored by classifier. |
conversation_id | string | free-form id | null | Active | Used for memory/sticky context continuity. |
planning_mode | string | preact or planact | tenant/default heuristic | Active | Controls planner style where planning layer is active. |
max_chain_steps | integer (u16) | 1..65535 | workflow-dependent | Active (conditional) | Applies when workflow is long_horizon_autonomous. |
chain_timeout_secs | integer (u32) | 0..4294967295 | workflow-dependent | Active (conditional) | Applies when workflow is long_horizon_autonomous. |
chain_routing | string | e.g. sticky / mixed | null | Reserved | Accepted in payload; rely on routing_hints.chain_routing for current behavior. |
reliability_level | string | standard, high, critical | standard | Active | Influences reliability/verification activation. |
enable_memory | boolean | true/false | true | Active | Controls persistence into L1/L2 memory in this handler path. |
enable_speculation | boolean | true/false | router/tenant default | Active | Per-request override for speculation toggle. |
enable_hedging | boolean | true/false | router/tenant default | Active | Per-request override for hedging toggle. |
enable_cache | boolean | true/false | true | Active | Enables/disables cache lookup path for this request. |
cache_ttl_secs | integer | 1 to 2592000 | organization setting (7 days) | Active | Lifetime, in seconds, of the response-cache entry this request writes; clamped to thirty days. The x-xantly-cache-ttl header does the same and wins when both are sent. |
output_verification | string | none, native, schema, cross_model | strategy auto-selection | Active | Per-request override for output verification strategy. |
compress_context | boolean | true/false | null | Reserved | Accepted for forward compatibility. |
redact_pii | boolean | true/false | false | Active | Sets request redaction signal (x-redact-pii=true metadata). |
voice_mode | string | typically "true" for voice path | null | Active | Enables voice-oriented handling when set. |
enable_tool_reranking | boolean | true/false | null | Reserved | Accepted for forward compatibility. |
intelligence_mode | string | proxy, cache, full | full (system default) | Active | Pipeline intelligence preset. See Intelligence Modes. |
Legacy request headers (still supported)
The gateway maps selected request headers into metadata for backward compatibility.
| Header | Mapped metadata key |
|---|---|
x-xantly-workflow | x-xantly-workflow |
x-xantly-voice | x-xantly-voice |
x-xantly-planning-mode | x-xantly-planning-mode |
x-xantly-preference | x-xantly-preference |
x-xantly-chain-routing | x-xantly-chain-routing |
x-xantly-lane | x-xantly-lane |
x-xantly-tier | x-xantly-tier |
x-xantly-run-id | run_id |
x-xantly-conversation-id | conversation_id |
x-intelligence-mode | x-intelligence-mode |
Choosing how the router picks
model accepts a routing alias instead of a model id. This is the normal way to
use Xantly: you state what matters and the router picks per request, rather than
you maintaining a model list that goes stale.
| Alias | What it optimises for |
|---|---|
auto or xantly/auto | Balanced. The router weighs quality, cost and latency per request. Start here. |
xantly/auto-speed | Lowest latency. |
xantly/auto-value | Lowest cost that still answers the request. |
xantly/auto-quality | Strongest answer. |
xantly/auto-safety | Safety-critical work. |
An objective reorders the models that can serve your request; it never lowers
the bar for what the request needs. Ask for speed on a hard question and you get
the fastest model that can still answer it, not a cheap one that cannot. That is
why xantly/auto-quality may return a mid-priced model on an easy question: it
was the best answer available for the work, and paying more would have bought
nothing.
Every response tells you what happened: x-xantly-objective echoes the
objective in force, and x-xantly-model-used names the model that answered.
Any xantly/* id that is not in this table is rejected with a 400, so a typo
fails loudly instead of silently routing somewhere you did not intend.
Response body
Non-stream (200 OK)
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1741400000,
"model": "bedrock/v3.2/us-east-1",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 31,
"completion_tokens": 18,
"total_tokens": 49
},
"xantly_metadata": {
"request_id": "req_01abc...",
"routing_decision": "Lane: Turbo (Source: BarpRouter)",
"provider": "deepseek",
"tier": "T2",
"provider_tier": "T2",
"latency_ms": 142,
"requested_model": "auto",
"decision_source": "BarpRouter",
"task_family": "analysis",
"cost_usd": 0.00032,
"baseline_cost_usd": 0.00120,
"savings_usd": 0.00088,
"savings_pct": 73.3,
"cost_attribution": "xantly",
"estimated_cost_usd": 0.00032,
"healer_report": null
}
}
xantly_metadata fields
| Field | Type | Always present | Description |
|---|---|---|---|
request_id | string | Yes | Correlation ID for support and audit. |
routing_decision | string | Yes | Human-readable routing summary (lane + source). |
provider | string | Yes | Provider that served the request (e.g. "deepseek", "anthropic"). |
tier | string | Yes | Effective execution tier ("T1", "T2", "T3"). |
provider_tier | string? | No | Provider-level tier when different from effective tier. |
latency_ms | integer | Yes | End-to-end gateway latency in milliseconds. |
requested_model | string | Yes | The model string you sent (e.g. "auto", "gpt-4o"). Useful for debugging routing decisions when using "auto". |
decision_source | string? | No | Machine-readable routing engine source (e.g. "BarpRouter", "Pinned", "CacheHit"). |
task_family | string? | No | Task category detected by the smart router (e.g. "code", "writing", "analysis"). |
cost_usd | number? | No | Actual cost for this specific request in USD. Present when usage data is available. |
baseline_cost_usd | number? | No | GPT-4o reference cost for the same token counts (input $2.50/M, output $10/M). Lets you compute savings without a separate analytics call. |
savings_usd | number? | No | baseline_cost_usd - cost_usd. Positive value means Xantly saved money vs. GPT-4o. |
savings_pct | number? | No | Savings as a percentage of baseline (0-100+). |
cost_attribution | string | Yes | "xantly" when routed via Xantly-managed provider keys; "byok" when routed via your own API key. |
estimated_cost_usd | number? | No | Legacy alias for cost_usd. Kept for backward compatibility. |
healer_report | object? | No | Present when JSON repair was applied. Contains original, healed, stage, confidence, healing_time_ms. |
Notes
choices[].logprobsis part of schema, but may be omitted in normalized API responses.- For
n > 1, if upstream returns fewer choices, the gateway may replicate the first choice to satisfy requested cardinality.
Stream (text/event-stream)
- All stream responses emit
chat.completion.chunkevents and terminate withdata: [DONE]. - Non-voice streaming currently emits a compact sequence (role chunk + content chunk +
[DONE]) instead of token-by-token chunking. stream_options.include_usageis forwarded to providers where supported; do not assume a terminal usage chunk is always present.
Response headers
Common observability headers
| Header | Present when | Description |
|---|---|---|
x-xantly-request-id | always | Request correlation id. Quote it in support requests. |
x-xantly-model-used | non-stream | The model that actually answered, which is the one you are billed for. This is the header to read when you want to know what auto chose. |
x-xantly-provider | always | Full catalog slug of the serving deployment, for example vertex/gemini-3.5-flash/global. |
x-xantly-objective | always | The routing objective in force: auto, speed, value, quality. |
x-xantly-tier-used | always | Effective execution tier (T1 to T3). |
x-xantly-lane-used | always | Effective lane (smart / turbo). |
x-xantly-cache-hit | always | true / false. |
x-xantly-cache-status | always | HIT or MISS. |
x-xantly-stream-mode | streaming | incremental or buffered. Some contracts (tools, strict JSON) are answered buffered. |
x-xantly-cost-usd | non-stream | What this request costs you. |
x-xantly-input-tokens, x-xantly-output-tokens | non-stream | Token counts as billed. |
x-xantly-latency-breakdown | non-stream | JSON string with stage-level latency, including gateway overhead. |
x-xantly-routing-confidence | non-stream | The router's confidence in the pick, 0.0 to 1.0. |
x-xantly-escalation-count, x-xantly-wasted-attempts | non-stream | How many substitutions the request needed, and how many attempts produced nothing. 0 on a clean request. |
x-xantly-audit-id | non-stream | Correlation id for the audit trail. |
x-ratelimit-limit, x-ratelimit-remaining | always | Your organization's per-minute request budget, and what is left of it. |
ratelimit-reset | always | Seconds from now until the window resets. This is the one to sleep on. |
x-ratelimit-reset | always | The same moment as a Unix epoch timestamp. Near-identical name, different unit: sleeping on this value waits decades. |
x-ratelimit-concurrency-limit, x-ratelimit-concurrency-scope | always | The in-flight request ceiling and what it applies to. The scope is gateway, meaning platform-wide and shared, not per organization and not raised by any plan. |
x-xantly-tool-capable | always | Whether the model that served this request can emit tool calls at all. Absent rather than false when we have no measurement, so it never guesses. This is what separates "the model ignored your tools" from "the model chose not to call one". |
x-xantly-reasoning-effort | when reasoning was requested | The effort the request actually resolved to and was served under. Absent when none applied. |
x-xantly-ignored-parameters | when non-empty | Comma-separated reserved fields you sent that nothing acted on. Emitted on buffered and streamed responses. The same list appears in xantly_metadata.ignored_parameters on the non-streamed body. |
x-xantly-provider-cost-usd | always | What the upstream provider charged for this request, so "we bill routed usage at provider cost" is checkable rather than merely asserted. |
x-xantly-cache-read-tokens, x-xantly-cache-write-tokens | when the provider reported them | Input tokens served from, and written to, the provider's prompt cache. See Prompt Caching. |
x-xantly-alignment | xantly/auto-safety, when the response proposed tool calls | checked;aligned=true;confidence=..;judge=.. when the calls serve your task, blocked;aligned=false;.. when they were stripped (the assistant message says why), unchecked;reason=.. when the judge could not answer and the calls were left in place. A streamed safety-mode request with tools is answered buffered (x-xantly-stream-mode: buffered) so the calls are judged whole. |
x-xantly-injection-guard | when the request carried tool results | mode=<mode>;scanned=<chunks>;flagged=<n>;confirmed=<n>;redacted=<n>;spotlighted=<n>: what the tool-result injection guard did to this request (flagged is the classifier; confirmed is the second check, which runs in redact mode only, and only a confirmed piece is removed). Send the same header name on the request, x-xantly-injection-guard: spotlight or redact, to tighten the mode for that request; it can never loosen it. Emitted on buffered and streamed responses. See Security & Compliance. |
Every response also carries the standard OpenAI fields, so a client that reads
response.model and response.usage needs no Xantly-specific code at all.
Cache-path headers
| Header | Value |
|---|---|
x-xantly-cache-type | exact or semantic |
x-xantly-latency-ms | end-to-end latency (cache path) |
x-xantly-semantic-similarity | similarity score (semantic cache only) |
Usage / cost headers
These are included when usage is available:
x-xantly-input-tokensx-xantly-output-tokensx-xantly-cost-usd
Timeouts
The gateway gives a request a budget for one attempt and a budget for the whole attempt chain. Both are derived from the shape of your request, because a tool call and a reasoning turn are not the same kind of wait.
| Request shape | One attempt | Whole chain |
|---|---|---|
| Plain chat | 25s | 52s |
| Plain chat asking for 2048 output tokens or more | max_tokens divided by 40 tokens a second, at least 25s | twice the attempt plus 2s |
Reasoning (reasoning or reasoning_effort set) | 850s | 900s |
| Tools present | 10s | 20s |
response_format JSON | 8s | 15s |
Pinned model (allow_fallbacks: false) | 8s | 52s |
| Voice | 5s | 8s |
Two ceilings apply to the scaled row. A buffered answer is capped at 90s per attempt and 95s for the chain, because the edge in front of the API closes a connection that has sent no bytes for 100 seconds. A streamed answer is not subject to that, since bytes are already flowing, so it runs up to the 15 minute hard cap. If you ask for a long answer, stream it.
Deciding for yourself
routing_override.timeout_ms sets the chain budget and raises the per-attempt
ceiling with it, from 1ms to 900000ms (15 minutes). It raises as well as lowers:
the gateway's own default is a starting point, not a limit you must live under.
curl -sS https://api.xantly.com/v1/chat/completions \
-H "Authorization: Bearer $XANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "auto",
"messages": [{"role": "user", "content": "Prove it, showing every step."}],
"max_tokens": 8192,
"stream": true,
"routing_override": {"timeout_ms": 600000}
}'
An organization-level timeout, if an administrator has set one, still applies as a ceiling over whatever a request asks for.
What happens when an attempt runs out
A timeout is a failed attempt, not a failed request: the gateway substitutes
another model and keeps going, until max_fallbacks or the chain budget is
spent. Set allow_fallbacks: false to fail on the first attempt instead.
x-xantly-escalation-count on the response tells you how many substitutions it
took, and x-xantly-wasted-attempts how many produced nothing.
Errors
Error payloads use OpenAI-style shape:
{
"error": {
"message": "temperature (2.5) must be between 0 and 2",
"type": "invalid_request_error",
"code": "validation_error",
"param": null
}
}
| HTTP | error.type | error.code (examples) | Typical trigger |
|---|---|---|---|
400 | invalid_request_error | validation_error | Invalid parameter range, unknown model, empty messages. |
401 | authentication_error | invalid_api_key, expired_api_key, revoked_api_key | Missing/invalid API key. |
403 | authorization_error | forbidden, insufficient_scopes, tenant_violation | Scope/policy denial. |
404 | not_found_error or governance_error | resource_not_found, tool_not_registered | Missing referenced resource or unregistered tool. |
422 | governance_error | tool_call_blocked | Governance blocked a tool call. |
429 | rate_limit_error | rate_limit_exceeded, upsteam_rate_limit | Tenant or upstream rate limiting. |
502 | upstream_error | provider_error | Upstream provider error. |
503 | governance_error | circuit_breaker_open | Circuit breaker open. |
504 | upstream_error | provider_timeout | Upstream timeout. |
500 | internal_error | internal_error | Internal platform failure. |
Error headers
x-error-idis returned on error responses for support correlation.retry-afteris returned on eligible rate-limit responses.
Edge cases and implementation notes
-
Model validation is catalog-aware
modelvalidation is strict when the model catalog is loaded.- During catalog cold-start/empty states, strict rejection may be relaxed.
-
top_logprobsvalidation- Server enforces
top_logprobs <= 20. - Provider-specific
logprobsbehavior may vary.
- Server enforces
-
Chain limit overrides are conditional
xantly.max_chain_stepsandxantly.chain_timeout_secsapply only when workflow resolves tolong_horizon_autonomous.
-
Streaming semantics
- Non-voice stream mode is SSE-compatible but not guaranteed token-by-token.
-
Reserved fields
- Some accepted proprietary fields are reserved for compatibility (
force_provider,compress_context,enable_tool_reranking, and certainrouting_hintsfields); they come back inx-xantly-ignored-parameters.
- Some accepted proprietary fields are reserved for compatibility (
Practical rollout checklist
- Start with
model: "auto"+ standard fields. - Enable
response_formatfor structured outputs. - Add one routing/orchestration control at a time.
- Track
x-xantly-*response headers in observability. - Add override fields only for test/debug traffic.
See also
- Benchmark Results, Every parameter on this page is individually validated. See the full 252-test scorecard including boundary and invalid-input cases.
- Streaming Responses, Complete guide to SSE format, chunk handling, and SDK examples.
- Rate Limits, RPM and TPM limits that apply to this endpoint.
- Billing & Credits, Prepaid credits, budget caps, and cost visibility via
xantly_metadata.