# Chat completions

POST /v1/chat/completions - OpenAI-compatible chat, with streaming, tools, and lane fallback.

*ProxyLLM docs · https://proxyllm.ai/docs/api/chat-completions*

```
POST /v1/chat/completions
```

Auth: **[routing key](/docs/api/authentication)** (`pllm_…`). Requests run through the key's lanes in order; if a lane hits a rate limit, an exhausted usage window, or an error, the next lane serves the request.

## Request body

Standard OpenAI Chat Completions fields. The gateway reads these; anything else is ignored:

| Field             | Type             | Notes                                                                                                                                                           |
| ----------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`           | string, required | A tier (`flagship`, `mini`), a provider model (`gpt-4o-mini`), `codex/flagship`, or `bridge/claude-code`. Append `#node=<tag>` to tag the request for the logs. |
| `messages`        | array, required  | OpenAI message objects, including `tool` results.                                                                                                               |
| `stream`          | boolean          | Server-sent events. Streams only when the first lane is a direct API provider; Codex, Anthropic, and bridge lanes return a complete response instead.           |
| `temperature`     | number           | Forwarded.                                                                                                                                                      |
| `max_tokens`      | number           | Forwarded.                                                                                                                                                      |
| `top_p`           | number           | Forwarded.                                                                                                                                                      |
| `response_format` | object           | `json_object` and `json_schema` supported.                                                                                                                      |
| `tools`           | array            | OpenAI function tools.                                                                                                                                          |
| `tool_choice`     | string or object | Forwarded.                                                                                                                                                      |

```bash
curl https://api.proxyllm.ai/v1/chat/completions \
  -H "Authorization: Bearer pllm_your_routing_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flagship",
    "messages": [{"role": "user", "content": "Say hello."}]
  }'
```

## Response

```json
{
  "id": "chatcmpl-4f8d2c9a-...",
  "object": "chat.completion",
  "created": 1751500000,
  "model": "flagship",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello!"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 9,
    "completion_tokens": 3,
    "total_tokens": 12,
    "prompt_tokens_details": { "cached_tokens": 0 }
  }
}
```

When the model calls a tool, `message.content` is `null` and `message.tool_calls` carries the calls, exactly like OpenAI. With `"stream": true` the body is `text/event-stream`: OpenAI `chat.completion.chunk` lines ending with `data: [DONE]`.

Every response carries an `x-proxyllm-request-id` header; that id is the request's row in your [logs](https://proxyllm.ai/dashboard/logs).

## Errors

| Status | Code              | When                                                       |
| ------ | ----------------- | ---------------------------------------------------------- |
| 400    | (none)            | `model` or `messages` missing.                             |
| 400    | `no_providers`    | The key has no lanes.                                      |
| 400    | `no_credential`   | Streaming lane has no saved provider key.                  |
| 401    | `signup_required` | Missing key, or a raw provider key was sent.               |
| 401    | `invalid_api_key` | Unknown or revoked routing key.                            |
| 402    | `budget_exceeded` | Monthly budget exhausted.                                  |
| 405    | (none)            | Method other than POST or GET.                             |
| 502    | `upstream_error`  | All lanes failed; message carries the last provider error. |

Full list: [error codes](/docs/api#error-codes).
