# Chat completions

POST /v1/chat/completions takes OpenAI's request and returns OpenAI's response, from Macs on the network. What the pilot accepts, what it refuses and the errors it gives.

```endpoint
POST https://api.mlx.app/v1/chat/completions
```

Send your key as `Authorization: Bearer sk-mlx-…`. The request waits until the job is settled (see [How jobs run](/compute/how-jobs-run)), at most 45 seconds.

## Request

| Parameter | Type | Notes |
| --- | --- | --- |
| `model` | string | Required. `qwen2.5-0.5b-instruct` (see `GET /v1/models`). |
| `messages` | array | Required. `system`, `developer`, `user` and `assistant` messages with text content: a string or a list of `{"type": "text"}` parts. |
| `max_tokens` | integer | 1 to 120. Default 120. `max_completion_tokens` is the same field. |
| `temperature` | number | Leave it out or send `0`: the pilot decodes greedily, so two Macs can check each other. |
| `stream` | boolean | Leave it out or send `false`. |
| `n` | integer | Leave it out or send `1`. |

Other OpenAI fields (`top_p`, `seed`, `user`…) are accepted and ignored. Tools, images and audio are refused.

### How the messages are run

MLX Node 0.9 runs one user turn with the model's chat template. So:

- One `user` message, with optional `system` or `developer` messages, becomes the instructions followed by the message, in one turn.
- A conversation (an `assistant` message, or more than one `user` message) becomes a transcript in one turn: `User: …`, `Assistant: …`, ending with `Assistant:`.

The whole prompt is at most 4,000 characters, and at most 32 messages.

## Response

OpenAI's `chat.completion`, plus `mlx`:

| Field | Notes |
| --- | --- |
| `choices[0].message.content` | The answer. MLX Node returns at most 600 characters; a longer one comes back cut, with `finish_reason: "length"`. |
| `choices[0].finish_reason` | `stop` (the model ended), or `length` (it reached `max_tokens` or the text limit). |
| `usage` | Tokens you pay for. Prompt tokens are those the Macs report (template included), never more than a bound from the prompt's length; completion tokens are the fewer of the agreeing Macs' counts. |
| `mlx.verified` | `true` when two Macs of different wallets gave exactly this answer. `false` when the wait ran out with one answer only. |
| `mlx.macs` | How many Macs gave the answer returned. |
| `mlx.gpu_group` | The Macs' GPU group (`m1`, `m2`, `m3` for M3–M4, `m5` for M5 and newer). See [GPU groups](/protocol/jobs#gpu-groups). |
| `mlx.cost_usd` | What this request cost. |
| `mlx.ms` | From your request to the answer. |

The header `x-mlx-verified` carries `mlx.verified` too.

## Errors

Errors use OpenAI's shape: `{"error": {"message", "type", "param", "code"}}`.

| Status | `code` | When |
| --- | --- | --- |
| 400 | `invalid_request` | A field the pilot doesn't take; `param` names it. |
| 401 | `invalid_api_key` | No key, an unknown key or a revoked one. |
| 402 | `insufficient_credit` | Your available credit doesn't cover the most this request could cost. Add credit in the [console](/console). |
| 403 | `pilot_closed` | The pilot is closed to your wallet (it can be closed again, and then opened to listed wallets only). |
| 413 | `request_too_large` | The body is over 64,000 characters. |
| 429 | `rate_limit` | Over 60 requests a minute on this key. |
| 429 | `too_many_in_flight` | Over 8 requests running at once for this wallet. |
| 503 | `no_capacity` | No Mac answered in time, or the Macs disagreed. Nothing was charged; try again. |

## Other endpoints

```endpoint try
GET /api/v1/models
```

`GET /v1/models` lists the models, with the price and limits under `mlx`. `GET /v1/account` (with your key) returns your wallet, `credit`, `pending` (used since the last bill), `held` (requests running), `available`, and `pilot` (whether your wallet is on the list).
