Model availability
Read which models can actually be served right now, as observed by the gateway's periodic probes.
What this endpoint reports
The gateway periodically calls the upstream route behind every model it currently sells and records the result. This endpoint returns that observation, scoped to the models your API key can call.
It answers "can this model be served right now", which is not the same as "is this model configured and enabled". A model can be enabled, published, and pass catalog sync while every call to it fails; that is exactly the case this endpoint exists to expose.
Two properties to design around:
- This is an observation, not an SLA. Detection lag equals the probe interval (
probe_interval_secin the response). A model that broke between two probes still reports its previous status. unknownis not "unavailable". It means the platform has no recent observation — probing is disabled, the model has never been probed, or the last probe is older than twice the interval. Treatunknownas usable and do not block on it.
Authentication
Server-to-server. Authenticate with Authorization: Bearer <API_KEY>; keep the key on your backend.
Read availability
/v1/models/availabilityReturns one row per model your key can call, with a per-channel breakdown.
curl https://hopsapi.com/v1/models/availability \\
-H "Authorization: Bearer sk-YOUR_KEY"{
"generated_at": "2026-09-23T09:30:00.000Z",
"probe_interval_sec": 600,
"data": [
{
"model": "claude-sonnet-5",
"status": "available",
"checked_at": "2026-09-23T09:29:41.000Z",
"channels": [
{
"channel": "standard",
"status": "available",
"checked_at": "2026-09-23T09:29:41.000Z",
"recent": "111"
}
]
},
{
"model": "gpt-5.6-terra",
"status": "degraded",
"reason": "upstream_5xx",
"checked_at": "2026-09-23T09:28:02.000Z",
"channels": [
{
"channel": "flex",
"status": "degraded",
"reason": "upstream_5xx",
"checked_at": "2026-09-23T09:28:02.000Z",
"recent": "011"
}
]
},
{
"model": "kimi-k3",
"status": "unknown",
"checked_at": null,
"channels": [
{ "channel": "standard", "status": "unknown", "checked_at": null, "recent": null }
]
}
]
}Status values
status | Meaning | Suggested handling |
|---|---|---|
available | The last three probes all succeeded. | Offer the model normally. |
degraded | Recent probes are mixed, or the only probes so far failed (too few samples to call it down). | Keep it selectable; expect retries. |
unavailable | The last three probes all failed, or the model has no usable route at all. | Hide it, or warn before sending traffic. |
unknown | No recent observation. | Treat as usable. Do not filter it out. |
status and reason are open value sets. A value you do not recognize must be handled as unknown, never as an error — new reasons are added without a breaking change.
reason is present only when the status is not available. Current values: no_upstream (no shippable route for this model and channel), upstream_5xx, upstream_timeout, upstream_auth, upstream_rate_limited, upstream_unavailable, empty_response, request_error.
recent is the last three probe results for that channel, newest first (1 = success, 0 = failure); it is null when the channel has never been probed.
Rows and scope
- Rows and channels match
GET /v1/modelsexactly: the same authorization scope, the same channel resolution. A model your key cannot call never appears here. - A model's
statusaggregates its channels: any channelavailablemakes the modelavailable; allunavailablemakes itunavailable; allunknownmakes itunknown; anything else isdegraded. - Results are per-platform, not per-key: two keys that can call the same model and channel see the same status. Probes use the route's default upstream credential, so a key with a dedicated upstream account still sees the shared route's status.
- Media models appear with
status: "unknown"— only text model routes are probed in this release.
The health field on /v1/models
GET /v1/models carries the same observation as an optional health field, so a device that fetches the catalog once does not need a second call:
availability status | /v1/models health |
|---|---|
available | "healthy" |
degraded | "degraded" |
unavailable | "down" |
unknown | field absent |
A missing health field means "not observed", not "unavailable" — filter on health === "down" only.
ETag polling
Poll with ETag / If-None-Match rather than pulling the full body on a timer. 60 seconds is a reasonable interval.
curl -i https://hopsapi.com/v1/models/availability \\
-H "Authorization: Bearer sk-YOUR_KEY" \\
-H 'If-None-Match: "a7f3c18e"'The ETag covers only the model, channel, status, and reason set — not checked_at. An unchanged status set returns 304 Not Modified with no body even though the underlying probe timestamps have moved on.
Errors
| Status | Code | Meaning |
|---|---|---|
401 | authentication_error | Missing, invalid, or inactive API key. |
503 | availability_unavailable | The observation source could not be read. Keep your previous snapshot; an error is never an empty catalog and never "everything is available". |
Every response carries x-aitoken-request-id; retain it for support and reconciliation.