Queries the provider's real models endpoint, so the answer is never stale — it is not the gateway's opinion, it is the provider's answer.
Reference#
GET /v1/models?provider=NAME
Requires Authorization: Bearer <SKYL_AUTH_TOKEN>. Returns
application/json.
Parameters
provider(query) — which registered provider to ask. Defaults toSKYL_DEFAULT_PROVIDER.
Response — ModelsResponse#
{
"provider": "anthropic",
"models": [
{
"id": "claude-opus-5",
"display_name": "Claude Opus 5",
"context_window": 200000,
"max_output_tokens": 64000
}
]
}{
"provider": "anthropic",
"models": [
{
"id": "claude-opus-5",
"display_name": "Claude Opus 5",
"context_window": 200000,
"max_output_tokens": 64000
}
]
}| Field | JSON | Notes |
|---|---|---|
| ID | id | Always present. This is what goes in "model". |
| DisplayName | display_name | Omitted when the provider does not report it. |
| ContextWindow | context_window | Omitted when zero. |
| MaxOutputTokens | max_output_tokens | Omitted when zero. |
Caveats
- Only
idis populated by every provider. OpenAI's endpoint reports no display name, context window or output cap — the gap is upstream. - This makes a live upstream call, so it is bounded by
SKYL_REQUEST_TIMEOUTand counts against your provider rate limits. Do not call it per request. - An unregistered provider name returns 404.
- On Gemini the list truncates silently beyond 1000 models, because the
adapter ignores
nextPageToken.
Usage#
Listing a provider's models
curl -sS "localhost:8080/v1/models?provider=openai" \
-H "Authorization: Bearer $SKYL_AUTH_TOKEN"curl -sS "localhost:8080/v1/models?provider=openai" \
-H "Authorization: Bearer $SKYL_AUTH_TOKEN"import httpx
r = httpx.get(f"{BASE}/v1/models", headers=HEADERS, params={"provider": "openai"})
r.raise_for_status()
for m in r.json()["models"]:
print(m["id"], m.get("context_window") or "")import httpx
r = httpx.get(f"{BASE}/v1/models", headers=HEADERS, params={"provider": "openai"})
r.raise_for_status()
for m in r.json()["models"]:
print(m["id"], m.get("context_window") or "")const res = await fetch(`${BASE}/v1/models?provider=openai`, { headers: HEADERS });
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { models } = await res.json();
for (const m of models) console.log(m.id, m.context_window ?? '');const res = await fetch(`${BASE}/v1/models?provider=openai`, { headers: HEADERS });
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { models } = await res.json();
for (const m of models) console.log(m.id, m.context_window ?? '');Populating a picker at startup, not per request
// Cache it. This is a live upstream call against your rate limit.
const models = await fetch('/v1/models?provider=anthropic', {
headers: { Authorization: `Bearer ${token}` },
}).then((r) => r.json());// Cache it. This is a live upstream call against your rate limit.
const models = await fetch('/v1/models?provider=anthropic', {
headers: { Authorization: `Bearer ${token}` },
}).then((r) => r.json());Troubleshooting#
404 with kind “not_found”
The named provider is not registered on this gateway. Check /v1/providers for
what is.
display_name and context_window are missing
Omitted when the provider does not report them, which on OpenAI is always.
This is slow
It is a live call to the provider. Cache the result; the list changes on the order of weeks.