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_sec in the response). A model that broke between two probes still reports its previous status.
  • unknown is 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. Treat unknown as 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

GET/v1/models/availability

Returns 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

statusMeaningSuggested handling
availableThe last three probes all succeeded.Offer the model normally.
degradedRecent probes are mixed, or the only probes so far failed (too few samples to call it down).Keep it selectable; expect retries.
unavailableThe last three probes all failed, or the model has no usable route at all.Hide it, or warn before sending traffic.
unknownNo 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/models exactly: the same authorization scope, the same channel resolution. A model your key cannot call never appears here.
  • A model's status aggregates its channels: any channel available makes the model available; all unavailable makes it unavailable; all unknown makes it unknown; anything else is degraded.
  • 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"
unknownfield 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

StatusCodeMeaning
401authentication_errorMissing, invalid, or inactive API key.
503availability_unavailableThe 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.

On this page

On this page