Models & catalog
GET /v1/models lists models in the OpenAI format, with extra
catalog fields and a belarel block. Call it with your key to learn exactly which models that key
may call, what each one can do, and what it costs your organization.
Two lists, one endpoint
Section titled “Two lists, one endpoint”| Request | What you get | Price shown |
|---|---|---|
No Authorization header |
The public catalog: the models Belarel offers through the API | The platform’s public price list — or no price, when none is published |
| A valid key | Your key’s catalog: what this key may call | Your organization’s price |
Any Authorization header that is not a valid key answers 401 invalid_api_key. It never falls
back to the public list, so a typo in your key cannot be mistaken for an empty catalog.
# Your key's catalogcurl https://api.belarel.com/v1/models \ -H "Authorization: Bearer $BELAREL_API_KEY"
# The public catalogcurl https://api.belarel.com/v1/modelsimport osfrom openai import OpenAI
client = OpenAI(base_url="https://api.belarel.com/v1", api_key=os.environ["BELAREL_API_KEY"])
for model in client.models.list(): extra = model.model_extra.get("belarel", {}) print(model.id, extra.get("category"), extra.get("pricing"))import OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'https://api.belarel.com/v1', apiKey: process.env.BELAREL_API_KEY });
for await (const model of client.models.list()) { const belarel = (model as unknown as { belarel?: { category: string; pricing: unknown } }).belarel; console.log(model.id, belarel?.category, belarel?.pricing);}The keyed list needs the inference scope and counts toward the key’s requests per minute. It
never needs the key’s required tags:
those apply to model calls only.
What a model entry contains
Section titled “What a model entry contains”An entry from a keyed list (values are illustrative):
{ "id": "openai/gpt-5.4-mini", "object": "model", "created": 1755734400, "owned_by": "openai", "name": "GPT 5.4 Mini", "description": "GPT-5.4 Mini brings the strengths of GPT-5.4 to a faster, more efficient model…", "context_length": 400000, "architecture": { "input_modalities": ["text", "image"], "output_modalities": ["text"], "modality": "text+image->text" }, "top_provider": { "context_length": 400000, "max_completion_tokens": 4096 }, "supported_parameters": ["max_tokens", "max_completion_tokens", "temperature", "top_p", "stop", "seed", "presence_penalty", "frequency_penalty", "stream", "metadata", "tools", "tool_choice", "response_format", "reasoning_effort"], "belarel": { "display_name": "GPT 5.4 Mini", "category": "language", "context_window": 400000, "max_output_tokens": 4096, "capabilities": { "tools": true, "vision": true, "reasoning": true, "web_search": false, "structured_output": true }, "residency_zone": "any", "zero_retention_available": true, "byok_available": true, "pricing": { "currency": "USD", "input_usd_per_million": 0.825, "output_usd_per_million": 4.95, "credits_per_million_input": 825, "credits_per_million_output": 4950 } }}| Field | Meaning |
|---|---|
id |
The value to send as model. |
created |
Release date in Unix seconds; 0 when unknown. |
owned_by |
The model’s vendor, such as openai or anthropic. |
name, description, context_length, architecture |
Catalog information. |
top_provider.max_completion_tokens |
The most output you can ask for: the model’s limit, lowered to your key’s maximum output tokens. |
supported_parameters |
The request parameters forwarded to this model. tools and tool_choice appear only on models that can call tools, response_format only on models with structured output, reasoning_effort only on reasoning models; an embedding model lists input, encoding_format and dimensions. Belarel extensions such as web search and belarel_tags are not listed. Sending tools to a model that cannot call tools is refused with 400 model_lacks_tools, and structured output on a model without it with 400 model_lacks_structured_output. A forwarded parameter can still be ignored by the model’s provider: the answer then names it in belarel.ignored_parameters. The list uses Chat Completions names: /v1/responses refuses stop, seed, presence_penalty and frequency_penalty, and spells the others its own way (max_output_tokens, text.format, reasoning.effort). |
belarel.category |
language or embedding. |
belarel.capabilities |
Tools, vision, reasoning, web search and structured output. |
belarel.residency_zone |
The residency zone that applies. |
belarel.zero_retention_available |
Whether a zero-retention key can be served on this model. |
belarel.byok_available |
Whether a BYOK key can run this model on your own provider key. |
belarel.pricing |
Price per million input and output tokens, in USD and in credits. null when no price applies. A BYOK key is not billed this price: it pays the BYOK fee. Admission still uses this price for its worst-case estimate. |
Only input and output prices are listed, because those are the only components billed.
The list itself also carries a belarel object. On a keyed list: request_id, default_model,
default_embedding_model, sandbox, pricing_status (priced or no_price_book), and
empty_reason when the list is empty. On the public list: request_id, public: true,
pricing_status and pricing_basis: "public_price_book" — the price list the platform publishes
as its public price. When no public price list is published, pricing_status is no_price_book
and every model’s pricing is null: the public list never shows a price derived from anything
else.
Why a model is missing
Section titled “Why a model is missing”Your key’s catalog is the intersection of several decisions. A model appears only if all of them allow it:
- Belarel offers it through the API. Models available elsewhere in the platform are not automatically available to API keys.
- It is available to your organization, in its residency zone.
- Your contract allows it: allowed or blocked models, allowed providers.
- Your organization opened it to its API keys. Administrators choose this in the dashboard, under Governance → Models & Providers, per category: offer every model the platform makes available, offer only a selection, or turn the category off.
- Your key allows it, when the key has an allowed-model list.
- For embedding models: the key has the
embeddingsscope. A live BYOK key does not list embedding models, because embeddings run on the platform’s own provider account.
When the list is empty, belarel.empty_reason names the stage that removed everything:
empty_reason |
Meaning |
|---|---|
not_exposed |
No model is available to your organization through the API. |
residency |
No model is available in your organization’s residency zone. |
org_api_disabled |
Your organization turned this category off for API keys. |
org_api_restriction |
Your organization restricted API keys to a selection that matches nothing. |
404 or 403: what a refusal tells you
Section titled “404 or 403: what a refusal tells you”| Code | HTTP | Meaning | What to do |
|---|---|---|---|
model_not_found |
404 | The model does not exist, or is not available to your organization. Both cases give the same answer. | Pick an id from GET /v1/models. |
model_not_allowed |
403 | Your organization knows this model, but a rule refuses it: contract, residency, your organization’s API choice, or the key’s list. belarel.policy.decisions names the rule. |
Ask an administrator, or use another key. |
model_not_priced |
403 | A live key called a model that has no price yet. | Pick another model. |
A model your organization cannot see is never a 403: it answers exactly like a model that does
not exist, so the API never reveals models you have no access to.
Caching
Section titled “Caching”- The public list is sent with
Cache-Control: public, max-age=300, s-maxage=300— a change to the public price can take up to five minutes to show — and is limited to 60 requests per minute per IP address. Past that limit you get429withRetry-After. - A keyed list is sent with
Cache-Control: private, no-store. - Both carry
Vary: Authorization, so no shared cache serves one caller’s list to another.

