---
title: "Get model"
description: "Fetch one curated model."
canonical: "https://pirouter.ai/docs/api/get-model"
last-updated: "2026-10-13"
lang: "en"
---

# Get model

Fetch one curated model.

`GET https://api.pirouter.ai/v1/models/{vendor}/{model}`

One model of the catalog, addressed by the two halves of its slug; the envelope is the same `{data}` object with a single Model in it.

A model that does not exist and a model that exists but is not listable (not active, unpriced, or past its deprecation grace) are the SAME `404 model_not_found`, and the message names neither. The two are deliberately indistinguishable: distinguishing them would turn this endpoint into a probe for models we have not published.

Public: no API key is required and none is read.

## Authorization

This endpoint is public: no API key is required and none is read.

## Path parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `vendor` | string | Yes | The left half of the model slug, matched exactly. |
| `model` | string | Yes | The right half of the model slug, matched exactly. |

## Response

`200` `application/json`

The model.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | object | Yes |  |
| `data.id` | string | Yes | Model slug in "vendor/model" form; the value accepted by createChatCompletion. |
| `data.canonical_slug` | string | Yes | The version-pinned slug this model resolves to; equal to `id` when the slug is already canonical. |
| `data.hugging_face_id` | string \| null | Yes | Always null. We hold no Hugging Face identifier; the key is present so ecosystem clients need no special case. |
| `data.name` | string | Yes | Display name, prefixed by the training organisation when we record one ("Anthropic: Claude Sonnet 4"); the bare display name otherwise. |
| `data.description` | string | Yes | English catalog description. One locale by design; the localized set is an internal surface. |
| `data.architecture` | object | Yes | What the model takes in and puts out, plus how it tokenizes. |
| `data.architecture.modality` | string | Yes | The input and output modality lists joined, "in+in->out+out". |
| `data.architecture.input_modalities` | string[] | Yes | Accepted input modalities in a fixed order (text, image, file, audio, video); text is always present. |
| `data.architecture.output_modalities` | string[] | Yes | Produced output modalities. The model's mode gives the first one (chat produces text; embedding and rerank name themselves), and declared output capabilities add the rest. |
| `data.architecture.tokenizer` | string | Yes | Tokenizer family, lowercase (claude, gpt, gemini, llama3 ...). Lowercase by our convention; ecosystem catalogs title-case the same families. |
| `data.architecture.instruct_type` | string \| null | Yes | Always null. We do not record prompt-template families. |
| `data.pricing` | object | Yes | List prices in USD as decimal strings to avoid float drift (a discount or a channel price in effect is on the endpoints view, in `pricing` there, never here): token classes are USD per token, per-item classes USD per call or item. The key names are the ecosystem's pricing vocabulary, mapped from the platform price classes by one table. Only the keys listed here may appear. The required keys are always present and read "0" when the model prices no such class; every OPTIONAL class the model does not price is absent rather than "0". |
| `data.pricing.prompt` | string | Yes | Input text, USD per token. Always present; "0" when the model has no input-text price (an image or audio model, say). |
| `data.pricing.completion` | string | Yes | Output text, USD per token. Always present; "0" when the model has no output-text price (an embedding model, say). |
| `data.pricing.request` | string | No | Flat charge per request, USD. |
| `data.pricing.image` | string | No | Input image, USD per image. |
| `data.pricing.image_output` | string | No | Output image, USD per token. |
| `data.pricing.audio` | string | No | Input audio, USD per token. |
| `data.pricing.audio_output` | string | No | Output audio, USD per token. |
| `data.pricing.input_audio_cache` | string | No | Cached input audio read, USD per token. |
| `data.pricing.web_search` | string | No | Built-in web search, USD per call. |
| `data.pricing.internal_reasoning` | string | No | Reasoning output tokens, USD per token. |
| `data.pricing.input_cache_read` | string | No | Cached input text read, USD per token. |
| `data.pricing.input_cache_write` | string | No | Cached input text write, USD per token (the vendor's default cache TTL). |
| `data.pricing.input_cache_write_1h` | string | No | Cached input text write at the one-hour TTL, USD per token; present only when the vendor prices that tier separately. |
| `data.pricing.image_token` | string | No | Input image, USD per token — the per-token tier, distinct from `image`, which is priced per whole image. |
| `data.pricing.overrides` | object[] | No | Context brackets above the base one, ascending by min_prompt_tokens (an INCLUSIVE lower bound on the prompt context length in tokens). A request is billed at the bracket with the largest min_prompt_tokens at or below its prompt context length; the base prices above cover everything below the first bracket. Absent when the model is single-priced. Each bracket renders exactly the classes IT declares — brackets replace, never merge — so a class missing from a bracket is a class that bracket does not restate, not one it prices at zero. |
| `data.pricing.overrides[].min_prompt_tokens` | integer | Yes | Inclusive lower bound on the prompt context length (tokens) from which this bracket applies. |
| `data.pricing.overrides[].prompt` | string | No |  |
| `data.pricing.overrides[].completion` | string | No |  |
| `data.pricing.overrides[].request` | string | No |  |
| `data.pricing.overrides[].image` | string | No |  |
| `data.pricing.overrides[].image_output` | string | No |  |
| `data.pricing.overrides[].audio` | string | No |  |
| `data.pricing.overrides[].audio_output` | string | No |  |
| `data.pricing.overrides[].input_audio_cache` | string | No |  |
| `data.pricing.overrides[].web_search` | string | No |  |
| `data.pricing.overrides[].internal_reasoning` | string | No |  |
| `data.pricing.overrides[].input_cache_read` | string | No |  |
| `data.pricing.overrides[].input_cache_write` | string | No |  |
| `data.pricing.overrides[].input_cache_write_1h` | string | No |  |
| `data.pricing.overrides[].image_token` | string | No |  |
| `data.top_provider` | object | Yes | Headline limits for the model itself, NOT for any one channel serving it. A projection of the model row: the per-channel view (which channel, at what price, with what ceiling) is a separate resource and arrives with GET /v1/models/{vendor}/{model}/endpoints. |
| `data.top_provider.is_moderated` | boolean | Yes | Always false at model level. Whether a given channel moderates is a channel fact and arrives with the endpoints view. |
| `data.top_provider.context_length` | integer | No | Maximum context window in tokens; absent when undeclared. |
| `data.top_provider.max_completion_tokens` | integer | No | Maximum completion tokens; absent when undeclared. |
| `data.per_request_limits` | object \| null | Yes | Always null. Per-request limits are a routing-time decision, not a catalog fact. |
| `data.supported_parameters` | string[] | Yes | Request parameters this model accepts, in the platform's frozen parameter order. Derived from the model's declared capabilities; a channel may accept fewer. |
| `data.default_parameters` | object | Yes | Sampling defaults applied when the request omits them; only the parameters the model declares appear. |
| `data.supported_voices` | array \| null | Yes | Always null. We do not catalog per-model voice inventories. |
| `data.knowledge_cutoff` | string \| null | Yes | Training-data cutoff as a calendar date (YYYY-MM-DD), or null when undeclared. Wire name of the platform's knowledge_cutoff_date. |
| `data.expiration_date` | string \| null | Yes | Vendor deprecation date (YYYY-MM-DD), or null when none is declared. This is the ONLY deprecation signal the catalog carries. A model stays listed for 30 days past this date — the routing surface refuses it from the date itself, so a listed model with a past expiration_date is one that answers errors — and disappears from the catalog entirely after the grace period. |
| `data.links` | object | Yes | Related catalog endpoints for this model, as paths relative to the API root. |
| `data.links.details` | string | Yes | The single-model endpoint for this row. |
| `data.created` | integer | No | Vendor release date as a Unix timestamp — midnight UTC of that calendar day. Absent when no release date is declared. |
| `data.context_length` | integer | No | Maximum context window in tokens. Absent when the model does not declare one. |
| `data.reasoning` | object | No | How this model handles extended thinking. The whole object is ABSENT for a model that does not reason — an all-false block would read as "reasons, but supports nothing", which is a different claim. |
| `data.reasoning.mandatory` | boolean | Yes | Reasoning cannot be switched off. |
| `data.reasoning.default_enabled` | boolean | Yes | Reasoning is on unless the request disables it. |
| `data.reasoning.supported_efforts` | string[] | Yes | Accepted effort levels, ordered high to low. |
| `data.reasoning.supports_max_tokens` | boolean | Yes | The model accepts a reasoning token budget instead of / besides an effort level. |
| `data.reasoning.default_effort` | string | No | Effort applied when the request names none; absent when the model has no default. |

## Errors

| Status | Code | Description |
| --- | --- | --- |
| `404` | `model_not_found` | No such listable model (`model_not_found`). |
| `default` | `internal_error` | Unexpected platform-side failure (`internal_error`). |

Error body

```json
{
  "error": {
    "code": "model_not_found",
    "message": "string"
  }
}
```

## Request samples

cURL

```bash
curl https://api.pirouter.ai/v1/models/anthropic/claude-sonnet-5.5
```

Python

```python
import requests

response = requests.get("https://api.pirouter.ai/v1/models/anthropic/claude-sonnet-5.5")
print(response.json())
```

TypeScript

```ts
const response = await fetch('https://api.pirouter.ai/v1/models/anthropic/claude-sonnet-5.5')
console.log(await response.json())
```

## Response sample

`200` `application/json`

```json
{
  "data": {
    "id": "anthropic/claude-sonnet-5.5",
    "canonical_slug": "anthropic/claude-sonnet-5.5",
    "hugging_face_id": null,
    "name": "string",
    "description": "string",
    "architecture": {
      "modality": "text+image+file->text",
      "input_modalities": [
        "string"
      ],
      "output_modalities": [
        "string"
      ],
      "tokenizer": "claude",
      "instruct_type": null
    },
    "pricing": {
      "prompt": "0.000003",
      "completion": "0.000015"
    },
    "top_provider": {
      "is_moderated": false
    },
    "per_request_limits": null,
    "supported_parameters": [
      "string"
    ],
    "default_parameters": {},
    "supported_voices": null,
    "knowledge_cutoff": "2025-03-01",
    "expiration_date": "2026-12-31",
    "links": {
      "details": "/v1/models/anthropic/claude-sonnet-5.5"
    }
  }
}
```
