Skip to content

Model Discovery (/v1/models)

/v1/models publishes every public model on the platform in machine-readable form. The endpoint requires no authentication: you can call it without an API key.

The response matches OpenAI's /v1/models schema exactly and adds the fields model selection actually needs: context window, pricing, modalities and accepted request parameters. Agent frameworks and model-routing libraries can read these fields and consume the catalog without a custom adapter.

Terminal window
curl https://llmtr.com/v1/models
{
"object": "list",
"data": [
{
"id": "aion-labs/aion-3.0",
"object": "model",
"created": 1783601983,
"owned_by": "aion-labs",
"name": "AionLabs: Aion 3.0",
"description": "AionLabs model for long-form fiction, roleplay and character-driven dialogue.",
"context_length": 131072,
"architecture": {
"modality": "text->text",
"input_modalities": ["text"],
"output_modalities": ["text"],
"tokenizer": null,
"instruct_type": null
},
"pricing": {
"prompt": "0.000003",
"completion": "0.000006",
"input_cache_read": "0.00000075"
},
"top_provider": {
"context_length": 131072,
"max_completion_tokens": 131072,
"is_moderated": false
},
"per_request_limits": null,
"supported_parameters": [
"include_reasoning",
"max_completion_tokens",
"max_tokens",
"parallel_tool_calls",
"reasoning",
"temperature",
"tool_choice",
"tools",
"top_p"
],
"supported_operations": ["CHAT_COMPLETIONS"],
"supported_endpoints": ["/v1/chat/completions"]
}
]
}
FieldMeaning
idCanonical identifier to send as model in a request.
context_lengthTotal window: input and output combined.
top_provider.context_lengthSame value as context_length.
top_provider.max_completion_tokensMaximum tokens in a single response. Not the same as context_length, and usually much smaller.
architecture.modalityinput->output form. Example: text+image->text.
pricing.*USD per single unit (per token, per image, per call). Not per million tokens.
supported_parametersRequest parameters this model accepts.
reasoningPresent only on reasoning models. An absent field means "no reasoning".
supported_operationsOperations the model can be invoked with.
supported_endpointsEndpoints the model can be called on.

pricing values are per token. Multiply by 1,000,000 for the per-million-token rate:

"prompt": "0.000003" -> 0.000003 * 1_000_000 = $3.00 / 1M tokens

Values are published as plain decimal strings, never in scientific notation.

For tiered models — where the rate depends on input length — pricing publishes the base tier. For models with a peak-hour surcharge it publishes the off-peak base rate. See the model detail page for the full rate table.

An absent field means the metric does not apply to that model; it does not mean "0". A video model billed per second, for example, carries no prompt field.

This field is never null.

Where the upstream enforces an output ceiling below the context window, the measured ceiling is published. The number is not inferred: each model is sent an oversized max_tokens and the limit is read out of the provider's own rejection, then recorded in the catalog.

Where no such ceiling has been measured, context_length is published instead. A response is part of the context, so that is always a valid upper bound — it is simply looser than the real ceiling for some models.

Publishing one of those two rather than null is deliberate: clients that see null substitute their own default, and 4096 is a common one. That advertised a model capable of 128K tokens as a 4K model.

For models with a measured ceiling the gateway enforces it before the request reaches the upstream; anything above it returns 400 with max_output_tokens_exceeded.

Filter with the supported_parameters query parameter. Multiple values are comma-separated and a model must support all of them to match.

Terminal window
curl "https://llmtr.com/v1/models?supported_parameters=tools"

Models supporting both reasoning and tool calling:

Terminal window
curl "https://llmtr.com/v1/models?supported_parameters=tools,reasoning"

Agent frameworks generally cannot drive a model without tools, so this filter is the recommended way to build a catalog.

Terminal window
curl https://llmtr.com/v1/models/openai/gpt-4o-mini

Returns the same schema as an entry in the list. An unknown identifier returns 404.

Because the endpoint is unauthenticated, resource use is governed by caching and a rate limit rather than a key:

  • Responses carry Cache-Control: public, max-age=300, s-maxage=300, stale-while-revalidate=3600, stale-if-error=86400. A shared cache keeps serving the response past the TTL while it refreshes behind the request.
  • Every response carries an ETag. Send it back as If-None-Match and you get 304 Not Modified with no body transferred when the catalog has not changed.
  • 120 requests per minute per IP. Exceeding it returns 429 with Retry-After.

A tool that polls the catalog regularly should store the ETag and send If-None-Match:

Terminal window
ETAG=$(curl -sI https://llmtr.com/v1/models | grep -i '^etag:' | cut -d' ' -f2- | tr -d '\r')
curl -s -o /dev/null -w "%{http_code}\n" \
-H "If-None-Match: $ETAG" \
https://llmtr.com/v1/models

The endpoint is open to every origin (Access-Control-Allow-Origin: *) and carries no credentials, so browser-based tools can read it with a plain fetch:

const response = await fetch("https://llmtr.com/v1/models");
const { data } = await response.json();
const toolCapable = data.filter((model) => model.supported_parameters?.includes("tools"));