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:

FieldSource protocolNotes
type: "model"AnthropicAnthropic SDK checks for it
idBothConsumed by both
display_nameAnthropicAnthropic SDK uses it for display
created_at (ISO string)AnthropicHard-coded "2026-01-01T00:00:00Z"
object: "model"OpenAIOpenAI SDK checks for it
created (Unix seconds)OpenAIHard-coded 1767225600 (same moment as created_at)
owned_by: "cc-router"OpenAIOpenAI 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:

FieldSource protocol
object: "list"OpenAI (list wrapper)
data: [...]Both
has_more: falseAnthropic (page wrapper)
first_id / last_idAnthropic

Request

GET /v1/models
  • No query parameters, no body
  • No authentication (allowlisted alongside /health and all OPTIONS preflights)
  • 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

GroupIDs (18 total)Resolves to
Anthropic short namesmodel-opus / model-sonnet / model-haikumodel-opus / model-sonnet / model-haiku
Anthropic versionedclaude-opus-4-7 / claude-sonnet-4-6 / claude-haiku-4-5model-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-minimodel-opus / model-sonnet / model-haiku
OpenAI openai/ prefixopenai/gpt-5.5 / openai/gpt-5.4 / openai/gpt-5.4-miniSame as above

Full resolution rules at Anthropic /v1/messages → Virtual model mapping.


Differences from the official /v1/models

Aspectcc-routerOfficial Anthropic / OpenAI
Data source18 fixed IDs (hard-coded)Dynamically returns models accessible to that API key
created_at / createdHard-coded 2026-01-01T00:00:00Z / 1767225600Each model’s real release timestamp
Paginationhas_more is always false; no before_id / after_idPaginated
model-fallbackNot in the list (only triggered implicitly)N/A
FieldsAnthropic + OpenAI supersetEach 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 string ok
$ 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.