# API reference The AhoosAI API serves the Nimbus 2 Apex model in the format of OpenAI's Chat Completions API. Existing OpenAI client libraries work with it once the base URL and the key are changed. ## Base URL {#base-url} ```text https://ahoos-ai.site/services/api/v1 ``` | Method | Path | Purpose | |---|---|---| | `GET` | `/models` | Lists the available models. | | `POST` | `/chat/completions` | Generates a response to a conversation. | ## Authentication {#authentication} Every request to `/chat/completions` must carry an API key in the `Authorization` header: ```text Authorization: Bearer nim_YOUR_KEY ``` Keys are created and revoked on the [platform](https://ahoos-ai.site/services/platforme/) after signing in. A missing, unknown or revoked key is answered with status `401`. ## Example {#example} With the official OpenAI library for Python: ```python from openai import OpenAI client = OpenAI(base_url="https://ahoos-ai.site/services/api/v1", api_key="nim_YOUR_KEY") response = client.chat.completions.create( model="nimbus-2-apex", messages=[{"role": "user", "content": "Explain recursion in two sentences."}], extra_body={"thinking_level": 5}, ) print(response.choices[0].message.content) ``` ## Request {#request} `POST /chat/completions` accepts a JSON body of at most 16 MB. | Field | Type | Description | |---|---|---| | `messages` | array | Required. The conversation. Roles: `system`, `user`, `assistant`, `tool`. The last message must be `user` or `tool`. Content is a string or an array of text parts. | | `model` | string | Optional. If given, it must be `nimbus-2-apex`. | | `stream` | boolean | Streams the response as server-sent events. Default `false`. | | `max_tokens` | integer | The maximum length of the answer, from 16 to 8,192 tokens. Default 4,096. `max_completion_tokens` is accepted as well. The reasoning has its own budget, set by the reasoning level. | | `thinking_level` | integer | Reasoning level, 1 to 20. Default 5. Higher levels reason longer before answering. | | `reasoning_effort` | string | An alternative to `thinking_level`: `minimal` (1), `low` (3), `medium` (6) or `high` (12). | | `temperature_dial` | integer | Temperature, 1 to 10. Default 5. Lower values give more predictable answers. | | `temperature` | number | An alternative to `temperature_dial`: the value is multiplied by 5 and rounded (1.0 becomes 5). | | `tools` | array | Functions the model may call, in OpenAI's format. See [Tools](#tools). | | `tool_choice` | string or object | `auto` (default), `none`, `required`, or `{"type": "function", "function": {"name": "..."}}`. | | `nimbus_tools` | array | Tools that run on AhoosAI's side: `web_search`, `fetch_url`. See [Web search](#web-search). | | `web_search_options` | object | Turns on web search, as in OpenAI's API. Its contents are not used. | Other fields of OpenAI's format are accepted and ignored. Audio and files other than images are not supported. ## Response {#response} ```json { "id": "chatcmpl-...", "object": "chat.completion", "model": "nimbus-2-apex", "choices": [{ "index": 0, "finish_reason": "stop", "message": { "role": "assistant", "content": "The answer.", "reasoning_content": "The reasoning that preceded it." } }], "usage": {"prompt_tokens": 120, "completion_tokens": 310, "total_tokens": 430}, "nimbus": {"thinking_level": 5, "temperature": 5, "think_words": 160, "forced_close": false, "tokens_per_second": 28.4} } ``` - `reasoning_content` holds the model's reasoning; `content` holds the answer. - `finish_reason` is `stop`, `length` (the answer reached `max_tokens` or the context limit) or `tool_calls`. - `nimbus.forced_close` is `true` when the reasoning reached the ceiling of its level and was closed. ## Streaming {#streaming} With `"stream": true` the response is a sequence of server-sent events, each a line `data: {...}`: - `choices[0].delta.reasoning_content`: the next part of the reasoning. - `choices[0].delta.content`: the next part of the answer. - `choices[0].delta.tool_calls`: a tool call, sent complete in one event. - The last event carries `finish_reason`, `usage` and `nimbus`; the stream then ends with `data: [DONE]`. An error during the stream is sent as `data: {"error": {"message": "...", "type": "..."}}`. ## Tools {#tools} Tools follow OpenAI's function-calling format. When the model decides to use a tool, the response ends with `finish_reason: "tool_calls"` and one call in `message.tool_calls`. Run the function, add the assistant message and a `tool` message with the result to the conversation, and send it again: ```json {"role": "tool", "tool_call_id": "call_...", "content": "the function's result"} ``` The arguments of every call match the parameter schema of the tool. At most one call is returned per response. ## Images {#images} A user message may include images as OpenAI's `image_url` parts, given as a `data:` URL (PNG, JPEG, WebP or GIF) or an `https` address: ```json {"role": "user", "content": [ {"type": "text", "text": "What does this chart show?"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}} ]} ``` The model reads text only. Each image is first read by AhoosAI's image module, apex-imagexo-v1, which turns its text (read by OCR, in English and Persian) and its shapes (position, size and colour) into an XML scene; the model answers from that scene. It reads documents, screenshots, tables, forms, charts and diagrams well; it does not see the content of photographs, such as faces or objects. - Up to 4 images per request, each up to 8 MB. - The scenes of the last message's images are returned in `nimbus.images`. Send them as text in later turns instead of the image, so the image is not read again. ## Agents and tools {#agents} When a request carries your own `tools`, the model works as an agent: at every step it either calls one of your tools or replies, and the call's arguments follow the tool's parameter schema. To help it choose, tools with common names for writing, editing and reading files, listing files and running commands (for example `Write`, `Edit`, `Read`, `Glob`, `Bash`) are pointed out to it, and a project folder named in your system message (such as `Primary working directory: ...`) is given to it as the place for files. Keep the list of tools to what the task needs: a long list costs context and makes the right choice harder. ## Web search {#web-search} `"nimbus_tools": ["web_search", "fetch_url"]` (or `web_search_options`) lets the model search the web and read pages before it answers. These tools run on AhoosAI's servers; their calls are not returned to you, and the answer continues with their results. An answer may use up to six such steps. - In a stream, each step is reported in a `nimbus_tool` field: `{"id", "name", "arguments", "status"}`, where `status` is `running`, `done` or `error`. A finished search also lists its `results` (title, url, snippet). - In a non-streamed response, the finished steps are listed in `nimbus.tools`. Search queries are sent to public search engines, and pages are fetched from their own sites. ## Errors {#errors} Errors use OpenAI's format: `{"error": {"message": "...", "type": "..."}}`. | Status | Type | Meaning | |---|---|---| | 400 | `invalid_request_error` | The body is not valid JSON, has no messages, or does not end with a `user` or `tool` message. | | 401 | `authentication_error` | The key is missing, unknown or revoked. | | 404 | `invalid_request_error` | Unknown endpoint or model. | | 405 | `invalid_request_error` | The method is not allowed. | | 413 | `invalid_request_error` | The body is larger than 16 MB. | | 429 | `rate_limit_error` | The key's limit for the period is used up. The error includes `used`, `limit` and `period`. | | 502, 503 | `engine_error`, `engine_unavailable` | The model could not answer. Retry later. | ## Context and timing {#context} - A request may use up to 40,960 tokens in total: the conversation, the tool definitions, the reasoning and the answer. When it is longer, the descriptions of the tools are shortened first and then the oldest exchanges are left out; if the last exchange alone does not fit, the request fails. - The model starts on demand. After a period without requests, the first response can take up to two minutes. - Requests are answered one at a time; others wait for their turn. - A long request can take a minute before its first token. While a streamed response waits, the server sends comment lines (`: keepalive`) every few seconds, which SSE clients ignore.