GET /v1/models
The catalog, in whichever shape your SDK expects.
Lists every model id this gateway can serve — one row per distinct id. This is the endpoint SDK models.list() calls resolve to, so it answers in two shapes:
| Request | Response shape |
|---|---|
No anthropic-version header | OpenAI list object |
anthropic-version header present (any value) | Anthropic models list |
The Anthropic SDK sends anthropic-version on every request, so each SDK gets its native shape with zero configuration.
Authentication is required, in either surface's convention — both work on every gateway route:
Authorization: Bearer $TM_API_KEY
# or
x-api-key: $TM_API_KEYA missing or invalid key is a 401 with error_type: "auth" (see Errors).
A key bound to your own provider connection lists that connection's models; a key under a routing policy lists the policy's models. Deployed endpoints from Optimize (tm/<name>-v<n>) are not listed here. Your organization's active dedicated endpoints (<your-namespace>/<name>) are, with owned_by routerplus, on the base URL they are served from.
OpenAI shape (default)
curl -s https://api.routerplus.com/v1/models \
-H "Authorization: Bearer $TM_API_KEY"{
"object": "list",
"data": [
{ "id": "claude-haiku-4-5", "object": "model", "created": 1756900000, "owned_by": "anthropic",
"architecture": { "input_modalities": ["text", "image", "file"], "output_modalities": ["text"] } },
{ "id": "gpt-image-1", "object": "model", "created": 1756900000, "owned_by": "openai",
"architecture": { "input_modalities": ["text"], "output_modalities": ["image"] } },
{ "id": "wan-3.0", "object": "model", "created": 1756900000, "owned_by": "openrouter-media",
"architecture": { "input_modalities": ["text"], "output_modalities": ["video"] } }
]
}| Field | Type | Meaning |
|---|---|---|
id | string | The exact string for your request's model field |
object | "model" | Constant |
created | integer | Unix seconds — see the note below |
owned_by | string | Provider id of the first deployment routing would try for this model; routerplus for a dedicated endpoint |
architecture.input_modalities | array | What a message to the model may carry: "text", plus each of "image", "file", "audio" and "video" that a route of this key takes. A chat request with a part that no route takes is a 400 (see Wire compatibility). Image, video and decision models take ["text"]. A route on your own provider key declares no inputs, so it adds none here; the gateway sends it every part as you sent it. A documented extension OpenAI SDKs ignore |
architecture.output_modalities | array | ["text"], ["image"], ["video"] or ["decisions"] — a documented extension OpenAI SDKs ignore |
Filtering by modality
curl -s "https://api.routerplus.com/v1/models?output_modalities=image" \
-H "Authorization: Bearer $TM_API_KEY"output_modalities takes a comma-separated list of text, image, video and decisions; the list is a filter, so ?output_modalities=text,image,video,decisions is the same as sending nothing. Any other value is a 400 invalid_request ("invalid output_modalities filter"). The Anthropic shape never lists image, video or decision models — the Anthropic Messages API has no route to call them on.
A provider query parameter takes the same JSON object as the request-level provider routing controls ({"only":["openai"]}, for example) and lists what those controls would let through — see Routing policies.
Anthropic shape
curl -s https://api.routerplus.com/v1/models \
-H "x-api-key: $TM_API_KEY" \
-H "anthropic-version: 2023-06-01"{
"data": [
{ "type": "model", "id": "claude-haiku-4-5", "display_name": "claude-haiku-4-5", "created_at": "2026-09-03T12:00:00.000Z" }
],
"has_more": false,
"first_id": "claude-haiku-4-5",
"last_id": "gpt-4o-mini"
}has_more is always false: the catalog is small and the full list arrives in one response. There is no pagination to implement.
created / created_at is the time the gateway process loaded its catalog, not the model's release date. Don't build logic on it. For real metadata — context length, prices, retention policy — use the public /api/models.json, documented in Models & catalog. This endpoint stays SDK-shaped plus one documented extension: architecture, which SDKs ignore and modality-aware clients read.
SDK usage
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.routerplus.com/v1", api_key=os.environ["TM_API_KEY"])
for m in client.models.list():
print(m.id, m.owned_by)import os
import anthropic
# The SDK sends anthropic-version itself, so this gets the Anthropic shape.
client = anthropic.Anthropic(base_url="https://api.routerplus.com", api_key=os.environ["TM_API_KEY"])
for m in client.models.list():
print(m.id)import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.routerplus.com/v1", apiKey: process.env.TM_API_KEY });
for await (const m of client.models.list()) {
console.log(m.id, m.owned_by);
}Unknown models on completion calls
This list is the contract: a completion request (/v1/chat/completions or /v1/messages) for any id not in it fails immediately with a 404 that echoes exactly the string you sent — before any provider is contacted and before anything is billed. The one exception is a dated pin of a listed model (claude-haiku-4-5-20251001, gpt-4o-2024-08-06, -latest), which routes as its family — see Models & catalog.
OpenAI surface:
{
"error": {
"code": "model_unavailable",
"message": "model \"gpt-5o\" is not in the catalog; GET /v1/models lists what this key can serve",
"type": "invalid_request_error",
"metadata": { "error_type": "model_unavailable" }
},
"request_id": "6f3c…"
}Anthropic surface:
{
"type": "error",
"error": {
"type": "not_found_error",
"message": "model \"gpt-5o\" is not in the catalog; GET /v1/models lists what this key can serve",
"error_type": "model_unavailable"
},
"request_id": "6f3c…"
}Both carry the x-tm-error-code: model_unavailable header alongside x-request-id.
There is no fuzzy matching and no silent fallback to a "close" model. A typo in the model id is a 404, not a quietly different bill. Echoing your own requested string back names the problem without turning the error into an existence oracle — unknown ids all 404 identically.
Three neighbors of this error are worth telling apart:
| Status | error_type | Meaning |
|---|---|---|
| 404 | model_unavailable | The id is not in the catalog. Fix the id. |
| 503 | gateway_error | The id is in the catalog, but every deployment serving it is cooling down after failures. Comes with retry-after: 5 and x-tm-limit-kind: health — retry, don't fix. |
| 400 | model_not_priced | Billed key, and the ledger has no price row for this model — the gateway refuses to serve what it cannot meter. |
The full taxonomy is in Errors.
One more neighbor: a listed image model sent to a chat route is a 400 invalid_request naming POST /v1/images/generations, not a 404 — the id exists, you called the wrong route. A video model on a chat route names POST /v1/videos the same way. The mirror case (a chat model on the images or videos route) is the same 400 pointing back at the chat routes. See POST /v1/images/generations and POST /v1/videos.
Markdown source for agents: /docs/api-models.md · index at /llms.txt