Models
List every model, its category, its status and the price it is published at.
GET /v1/models is the whole catalogue, every model, its category, its status and every axis it bills against. It answers anonymously and is cached, so a model picker or a pricing page can read it straight from a browser. There is no companion route naming the upstreams behind a model: which host serves a request is not part of what the model is.
GET/v1/modelsAvailable
Every model with its category, status, published price and per-token rates.
What a model row carries
| FIELD | TYPE | MEANING |
|---|---|---|
| id | string | What you put in model. The primary alias where one is set, otherwise the canonical slug. |
| canonical_slug | string | The stable vendor/model slug. Equal to id unless id is an alias. |
| name | string | Display name, for a human. GPT-4o mini. |
| description | string | One line on what the model is for. |
| category | enum | text, image, video, audio, embedding or rerank. Decides which route serves it. |
| status | enum | available, coming_soon or deprecated. |
| unit | string | The billing unit in words, per 1M tokens, per 5s clip, 720p. |
| price | number, null | Headline price in that unit. Null while a model is coming_soon. |
| reference_price | number, null | The same unit at the vendor's own list price. |
| context_length | integer, null | Largest context this model accepts. Null where the idea does not apply. |
| created | integer | Unix seconds the model joined the catalogue. |
| architecture | object | Modalities in and out, tokeniser, instruct type. |
| pricing | object | Every metered axis, as strings. |
| supported_parameters | string[] | Request fields the model honours. |
Frozen, then additive
The first six fields (id, category, status, unit, price and reference_price) are frozen, and every change since has been an addition. That is the only kind of change a published catalogue may make: a field appears, an existing one does not change meaning and does not go away. Read the fields you need by name and ignore the rest, and nothing added next month can break you.
Rates are strings, the headline price is a number
The rates inside pricing come back as JSON strings. A per-token rate is around 0.0000005; a client that parses a million of them as float64 and adds them up drifts away from the invoice, and internally the value is a decimal backed by a numeric column, so emitting a number would mean converting it to a double on the way out. Parse rates with a decimal library, not parseFloat. price and reference_price stay numbers: they are a headline for a human to read, not an input to billing arithmetic.
The pricing object
| AXIS | METERED ON |
|---|---|
| prompt | Each input token. |
| completion | Each output token. |
| cached_prompt | Each input token served from the upstream prompt cache. |
| internal_reasoning | Each reasoning token, where they are billed apart from completion. |
| request | A flat charge per call, on top of any metered axis. |
| image | Each generated image. |
| image_input | Each image supplied as input. |
| video_second | Each second of generated video. |
| audio_second | Each second of generated or transcribed audio. |
| web_search | Each built-in web-search call. |
Null on an axis means the model is not metered on it. That is a different claim from the string 0, which means metered and free. A text model has a null image rate, a promotional model has a zero one, and a client that treats the two the same bills the wrong thing the first time one becomes the other.
architecture
architecture.modality is the summary (text->text, text->image), and the arrays under it are the precise version: input_modalities is what you may send, output_modalities is what comes back. tokenizer names the tokeniser the context length is counted in, and instruct_type names the prompt template that models needing one expect; both are null where the vendor has published neither. Check input_modalities before sending an image inside a message, rather than finding out from a 400.
supported_parameters
supported_parameters lists the request fields this model honours. Tools, temperature, response_format and the rest. Read it before you send one: a parameter that is not on the list may be ignored rather than refused, so a request carrying tools can come back as a plain completion. Checking the list at render time is cheaper than discovering it in a response you have already paid for.
The three statuses
| STATUS | PRICE | WHAT A REQUEST DOES |
|---|---|---|
| available | Set | Routed and billed normally. |
| coming_soon | Null | 409 model_unavailable. Announced, listed, not yet servable. |
| deprecated | Set | Still served and still billed. It will stop being. Move off it. |
Nothing is priced automatically. A model discovered on an upstream catalogue arrives as coming_soon with a null price and stays there until somebody sets one, which is why an unpriced model is refused rather than served for nothing: a 200 that charged nothing cannot be billed for afterwards. deprecated exists so that retiring a model is distinguishable from deleting one, without it, a model on its way out looks exactly like a model that never existed.
Aliases
gpt-4o and openai/gpt-4o are the same model. The alias is resolved in the same query as the slug, so the short form costs the same single round trip as the long one and neither is a redirect. The response, the charge and the record on GET /v1/generation are identical. Store canonical_slug rather than id: canonical_slug is stable, while the id published for a model changes if its primary alias changes.
Narrower reads
Four routes answer a smaller question than the full catalogue, and each is cheaper to parse than filtering it yourself. GET /v1/models/count is the number of servable models. GET /v1/embeddings/models, /v1/images/models and /v1/videos/models are the catalogue narrowed to one modality, each carrying a total_count beside its data. GET /v1/model/{author}/{slug} is one model. Note the singular model, which is the spelling this route has and the plural one does not.
GET/v1/models/countAvailable
How many models are servable right now.
GET/v1/model/{author}/{slug}Available
One model by slug. Singular 'model'. OpenRouter's spelling for this route.
GET/v1/embeddings/modelsAvailable
The catalogue narrowed to embedding models, with a total count.
GET /v1/models/user exists and answers, but it returns the same set as GET /v1/models: model access here is not scoped per key, so there is no narrower list for it to give. It is published so that a client written against a gateway that does scope access gets a catalogue rather than a 404.
Reading the catalogue
# Every text model callable today, cheapest first. The catalogue
# route takes no query parameters. Narrow it client-side, or use one
# of the modality routes above.
curl -s https://api.routehook.ai/v1/models \
| jq '[.data[] | select(.category == "text" and .status == "available")]
| sort_by(.price)
| .[] | {id, price, unit}'
# One model in full, including every axis it bills against.
curl -s https://api.routehook.ai/v1/model/openai/gpt-4o-mini \
| jq '.data | {id, name, context_length, pricing, supported_parameters}'