> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lithosai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Chat Completions

<CodeGroup>
  ```python Python theme={null}
  import os

  from openai import OpenAI

  client = OpenAI(
      api_key=os.environ["LITHOSAI_API_KEY"],
      base_url="https://api.lithosai.cloud/v1",
  )

  response = client.chat.completions.create(
      model="moonshotai/Kimi-K3",
      messages=[{"role": "user", "content": "Hello"}],
  )
  print(response.choices[0].message.content)
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: process.env.LITHOSAI_API_KEY,
    baseURL: "https://api.lithosai.cloud/v1",
  });

  const response = await client.chat.completions.create({
    model: "moonshotai/Kimi-K3",
    messages: [{ role: "user", content: "Hello" }],
  });
  console.log(response.choices[0].message.content);
  ```

  ```bash curl theme={null}
  curl https://api.lithosai.cloud/v1/chat/completions \
    -H "Authorization: Bearer $LITHOSAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"model":"moonshotai/Kimi-K3","messages":[{"role":"user","content":"Hello"}]}'
  ```
</CodeGroup>

## Streaming

Set `stream: true` to receive tokens as they are generated.

<CodeGroup>
  ```python Python theme={null}
  import os

  from openai import OpenAI

  client = OpenAI(
      api_key=os.environ["LITHOSAI_API_KEY"],
      base_url="https://api.lithosai.cloud/v1",
  )

  stream = client.chat.completions.create(
      model="moonshotai/Kimi-K3",
      messages=[{"role": "user", "content": "Hi"}],
      stream=True,
  )
  for chunk in stream:
      print(chunk.choices[0].delta.content or "", end="")
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: process.env.LITHOSAI_API_KEY,
    baseURL: "https://api.lithosai.cloud/v1",
  });

  const stream = await client.chat.completions.create({
    model: "moonshotai/Kimi-K3",
    messages: [{ role: "user", content: "Hi" }],
    stream: true,
  });
  for await (const chunk of stream) {
    process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
  }
  ```

  ```bash curl theme={null}
  curl https://api.lithosai.cloud/v1/chat/completions \
    -H "Authorization: Bearer $LITHOSAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "moonshotai/Kimi-K3",
      "stream": true,
      "messages": [{ "role": "user", "content": "Hi" }]
    }'
  ```
</CodeGroup>

LithosAI includes a `usage` object in every chunk.

## Errors

Error response bodies specify the issue. `code` disambiguates the error, and for rate limits, `type` specifies the triggered limit.

```json theme={null}
{
  "error": {
    "message": "rate limit exceeded",
    "type": "input_tokens",
    "param": null,
    "code": "rate_limit_exceeded"
  }
}
```

| Status | Code                     | Meaning                                                                            | Retry                    |
| ------ | ------------------------ | ---------------------------------------------------------------------------------- | ------------------------ |
| `400`  | `invalid_json`           | The request body is not valid JSON.                                                | No                       |
| `400`  | `model_required`         | No model was named: the field is absent, empty, not a string, or there is no body. | No                       |
| `400`  | `invalid_stream_options` | `stream_options` is present but is not an object or null.                          | No                       |
| `400`  | `request_too_large`      | The prompt is larger than this model's input budget can ever hold.                 | No                       |
| `401`  | —                        | The key is missing, malformed, unknown or revoked.                                 | No                       |
| `402`  | `insufficient_quota`     | Your organization is out of credit.                                                | No                       |
| `404`  | `model_not_found`        | The catalog holds no such id. The message quotes it back.                          | No                       |
| `429`  | `rate_limit_exceeded`    | One of your three per-minute budgets is empty. `error.type` names which.           | Yes, after `retry-after` |
| `429`  | `provider_overloaded`    | The model is at capacity.                                                          | Yes, after `retry-after` |

Retry 429 and 5xx with exponential backoff and jitter. Prefer the delay we advise, `retry-after-ms` first and then `retry-after`, over an interval of your own. Do not retry 400, 401, 402 or 404, and do not retry anything carrying `x-should-retry: false`: the answer will not change until you do something about it.

A 429 means one of your three per-minute budgets is empty — see [Rate limits](/rate-limits) for what they are and how to read the headers that report them.
