GET /v1/models
GET /v1/models returns cc-router’s fixed list of virtual models. The list does not reflect upstream subscriptions — it is a hard-coded alias table so that Claude Code, Codex CLI, and any client speaking the Anthropic or OpenAI Models protocol all see the same virtual model catalog.
Applies to cc-router v3.0.0 and later.
Design change (v3.0+: Anthropic + OpenAI field superset)
To let one URL satisfy both Anthropic SDKs and OpenAI SDKs / OpenAI Responses clients, cc-router treats /v1/models as the superset of Anthropic /v1/models and OpenAI /v1/models schemas:
| Field | Source protocol | Notes |
|---|---|---|
type: "model" | Anthropic | Anthropic SDK checks for it |
id | Both | Consumed by both |
display_name | Anthropic | Anthropic SDK uses it for display |
created_at (ISO string) | Anthropic | Hard-coded "2026-01-01T00:00:00Z" |
object: "model" | OpenAI | OpenAI SDK checks for it |
created (Unix seconds) | OpenAI | Hard-coded 1767225600 (same moment as created_at) |
owned_by: "cc-router" | OpenAI | OpenAI SDK uses it as the owner label |
Both SDKs use extra: allow and ignore unknown fields, so the same JSON is valid for both sides.
The outer wrapper is also a superset:
| Field | Source protocol |
|---|---|
object: "list" | OpenAI (list wrapper) |
data: [...] | Both |
has_more: false | Anthropic (page wrapper) |
first_id / last_id | Anthropic |
Request
GET /v1/models
- No query parameters, no body
- No authentication (allowlisted alongside
/healthand allOPTIONSpreflights) - Response:
200 OK,Content-Type: application/json
Response
The catalog currently has 18 entries (since 2026-05, 6 OpenAI Responses compat aliases were added):
{
"object": "list",
"data": [
{ "type": "model", "id": "model-opus", "display_name": "model-opus", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "model-sonnet", "display_name": "model-sonnet", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "model-haiku", "display_name": "model-haiku", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "claude-opus-4-7", "display_name": "claude-opus-4-7", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "claude-sonnet-4-6", "display_name": "claude-sonnet-4-6", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "claude-haiku-4-5", "display_name": "claude-haiku-4-5", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "anthropic/claude-opus-4-7", "display_name": "anthropic/claude-opus-4-7", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "anthropic/claude-sonnet-4-6", "display_name": "anthropic/claude-sonnet-4-6", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "anthropic/claude-haiku-4-5", "display_name": "anthropic/claude-haiku-4-5", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "anthropic/model-opus", "display_name": "anthropic/model-opus", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "anthropic/model-sonnet", "display_name": "anthropic/model-sonnet", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "anthropic/model-haiku", "display_name": "anthropic/model-haiku", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "gpt-5.5", "display_name": "gpt-5.5", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "gpt-5.4", "display_name": "gpt-5.4", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "gpt-5.4-mini", "display_name": "gpt-5.4-mini", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "openai/gpt-5.5", "display_name": "openai/gpt-5.5", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "openai/gpt-5.4", "display_name": "openai/gpt-5.4", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" },
{ "type": "model", "id": "openai/gpt-5.4-mini", "display_name": "openai/gpt-5.4-mini", "created_at": "2026-01-01T00:00:00Z", "object": "model", "created": 1767225600, "owned_by": "cc-router" }
],
"has_more": false,
"first_id": "model-opus",
"last_id": "openai/gpt-5.4-mini"
}
ID groups
| Group | IDs (18 total) | Resolves to |
|---|---|---|
| Anthropic short names | model-opus / model-sonnet / model-haiku | model-opus / model-sonnet / model-haiku |
| Anthropic versioned | claude-opus-4-7 / claude-sonnet-4-6 / claude-haiku-4-5 | model-opus / model-sonnet / model-haiku |
anthropic/ prefix (versioned) | anthropic/claude-{opus,sonnet,haiku}-* | Same as above |
anthropic/ prefix (short) | anthropic/model-{opus,sonnet,haiku} | Same as above |
| OpenAI aliases (v2.3+ new) | gpt-5.5 / gpt-5.4 / gpt-5.4-mini | model-opus / model-sonnet / model-haiku |
OpenAI openai/ prefix | openai/gpt-5.5 / openai/gpt-5.4 / openai/gpt-5.4-mini | Same as above |
Full resolution rules at Anthropic /v1/messages → Virtual model mapping.
Differences from the official /v1/models
| Aspect | cc-router | Official Anthropic / OpenAI |
|---|---|---|
| Data source | 18 fixed IDs (hard-coded) | Dynamically returns models accessible to that API key |
created_at / created | Hard-coded 2026-01-01T00:00:00Z / 1767225600 | Each model’s real release timestamp |
| Pagination | has_more is always false; no before_id / after_id | Paginated |
model-fallback | Not in the list (only triggered implicitly) | N/A |
| Fields | Anthropic + OpenAI superset | Each side only its own fields |
These 18 fixed IDs exist so that CC, any Anthropic client, or any OpenAI Responses client always sees the same virtual model list regardless of which upstream subscription cc-router routes to — they are cc-router’s virtual models. The real routing is decided at POST /v1/messages or POST /v1/responses time per the virtual-model mapping.
Examples
# List every virtual model ID
curl -s http://127.0.0.1:23456/v1/models | jq '.data[].id'
# Enumerate in OpenAI SDK style
curl -s http://127.0.0.1:23456/v1/models | jq '.data[] | {id, owned_by}'
# Enumerate in Anthropic SDK style
curl -s http://127.0.0.1:23456/v1/models | jq '.data[] | {id, display_name, created_at}'
GET /health
/health is cc-router’s own diagnostic endpoint, usually paired with /v1/models in the allowlist.
GET /health
- No parameters, no body, no auth
- Response:
200 OK,Content-Type: text/plain; charset=utf-8, body is the literal stringok
$ curl -i http://127.0.0.1:23456/health
HTTP/1.1 200 OK
content-type: text/plain; charset=utf-8
ok
Use cases:
- Startup scripts checking whether cc-router is listening
- Reverse proxy (nginx / caddy) upstream health checks
- Quick browser-based liveness check
/health does not return JSON and does not expose any internal state (version, subscription count, upstream health, etc.). Those flow over Tauri IPC to the UI and are not surfaced via HTTP.