# Models (https://hypit.ai/api-reference/models/)

> List what your key can route to, and read a model's credit rate before you spend anything.

The model catalogue is the one place that says, authoritatively, which models your key can reach and
what each one costs. Model availability changes as upstream vendors come and go, so read the list at
runtime rather than hard-coding it.

## List models [#list-models]

```bash
curl https://hypit.ai/v1/models \
  -H "Authorization: Bearer $HYPIT_API_KEY"
```

```json
{
  "object": "list",
  "data": [
    {
      "id": "nano-banana-pro",
      "object": "model",
      "created": 1787824589,
      "owned_by": "hypit",
      "modality": "image",
      "display_name": "Nano Banana Pro",
      "description": "…",
      "tags": ["image", "edit"],
      "endpoints": ["images", "image_edits"],
      "pricing": {
        "mode": "per_image",
        "currency": "usd",
        "per_image_usd": 0.0,
        "credits": { "base": 0.0 }
      }
    }
  ]
}
```

The first four fields (`id`, `object`, `created`, `owned_by`) are the OpenAI shape byte for byte, so
`client.models.list()` in an unmodified OpenAI SDK works. Everything after them is ours; clients
that do not want the extra keys ignore them.

### `endpoints` [#endpoints]

The most useful field. It names which routes will actually accept the model, and it is the answer to
"why does this model 404 on `/v1/videos`". The vocabulary is fixed:

| Value            | Route                                  |
| ---------------- | -------------------------------------- |
| `images`         | `POST /v1/images/generations`          |
| `image_edits`    | `POST /v1/images/edits`                |
| `videos`         | `POST /v1/videos`                      |
| `chat`           | `POST /v1/chat/completions`            |
| `gemini`         | `POST /v1beta/models/{model}:{action}` |
| `audio_speech`   | `POST /v1/audio/speech`                |
| `transcriptions` | `POST /v1/audio/transcriptions`        |
| `audio_music`    | `POST /v1/audio/music`                 |

`modality` is coarser — one of `image`, `video`, `chat`, `audio` — and is what the job envelope's
`kind` will say.

### `pricing` [#pricing]

`pricing.mode` tells you what the model charges per: `per_image`, `per_request`, `per_second`,
`per_token`, `per_k_char` or `per_audio_second`. The `*_usd&#x60; figures are the retail price already
including our markup, kept as a reference; the figure that matters is &#x2A;*`pricing.credits`**, because
credits are the only balance an account holds.

```json
"pricing": {
  "mode": "per_second",
  "currency": "usd",
  "per_second_usd": 0.0,
  "resolution_ratio": { "480p": 1, "720p": 2.1579, "1080p": 5.3684 },
  "credits": { "per_second": 0.0 }
}
```

Token rates inside `credits` are quoted **per thousand tokens** (`input_per_k`, `output_per_k`,
`cached_per_k`, `reasoning_per_k`), while the USD fields beside them are per million — that is how
vendors publish, and how our rate cards store them. The ratio tables (`size_ratio`, `quality_ratio`,
`resolution_ratio`, `flag_ratio`) are pure multipliers on the headline rate: a 1080p second of a
model whose `resolution_ratio["1080p"]` is `5.3684` costs `per_second × 5.3684`.

`token_tiers` is a long-context ladder: each rung carries a `min_prompt_tokens` threshold and the
rates that apply above it. A rate left at zero on a rung inherits the headline one.

<Callout type="info" title="Do not re-derive credits from the USD numbers">
  The projection from dollars to credits involves a per-model multiplier that the API deliberately
  does not publish. Read `pricing.credits`; a client that multiplies `per_second_usd` by a constant
  will quote promotional and premium models wrong, silently, and only where being wrong costs money.
</Callout>

## Fetch one model [#fetch-one-model]

```bash
curl https://hypit.ai/v1/models/nano-banana-pro \
  -H "Authorization: Bearer $HYPIT_API_KEY"
```

Model ids may be namespaced with a slash, and the route handles that intact:

```bash
curl https://hypit.ai/v1/models/bytedance/seedance-2 \
  -H "Authorization: Bearer $HYPIT_API_KEY"
```

The response is a single model card — the same object as one element of `data` above. A model your
key cannot route to answers `404` with code `model_not_found`, which is the same answer an unknown
model gets: the catalogue does not confirm that a model exists but is out of your reach.

## The public rate card [#the-public-rate-card]

There is one unauthenticated endpoint, for pricing pages and for evaluating hypit.ai before signing
up:

```bash
curl https://hypit.ai/api/hub/public/models
```

```json
{
  "object": "list",
  "count": 18,
  "modalities": { "image": 7, "video": 9, "chat": 2, "audio": 0 },
  "updated_at": 1787824589,
  "data": [
    {
      "name": "bytedance/seedance-2",
      "modality": "video",
      "endpoints": ["videos"],
      "pricing": { "mode": "per_second", "unit": "per second", "currency": "usd", "credits": {} }
    }
  ]
}
```

Note the differences from `/v1/models`: the model is keyed `name` rather than `id`, there is a
`unit` string spelling out what the headline number is charged per, and the envelope carries
`count`, `modalities` and `updated_at` so a page can render "prices as of …" without walking the
list. It also only ever shows the default routing group — your key may see more models on `/v1/models`.

It is rate limited to 60 requests per minute per IP, served from a pre-rendered snapshot with an
`ETag`, and honours `If-None-Match`:

```bash
curl -i https://hypit.ai/api/hub/public/models \
  -H 'If-None-Match: "ce323a…"'
```

## Choosing a model in code [#choosing-a-model-in-code]

```python
import os, httpx

r = httpx.get(
    f"{os.environ['HYPIT_BASE_URL']}/models",
    headers={"Authorization": f"Bearer {os.environ['HYPIT_API_KEY']}"},
)
r.raise_for_status()

video = [m for m in r.json()["data"] if "videos" in m["endpoints"]]
for m in video:
    print(m["id"], m["pricing"]["credits"].get("per_second"), "credits/second")
```

```js
const r = await fetch(`${process.env.HYPIT_BASE_URL}/models`, {
  headers: { Authorization: `Bearer ${process.env.HYPIT_API_KEY}` },
});
if (!r.ok) throw new Error(await r.text());

const { data } = await r.json();
for (const m of data.filter((m) => m.endpoints.includes("videos"))) {
  console.log(m.id, m.pricing.credits?.per_second, "credits/second");
}
```