{"openapi":"3.1.0","info":{"title":"openaiapi","version":"1.0.0","summary":"OpenAI-compatible API to many language models, prepaid in rubles.","description":"The subset of the OpenAI API the gateway serves: `GET /models`, `GET /models/{model}`, `POST /chat/completions`, `POST /responses`, `POST /embeddings`. Any OpenAI SDK works with `base_url` https://api.openaiapi.ru/v1.\n\nText only: image, audio and file inputs are refused with 400. Nothing is stored on the server: `store` is ignored, `previous_response_id`, `conversation` and `background` are refused, send the full history with every request. Built-in tools (web search, file search, code interpreter, MCP) are refused, tools of type `function` and `custom` work; on /responses a `web_search` tool (Codex CLI adds one to every request) is dropped instead, and the model answers without searching.\n\nA request body may carry only the fields listed in its schema; fields without a note are passed to the model as is. Any other field is refused with 400 `invalid_request` and `param` naming it, unless its value is null. Model IDs are `vendor/model`, e.g. `openai/gpt-5.6-sol`; the bare name (`gpt-5.6-sol`) works too; the live list with prices is https://openaiapi.ru/api/catalog (no key) or `GET /models`. A model has several providers and one price, whichever provider answers.\n\nLimits per key by default: 600 requests per minute and 64 concurrent requests, counted by each gateway server on its own: a guard against runaway loops, not a metered quota. Request body up to 8 MiB. Every response, errors included, carries `x-request-id`. A 429 for balance or a key spending cap carries `x-should-retry: false`: a retry gets the same answer.","contact":{"name":"openaiapi support","email":"support@openaiapi.ru","url":"https://openaiapi.ru/docs"}},"externalDocs":{"description":"Docs (Russian)","url":"https://openaiapi.ru/docs"},"servers":[{"url":"https://api.openaiapi.ru/v1"}],"security":[{"bearer":[]}],"paths":{"/models":{"get":{"operationId":"listModels","summary":"List models","description":"Every model with its kind, context length and price. The same data without a key: https://openaiapi.ru/api/catalog.","responses":{"200":{"description":"Models.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/models/{model}":{"get":{"operationId":"retrieveModel","summary":"Retrieve a model","description":"One model of `GET /models` by id; the id keeps its slash (`/models/openai/gpt-5.6-sol`).","parameters":[{"name":"model","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ModelId"}}],"responses":{"200":{"description":"The model.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Model"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/chat/completions":{"post":{"operationId":"createChatCompletion","summary":"Create a chat completion","description":"Chat Completions as in the OpenAI API, for models of kind `chat`. With `stream: true` the answer is Server-Sent Events of `chat.completion.chunk` objects ending with `data: [DONE]`. As with OpenAI, reasoning text is not returned; its size is `usage.completion_tokens_details.reasoning_tokens`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}}},"responses":{"200":{"description":"The completion, or an SSE stream when `stream` is true.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletion"}},"text/event-stream":{"schema":{"type":"string","description":"`data: {chat.completion.chunk}` events, then `data: [DONE]`. If the model stream breaks: `data: {\"error\":{\"code\":\"stream_interrupted\",...}}` and the stream closes."}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/BadRequest"},"413":{"$ref":"#/components/responses/TooLarge"},"422":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/TooMany"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/Timeout"}}}},"/responses":{"post":{"operationId":"createResponse","summary":"Create a model response","description":"Responses API without server-side state, for models of kind `chat`: `store` is forced to false; `previous_response_id`, `conversation` and `background: true` are refused with 400. Keep reasoning between turns with `include: [\"reasoning.encrypted_content\"]` and send it back in `input`; reasoning items sent back without `encrypted_content` are dropped, as they refer to storage. With `stream: true` the answer is Server-Sent Events of Responses events (`response.created` ... `response.completed`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponsesRequest"}}}},"responses":{"200":{"description":"The response object, or an SSE stream when `stream` is true.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"text/event-stream":{"schema":{"type":"string","description":"`event: <type>` / `data: {...}` Responses events. If the model stream breaks: `event: error` with `{\"type\":\"error\",\"code\":\"stream_interrupted\",\"param\":null,\"sequence_number\":...}` and the stream closes."}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/BadRequest"},"413":{"$ref":"#/components/responses/TooLarge"},"422":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/TooMany"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/Timeout"}}}},"/embeddings":{"post":{"operationId":"createEmbedding","summary":"Create embeddings","description":"Embeddings for models of kind `embedding`. No streaming.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingsRequest"}}}},"responses":{"200":{"description":"Embeddings.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/BadRequest"},"413":{"$ref":"#/components/responses/TooLarge"},"422":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/TooMany"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/Timeout"}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"API key from the cabinet (https://openaiapi.ru/c/): `sk-` followed by 64 hex characters. `Authorization: Bearer sk-...`."}},"headers":{"RequestId":{"description":"Request id; quote it to support.","schema":{"type":"string","format":"uuid"}},"RetryAfter":{"description":"Seconds to wait before retrying, when known.","schema":{"type":"string"}}},"responses":{"BadRequest":{"description":"`invalid_request`: the body is not a JSON object, a field is unknown or has an unsupported value (`param` names it), or the provider refused the request. `context_length_exceeded`: the input is too long for the model. `content_policy_violation`: refused by the content policy. These provider refusals keep the provider's status, which may also be 404, 409, 413 or 422.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"`invalid_api_key`: no key, a malformed key, an unknown or revoked key.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"`model_not_found` (`param: model`): no such model, or not of the kind this endpoint serves (a chat model on /embeddings and vice versa). `not_found`: unknown path or wrong HTTP method.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooLarge":{"description":"`request_too_large`: the body is over 8 MiB. A provider's 413 refusal keeps its own code, see 400.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooMany":{"description":"`rate_limit_exceeded` (type `requests`): the key is over its requests per minute or concurrent requests, or the model provider is rate limiting; retry later. `insufficient_quota`: the balance does not cover the request or the key reached its spending cap; top up or lower the output limit, a retry will not help.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadGateway":{"description":"`upstream_unavailable` or `upstream_error`: every provider of the model failed; retry later.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"`service_unavailable`: overload, billing unavailable, requests paused, or the model has no provider available right now; retry later. May carry `Retry-After`.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Timeout":{"description":"`upstream_timeout`: no provider started answering in time; retry later.","headers":{"x-request-id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"OpenAI error format.","required":["error"],"properties":{"error":{"type":"object","required":["message","type","code"],"properties":{"message":{"type":"string","description":"Human-readable, in English."},"type":{"type":"string","description":"Error class.","enum":["invalid_request_error","requests","insufficient_quota","server_error"]},"code":{"type":"string","description":"Machine-readable code.","enum":["invalid_request","invalid_api_key","model_not_found","not_found","request_too_large","context_length_exceeded","content_policy_violation","rate_limit_exceeded","insufficient_quota","upstream_unavailable","upstream_error","service_unavailable","upstream_timeout"]},"param":{"type":["string","null"],"description":"The request field the error is about, or null."}}}},"example":{"error":{"message":"Insufficient balance for this request. Top up in the cabinet or lower the output limit.","type":"insufficient_quota","code":"insufficient_quota","param":null}}},"ModelId":{"type":"string","description":"`vendor/model`, e.g. `openai/gpt-5.6-sol`. Live list: https://openaiapi.ru/api/catalog.","examples":["openai/gpt-5.6-sol"]},"ModelList":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Model"}}}},"Model":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/ModelId"},"object":{"const":"model"},"created":{"type":"integer","description":"Always 0."},"owned_by":{"type":"string","description":"The vendor part of the id."},"name":{"type":"string","description":"Display name."},"kind":{"type":"string","description":"Which endpoint serves the model: `chat` for /chat/completions and /responses, `embedding` for /embeddings.","enum":["chat","embedding"]},"context_length":{"type":["integer","null"],"description":"Context window, tokens; null when unknown."},"pricing":{"type":"object","description":"The price you are charged, whichever provider answers.","properties":{"input":{"type":"number","description":"RUB per 1M input tokens."},"cached":{"type":"number","description":"RUB per 1M cached input tokens."},"output":{"type":"number","description":"RUB per 1M output tokens; 0 for embeddings."},"unit":{"const":"RUB/1M"}}}}},"Tool":{"type":"object","description":"Only `function` and `custom` tools, run by the client. Built-in tools (file search, code interpreter, MCP, ...) are refused with 400, param `tools`; so is web search on /chat/completions, while on /responses a `web_search` tool is dropped.","required":["type"],"properties":{"type":{"enum":["function","custom"]}}},"ToolChoice":{"description":"`none`, `auto`, `required`, an object naming a `function` or `custom` tool, or `allowed_tools` listing only such tools. Anything else: 400, param `tool_choice`.","oneOf":[{"enum":["none","auto","required"]},{"type":"object"}]},"ChatMessage":{"type":"object","required":["role"],"properties":{"role":{"enum":["developer","system","user","assistant","tool","function"]},"content":{"description":"A string or an array of parts of type `text` or `refusal`. Image, audio and file parts are refused with 400.","oneOf":[{"type":"string"},{"type":"null"},{"type":"array","items":{"type":"object","required":["type"],"properties":{"type":{"enum":["text","refusal"]},"text":{"type":"string"}}}}]},"name":{"type":"string"},"tool_calls":{"type":"array","items":{"type":"object"}},"tool_call_id":{"type":"string"}}},"ChatRequest":{"type":"object","required":["model","messages"],"additionalProperties":false,"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"messages":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ChatMessage"}},"max_completion_tokens":{"type":"integer","description":"Output limit, 1 to the model maximum. Absent: no limit, the model answers in full. Give either this or `max_tokens`, not both.","minimum":1},"max_tokens":{"type":"integer","description":"Same as `max_completion_tokens`.","minimum":1},"stream":{"type":"boolean","description":"Answer as SSE."},"stream_options":{"type":"object","description":"`include_usage: true` adds the final chunk with `usage` and empty `choices`. Ignored without `stream`.","properties":{"include_usage":{"type":"boolean"}}},"tools":{"type":"array","items":{"$ref":"#/components/schemas/Tool"}},"tool_choice":{"$ref":"#/components/schemas/ToolChoice"},"parallel_tool_calls":{"type":"boolean"},"functions":{"type":"array","items":{"type":"object"},"description":"Legacy function calling."},"function_call":{"description":"Legacy function calling."},"response_format":{"type":"object","description":"`text`, `json_object` or `json_schema`, passed to the model."},"temperature":{"type":"number","description":"Sampling temperature, passed to the model."},"top_p":{"type":"number","description":"Nucleus sampling, passed to the model."},"frequency_penalty":{"type":"number"},"presence_penalty":{"type":"number"},"stop":{},"seed":{"type":"integer"},"n":{"type":"integer","description":"Only 1.","enum":[1]},"logit_bias":{"type":"object"},"logprobs":{"type":"boolean"},"top_logprobs":{"type":"integer"},"reasoning_effort":{"type":"string"},"verbosity":{"type":"string"},"prediction":{"type":"object","description":"Predicted output; rejected prediction tokens are billed as output tokens."},"modalities":{"type":"array","items":{"const":"text"},"description":"Only `[\"text\"]`."},"store":{"type":"boolean","description":"Ignored: nothing is stored."},"service_tier":{"type":"string","description":"Only `auto` or `default`; any other value is refused (400, param `service_tier`).","enum":["auto","default"]},"metadata":{"type":"object","description":"Ignored: nothing is stored."},"user":{"type":"string","description":"End-user id, passed to the model."},"safety_identifier":{"type":"string","description":"End-user id for abuse detection, passed to the model."},"prompt_cache_key":{"type":"string","description":"Accepted, but replaced: the gateway sends its own cache key derived from your account and the conversation, so turns of one conversation hit the same prompt cache."},"prompt_cache_retention":{"type":"string","description":"Accepted and ignored: the cache is keyed by the gateway (see `prompt_cache_key`)."},"prompt_cache_options":{"type":"object","description":"Accepted and ignored, as `prompt_cache_retention`."}}},"ChatCompletion":{"type":"object","description":"As in the OpenAI API. `id` is ours (`chatcmpl-...`), `model` is the id you sent, `system_fingerprint` is absent.","properties":{"id":{"type":"string"},"object":{"const":"chat.completion"},"created":{"type":"integer"},"model":{"$ref":"#/components/schemas/ModelId"},"choices":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"message":{"$ref":"#/components/schemas/ChatMessage"},"finish_reason":{"type":"string"}}}},"usage":{"$ref":"#/components/schemas/ChatUsage"}}},"ChatUsage":{"type":"object","description":"Tokens you are billed for.","properties":{"prompt_tokens":{"type":"integer"},"completion_tokens":{"type":"integer"},"total_tokens":{"type":"integer"},"prompt_tokens_details":{"type":"object","properties":{"cached_tokens":{"type":"integer"}}}}},"ResponsesRequest":{"type":"object","required":["model","input"],"additionalProperties":false,"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"input":{"description":"A string or an array of items. Item types: `message` (or no type), `function_call`, `function_call_output`, `custom_tool_call`, `custom_tool_call_output`, `reasoning`. Content parts: `input_text`, `output_text`, `text`, `refusal`, `summary_text`, `reasoning_text`. A `reasoning` item without `encrypted_content` is dropped. Other items (`item_reference`, a bare `{\"id\"}`, built-in tool calls) and parts (`input_image`, `input_file`, audio) are refused with 400.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"object"}}]},"instructions":{"type":"string","description":"System instructions."},"max_output_tokens":{"type":"integer","description":"Output limit, 1 to the model maximum. Absent: no limit, the model answers in full.","minimum":1},"stream":{"type":"boolean","description":"Answer as SSE."},"stream_options":{"type":"object"},"tools":{"type":"array","items":{"$ref":"#/components/schemas/Tool"}},"tool_choice":{"$ref":"#/components/schemas/ToolChoice"},"parallel_tool_calls":{"type":"boolean"},"max_tool_calls":{"type":"integer"},"text":{"type":"object","description":"Output format and verbosity, passed to the model."},"reasoning":{"type":"object","description":"Reasoning effort and summary, passed to the model."},"include":{"type":"array","items":{"type":"string"},"description":"E.g. `[\"reasoning.encrypted_content\"]` for stateless multi-turn reasoning."},"truncation":{"type":"string"},"temperature":{"type":"number","description":"Sampling temperature, passed to the model."},"top_p":{"type":"number","description":"Nucleus sampling, passed to the model."},"top_logprobs":{"type":"integer"},"store":{"type":"boolean","description":"Always sent as false: nothing is stored."},"service_tier":{"type":"string","description":"Only `auto` or `default`; any other value is refused (400, param `service_tier`).","enum":["auto","default"]},"metadata":{"type":"object"},"user":{"type":"string","description":"End-user id, passed to the model."},"safety_identifier":{"type":"string","description":"End-user id for abuse detection, passed to the model."},"prompt_cache_key":{"type":"string","description":"Accepted, but replaced: the gateway sends its own cache key derived from your account and the conversation, so turns of one conversation hit the same prompt cache."},"prompt_cache_retention":{"type":"string","description":"Accepted and ignored: the cache is keyed by the gateway (see `prompt_cache_key`)."},"prompt_cache_options":{"type":"object","description":"Accepted and ignored, as `prompt_cache_retention`."},"previous_response_id":{"type":"null","description":"Not supported: 400, param `previous_response_id`. Send the full input with every request."},"conversation":{"type":"null","description":"Not supported: 400, param `conversation`. Send the full input with every request."},"background":{"const":false,"description":"Not supported: `true` is refused with 400."}}},"Response":{"type":"object","description":"As in the OpenAI API, with `model` as you sent it.","properties":{"id":{"type":"string"},"object":{"const":"response"},"status":{"type":"string"},"model":{"$ref":"#/components/schemas/ModelId"},"output":{"type":"array","items":{"type":"object"}},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"total_tokens":{"type":"integer"},"input_tokens_details":{"type":"object","properties":{"cached_tokens":{"type":"integer"}}}}}}},"EmbeddingsRequest":{"type":"object","required":["model","input"],"additionalProperties":false,"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"input":{"description":"A string, a non-empty array of strings, or arrays of token ids.","oneOf":[{"type":"string"},{"type":"array","minItems":1,"items":{"oneOf":[{"type":"string"},{"type":"integer"},{"type":"array","items":{"type":"integer","minimum":0}}]}}]},"encoding_format":{"type":"string","enum":["float","base64"]},"dimensions":{"type":"integer","description":"Passed to the model, if it supports it."},"user":{"type":"string","description":"End-user id, passed to the model."}}},"EmbeddingList":{"type":"object","properties":{"object":{"const":"list"},"model":{"$ref":"#/components/schemas/ModelId"},"data":{"type":"array","items":{"type":"object","properties":{"object":{"const":"embedding"},"index":{"type":"integer"},"embedding":{"oneOf":[{"type":"array","items":{"type":"number"}},{"type":"string"}]}}}},"usage":{"type":"object","properties":{"prompt_tokens":{"type":"integer"},"total_tokens":{"type":"integer"}}}}}}}}
