API guides
OpenRouter compatibility
What the Numes API shares with OpenRouter, every intentional difference, and a checklist for migrating an existing integration.
On this page
- Endpoints
- Request shape
- Routing
- Pricing and currency
- Billing and delivery
- Model discovery
- Media
- Headers, errors and keys
- Unsupported OpenRouter features
- Additive fields
- Migrate from OpenRouter
- Change the base URL and key
- Pick the key currency
- Use one model ID per request
- Remove unsupported fields
- Convert routing preferences
- Update keys, balances and retries
- Next steps
Numes implements a subset of the OpenRouter API, which extends the OpenAI Chat Completions API. Request and response shapes, the stream format, the error envelope and the usage object follow OpenRouter, so OpenAI SDK and OpenRouter-style clients usually work after changing the base URL and key. Every deliberate difference is listed here; most exist so the API never appears to honor something it doesn't, or so billing stays exact. This page was last reviewed against OpenRouter's documentation in September 2026.
Endpoints
| Endpoint | Notes |
|---|---|
GET /models | Filters output_modalities and supported_parameters; four sort orders; prices in your key's currency |
GET /models/{author}/{slug}/endpoints | Per-endpoint pricing, limits, parameters and routing tag |
POST /chat/completions | Buffered and streamed; tools, structured outputs, reasoning, caching, multimodal input |
GET /generation | Its own field set; see Billing and delivery |
POST /embeddings | Text, token and multimodal inputs |
POST /images | Buffered or streamed, base64 output |
POST /audio/speech | MP3, PCM and WAV |
POST /audio/transcriptions | Multipart or JSON input; json and verbose_json output |
GET /key | The current key's metadata; inference or management key |
GET, POST /keys; GET, PATCH, DELETE /keys/{hash} | Management key; manages inference keys only |
GET /credits | Management key; requires currency=USD or currency=BTC |
POST /e2ee/resolve, POST /e2ee/chat/completions | Extension for encrypted inference |
Request shape
| Behavior | OpenRouter | Numes | What to do |
|---|---|---|---|
| Unknown request fields | Generally ignored | Chat forwards ordinary generation hints; gateway-owned controls have separate validation | Use Parameters to check which controls the gateway handles |
| Models per request | models array falls back across models | Exactly one: model, or models with one entry | Implement model fallback in your client |
| Requested features | Unsupported parameters may be ignored | Tools, response_format, reasoning, cache directives and output modalities are hard requirements; no matching endpoint returns 503 provider_unavailable. Unsupported sampling parameters are omitted unless provider.require_parameters is true | Check supported_parameters before sending a feature |
| Output limit | Large values accepted | max_tokens and max_completion_tokens must be 1–131,072, otherwise 400. When omitted, the endpoint's own maximum applies | Use the model's top_provider.max_completion_tokens or less |
| Unknown model | An error naming the invalid model | 503 provider_unavailable, the same response as any request no endpoint can serve | Copy IDs from GET /models |
Message name | Accepted on any role | Accepted only on tool messages, and not forwarded | Remove name from other roles |
| File references | OpenAI-style file_id | file_id is rejected; chat audio must be inline base64 | Send PDFs as HTTPS or data: URLs and audio as base64 |
X-Request-Id | No uniqueness rule | Unique per account; reusing a recorded ID returns 400 | Omit it, or send a new value on every call, including retries |
| Browser requests | Allowed | Third-party origins get 403 cors_forbidden | Call the API from your server |
Routing
| Behavior | OpenRouter | Numes | What to do |
|---|---|---|---|
max_price units | Dollars per million tokens | Per unit (per token for text) in the key's currency; also caps separately priced reasoning and cache-write rates | Divide OpenRouter values by 1,000,000; use satoshis for BTC keys |
| Default routing | Price-weighted load balancing | Balances price, reliability and performance across eligible endpoints | Use provider.sort or order for a deterministic order |
provider.sort | price, throughput, latency, or an object | The three strings plus quality; no object form | Use the string form |
| Data residency | Separate EU base URL | provider.regions filters on endpoint-declared countries and regions | Send provider.regions |
Other provider fields | Percentile performance preferences, distillation filters and more | Accepted: order, allow_fallbacks, require_parameters, data_collection, zdr, only, ignore, quantizations, regions, sort and max_price. Others return 400 | Remove the rest |
Pricing and currency
| Behavior | OpenRouter | Numes | What to do |
|---|---|---|---|
| Currency | USD credits | Each key has a fixed currency, USD or BTC (in satoshis), for prices, costs and limits. No conversion | Pick the currency when you create the key |
| Creating keys | POST /keys without currency | billing_currency is required and can't be changed | Add billing_currency to create calls |
| Upstream cost | Aggregate plus prompt and completion split | Aggregate cost_details.upstream_inference_cost only | Use GET /generation for detail |
| Media usage | Some media counted as tokens | Image counts and audio seconds are separate usage fields | Read input_images, output_images, input_audio_seconds, output_audio_seconds |
| Prompt caching | Provider cache pricing | Cache rates never exceed the prompt rate | No action needed |
| Money in JSON | Numbers | Numbers written from exact decimals; prices are decimal strings | Parse as decimals when you add amounts |
Billing and delivery
| Behavior | OpenRouter | Numes | What to do |
|---|---|---|---|
| What you pay for | Varies by request type | Output delivered to you; GET /generation is the authoritative record | No action needed |
| Usage opt-in | usage.include, stream_options.include_usage | Usage is always returned; both fields are accepted and ignored | Read usage from every response |
| Final usage chunk | Can include a choice | choices: [] with usage | Skip chunks with an empty choices array when reading content |
| Fallback | Across providers and models | Across endpoints of the requested model, only before output reaches you | Treat mid-stream errors as final |
| Stream keep-alives | : OPENROUTER PROCESSING comments | None | Use a read timeout of several minutes |
GET /generation fields | OpenRouter's generation stats | Outcome, delivery state, exact costs and a breakdown of each endpoint tried; never content | See Usage accounting |
Model discovery
| Behavior | OpenRouter | Numes | What to do |
|---|---|---|---|
| Which models are listed | The whole catalogue | Only models with an available endpoint priced in your key's currency | Expect different lists for USD and BTC keys |
| One model row | Model-level summary | Describes one representative endpoint | Use the endpoints API for every endpoint |
| Filters and sorting | Many filters and search | output_modalities, supported_parameters and four sorts; no pagination or search | Filter client-side |
| Prices | Rates you pay | Provider rates, which are what requests are charged | Expect usage.cost to equal upstream_inference_cost |
Media
| Behavior | OpenRouter | Numes | What to do |
|---|---|---|---|
| PDFs | Parsed for any model | Routed only to endpoints that accept file input | Pick a model whose endpoints list file input |
| Generated images | URL or base64 | Base64 only | Decode b64_json |
| Transcription | Several formats | json and verbose_json; prompt accepted and ignored; no word or segment timestamps | Use json |
| Image or audio output from chat | Supported on some models | Only from endpoints that declare the output; otherwise 503 | Use /images and /audio/speech |
Headers, errors and keys
| Behavior | OpenRouter | Numes | What to do |
|---|---|---|---|
X-OpenRouter-Metadata | enabled | enabled, or true; chat only; never on error bodies | Send enabled |
HTTP-Referer, X-Title | App attribution and rankings | HTTP-Referer groups your own Activity; X-Title is ignored; neither reaches providers | No action needed |
| Error metadata | Can include provider name and raw upstream error | error_type only; upstream messages are never relayed | Branch on status and error_type |
| Key metadata | Includes is_free_tier, is_provisioning_key, rate_limit and BYOK fields | Those are omitted; billing_currency is added | Don't depend on the omitted fields |
| Management keys | Created through the API | Created and revoked in the dashboard | Create them in Keys |
| Structured outputs | Provider-enforced | Schema adherence depends on the model and endpoint | Validate the parsed result |
Unsupported OpenRouter features
Unimplemented endpoints return 404. Chat rejects settings that would bypass gateway execution or privacy controls.
Other API shapes: the Responses API, the Anthropic Messages API, legacy
/completionsand video generation.Account features: bring-your-own-key, presets, OAuth sign-in for third-party apps, the analytics API, workspaces, guardrails and customer webhooks.
Model shortcuts: variants such as
:free,:nitro,:floor,:onlineor:thinking;~author/modelaliases; multi-model fallback throughmodelsorroute.Plugins and transforms: the
pluginsfield, response caching andtransforms. For web search, use the hostedweb_searchtool; see Hosted tools.Search and choices:
web_search_optionsand requesting more than one choice withnreturn400.Other OpenAI fields:
store: true,service_tierother thandefault, and legacyfunctionsandfunction_call(usetoolsandtool_choice).
Penalty samplers (frequency_penalty, presence_penalty, repetition_penalty) and min_p or top_a are forwarded as numeric hints. Chat also forwards ordinary hints such as logit_bias, logprobs, top_logprobs, prediction and verbosity to compatible sources. Token log probabilities are not returned in the public response. Use require_parameters if the endpoint must declare the hints you send.
Additive fields
Responses carry some fields OpenRouter doesn't define. Clients that ignore unknown fields are unaffected.
| Field | Where | Meaning |
|---|---|---|
billing_currency | Models, endpoints, key metadata | The currency of every price and amount for this key |
usage_meter | Models, endpoints, usage | Identifies the usage contract that prices the offer; treat it as opaque |
token_count_basis | Usage | provider_native when token counts come from the provider, gateway_estimate otherwise |
native_tokens | Usage | The provider's token counts, including reasoning and cache classifications |
gateway_billing_units | Usage | Billing quantities on some older requests; absent on current offers |
hosted_tool_usage | Usage | Quantities of each hosted tool used |
maximum_output_length, embeddings, supported_hosted_tools, reasoning | Models, endpoints | Output ceiling, embedding support, hosted tools and reasoning capabilities |
access | Models | Present on models shared with one of your organizations |
pricing_basis, reference, usd_rates | Model pricing | Present on BTC prices that track a USD price |
provider.regions, provider.sort: "quality" | Chat request | Routing extensions; see Parameters |
Migrate from OpenRouter
Change the base URL and key
Point your client at
https://api.numes.ai/api/v1and use an inference key from Keys. With the OpenAI SDK, only the base URL and API key change.Pick the key currency
Choose
USDorBTCwhen you create the key. Prices,usage.costand limits come back in that currency, and BTC amounts are satoshis. Fund the matching balance in Billing.Use one model ID per request
Check IDs against
GET /models. Remove variant suffixes and~aliases, and replace multi-modelmodelsarrays with one model. For model fallback, catch503 provider_unavailableand retry with your next model.Remove unsupported fields
Remove blocked settings such as
pluginsandweb_search_options. For reasoning, sendreasoning: {effort}. A forwarded hint can still be refused by the endpoint; check the field named in the error against Parameters.Update keys, balances and retries
Add
billing_currencytoPOST /keyscalls, create management keys in the dashboard, and addcurrencytoGET /creditscalls. Send a freshX-Request-Idon every retry, or none, and retry only before a stream starts; see Retry strategy.