# OpenAI-compatible API

https://modelportal.ai/docs/api-openai

Base URL `https://modelportal.ai/v1`, authenticated with `Authorization: Bearer YOUR_API_KEY`. Requests and responses follow the OpenAI API; only platform-specific fields and differences are listed here.

## POST /v1/chat/completions

Chat completions, with streaming.

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `model` | string | Yes | Model ID or alias |
| `messages` | array | Yes | Conversation messages: text, images and tool results |
| `stream` | boolean | No | `true` returns SSE; see [Streaming](/docs/streaming) |
| `stream_options` | object | No | `{"include_usage": true}` puts `usage` in the last chunk |
| `max_tokens`, `max_completion_tokens` | integer | No | Maximum output; when unset the platform fills one from the model limit and your available balance |
| `temperature`, `top_p`, `top_k`, `seed`, `n`, `logprobs`, `frequency_penalty`, `presence_penalty` | — | No | Sampling parameters; endpoints that lack one are not chosen. See [Parameter mapping](/docs/parameter-mapping) |
| `tools`, `tool_choice` | — | No | See [Tool calling](/docs/tool-calling) |
| `response_format` | object | No | `json_object` or `json_schema`; see [Structured output](/docs/structured-output) |
| `reasoning`, `reasoning_effort` | — | No | See [Reasoning](/docs/reasoning) |
| `provider` | object | No | Routing preferences, removed before forwarding; see [Provider selection](/docs/provider-routing) |

`user`, `safety_identifier` and `prompt_cache_key` are accepted as usual and removed before forwarding.

```json
{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "model": "MODEL_ID",
  "choices": [{"index": 0, "message": {"role": "assistant", "content": "Hello!"}, "finish_reason": "stop"}],
  "usage": {"prompt_tokens": 9, "completion_tokens": 4, "total_tokens": 13, "cost": 0.000013},
  "provider": "PROVIDER_ID",
  "attempts": [{"provider": "PROVIDER_ID", "endpoint": "ENDPOINT_ID", "status": 200, "cost": 0.000013}]
}
```

`model` in the response is the canonical model ID, and `usage` is the billed usage: input includes cache reads and writes, output includes reasoning. `usage.cost` is the charge for this call (USD); `provider` and `attempts` name the provider that served it and each attempt. When streaming, every chunk carries `provider`, and `attempts` and `usage.cost` are in the last chunk with `usage`. Response headers are in the [API overview](/docs/api-overview).

## POST /v1/responses

The Responses API, with streaming, for stateless calls only:

- `store` is always treated as `false`, and the response says `"store": false`.
- `previous_response_id`, `conversation` or `background` return 400 `stateful_not_supported`. For multiple turns, put the history in `input` yourself.
- `POST /v1/responses/input_tokens` counts input tokens for free.

Fields match OpenAI: `model` and `input` are required, maximum output is `max_output_tokens`, and structured output uses `text.format`.

## POST /v1/embeddings

Creates embeddings. The model needs the embedding capability, otherwise you get 404 `model_not_found`. `model` and `input` are required; `input` is a string or an array of strings.

## GET /v1/models

Returns the models this key may call that have an available endpoint; see the [Models API](/docs/api-models).

## Error codes

| HTTP | Code | Typical cause |
| --- | --- | --- |
| 400 | `invalid_request`, `unsupported_parameter`, `stateful_not_supported` | Invalid field, unsupported parameter, stateful call |
| 401 | `invalid_api_key`, `key_inactive` | Key problems |
| 402 | `insufficient_balance`, `budget_exceeded` | Balance or spend limit |
| 403 | `key_restricted`, `request_blocked` | Key restrictions, risk control |
| 404 / 410 | `model_not_found`, `model_retired` | Model unavailable |
| 422 | `no_eligible_endpoint` | No endpoint meets the constraints |
| 429 | `rate_limited`, `capacity_exhausted` | Rate limited or at capacity |
| 502 | `upstream_failed` | Upstream failure |

Error bodies are `{"error": {"message", "type", "code", "request_id"}}`; see [Common errors](/docs/errors) for details.
