Stav refuses request parameters by name rather than dropping them silently. That is only fair if you can find out what a model honours before you send anything — so capability discovery is part of the model API, not an afterthought.
List what your key can call
curl https://api.stav.ai/v1/models \
-H "Authorization: Bearer $STAV_API_KEY"
for m in client.models.list():
print(m.id)
This returns only models that (a) your team's policy allows and (b) currently have a healthy endpoint. It is a permission-and-health view, not the whole catalogue — if a model you expect is missing, the cause is one of those two things.
Requires the inference:models scope.
Read a single model
curl https://api.stav.ai/v1/models/claude-sonnet-4-5-20250929 \
-H "Authorization: Bearer $STAV_API_KEY"
{
"id": "claude-sonnet-4-5-20250929",
"object": "model",
"created": 1759104000,
"owned_by": "anthropic",
"stav_category": "llm",
"stav_context_window": 200000,
"stav_parameters": null,
"stav_quantization": null,
"stav_supports_thinking": true,
"stav_capabilities": {
"extended_thinking": true,
"tool_use": true,
The stav_* fields are additive extensions. The OpenAI SDK ignores them; your code should not.
Use capabilities as a pre-flight check
info = client.models.retrieve("claude-sonnet-4-5-20250929")
caps = info.model_extra["stav_capabilities"]
if caps.get("extended_thinking"):
kwargs["reasoning_effort"] = "high"
if not caps.get("vision"):
raise RuntimeError("this pipeline needs a vision model")
The same flags are what the Smart Router filters on. If you send tools with model="auto", the candidate pool is narrowed to models where tool_use is true before any scoring happens — so you never get routed to a model that cannot do the job.
Browse the public catalogue
The full catalogue is anonymous — no key needed — and is what powers stav.ai/models.
curl "https://api.stav.ai/v1/public/models?page_size=20&category=llm"
curl "https://api.stav.ai/v1/public/models/qwen%2Fqwen3-6-27b"
curl "https://api.stav.ai/v1/public/providers"
curl "https://api.stav.ai/v1/public/sovereignty"
Supported filters on /v1/public/models: page, page_size, category, creator_slug, is_open_weights, featured, search, model_family, sort_by, sort_dir. Responses are ETag-cached; the endpoint is rate-limited per IP.
Use /v1/public/* for catalogue browsing and /v1/models for "what can this key do right now". They answer different questions.
Addressing: the three forms of model
| Form | Example | Meaning |
|---|---|---|
| Catalogue slug | claude-sonnet-4-5-20250929 | Pin exactly this model |
auto | auto | Route using your team's default policy |
| Named router | @acme/eu-only | Route using a router your team authored |
Slugs are matched exactly. Some carry a vendor prefix (qwen/qwen3-6-27b, mistralai/mistral-nemo-instruct-2407) and some do not (gpt-4o-mini, claude-haiku-4-5-20251001) — there is no alias resolution, so copy the slug from /v1/models rather than guessing it. A wrong slug is a clean 404, here on /v1/chat/completions:
{
"error": {
"message": "The model 'gpt-4o-turbo' does not exist or you do not have access to it.",
"type": "invalid_request_error",
"param": "model",
"code": "model_not_found"
}
}
Note the message covers both cases deliberately — Stav does not confirm the existence of a model your team cannot use.
Choosing between pinning and routing
Pin a model when you are reproducing a specific evaluation, you depend on a quirk of one model's formatting, or a customer contract names it.
Route when you care about the outcome rather than the vendor — which is most production traffic. Routing gives you failover, cost and latency optimisation, and a sovereignty floor you can enforce centrally instead of in every service.
A useful middle ground is a named router with a tight allowlist: two or three models you have qualified, ordered by your own weights, addressed as one stable identifier. Your application code stops changing every time you re-qualify a model.
Next steps
- Smart Router
- Request parameters — what each model honours and what it refuses
- Reasoning