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

# OpenAI Compatible API

> Connect your existing OpenAI SDK code to Air API

Air API provides an endpoint that is compatible with the OpenAI API. If your application already uses the OpenAI SDK, you only need to replace **two settings — the base URL and the API key** — to get started.

## Quick Start

Update just two lines in your existing OpenAI code.

<CodeGroup>
  ```python Python theme={null}
  from openai import OpenAI

  client = OpenAI(
      api_key="YOUR_AIEEV_API_KEY",                            # Replace: Air API key
      base_url="https://ap-1.aieev.cloud/endpoints/<endpoint_id>/v1"  # Replace: Air API base URL
  )

  response = client.chat.completions.create(
      model="qwen/qwen3.5-9b",
      messages=[
          {"role": "user", "content": "Hello!"}
      ]
  )

  print(response.choices[0].message.content)
  ```

  ```javascript Node.js theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: "YOUR_AIEEV_API_KEY",                            // Replace: Air API key
    baseURL: "https://ap-1.aieev.cloud/endpoints/<endpoint_id>/v1", // Replace: Air API base URL
  });

  const response = await client.chat.completions.create({
    model: "qwen/qwen3.5-9b",
    messages: [{ role: "user", content: "Hello!" }],
  });

  console.log(response.choices[0].message.content);
  ```

  ```bash cURL theme={null}
  curl https://ap-1.aieev.cloud/endpoints/<endpoint_id>/v1/chat/completions \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_AIEEV_API_KEY" \
    -d '{
      "model": "qwen/qwen3.5-9b",
      "messages": [
        {"role": "user", "content": "Hello!"}
      ]
    }'
  ```

  ```bash Shell theme={null}
  #!/bin/bash

  API_KEY="YOUR_AIEEV_API_KEY"

  response=$(curl -s -X POST https://ap-1.aieev.cloud/endpoints/<endpoint_id>/v1/chat/completions \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "model": "qwen/qwen3.5-9b",
    "messages": [{"role": "user", "content": "Hello!"}]
  }')

  echo "$response" | jq .
  ```
</CodeGroup>

<Tip>
  See the [Authentication](/docs/get-started/authentication) page for how to generate an API key.
</Tip>

## Base URL

Each model has its own endpoint URL. Replace `<endpoint_id>` with the **endpoint ID** from the table below.

```text theme={null}
https://ap-1.aieev.cloud/endpoints/<endpoint_id>/v1
```

For example, for Qwen3.6-35B-A3B:

```text theme={null}
https://ap-1.aieev.cloud/endpoints/qwen3-6-35b-a3b/v1
```

<Warning>
  The **endpoint ID** in the URL and the **model ID** passed as the `model` parameter use different notation. If the `model` value does not exactly match the model ID in the table below, the API returns a 404 error (`The model ... does not exist.`).
</Warning>

## Supported Models and Features

| Model              | Endpoint ID          | Model ID (`model`)                       | Context | Streaming | Tool Calling |   Vision  | Reasoning |
| :----------------- | :------------------- | :--------------------------------------- | :-----: | :-------: | :----------: | :-------: | :-------: |
| Qwen3.6-35B-A3B    | `qwen3-6-35b-a3b`    | `qwen/qwen3.6-35b-a3b`                   |   256K  |     ✅     |       ✅      | ✅ up to 4 |     ✅     |
| Qwen3.5-9B         | `qwen3-5-9b`         | `qwen/qwen3.5-9b`                        |   256K  |     ✅     |       ✅      | ✅ up to 1 |     ✅     |
| Llama3.3-70B       | `llama3-3-70b`       | `meta-llama/Meta-Llama-3.3-70B-Instruct` |   128K  |     ✅     |       ✅      |     ❌     |     ❌     |
| Qwen3-32B          | `qwen3-32b`          | `qwen/qwen3-32b`                         |   40K   |     ✅     |       ✅      |     ❌     |     ✅     |
| Qwen3-Embedding-8B | `qwen3-embedding-8b` | `qwen/qwen3-embedding-8b`                |   32K   |     -     |       -      |     -     |     -     |
| Qwen3-TTS          | `qwen3-tts`          | `qwen/qwen3-tts-customvoice`             |    -    |     -     |       -      |     -     |     -     |

<sub>✅ supported · ❌ not supported · `-` not applicable or TBD</sub>

## Response Format

Chat Completions responses use the same structure as the OpenAI API.

```json theme={null}
{
  "id": "chatcmpl-8f3a1b2c...",
  "object": "chat.completion",
  "created": 1753250000,
  "model": "qwen/qwen3.5-9b",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I help you today?",
        "reasoning": null,
        "tool_calls": []
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 12,
    "total_tokens": 22
  }
}
```

| Field                          | Description                                                                                                            |
| :----------------------------- | :--------------------------------------------------------------------------------------------------------------------- |
| `choices[].message.content`    | The model's response text                                                                                              |
| `choices[].message.reasoning`  | The model's reasoning trace (see [Reasoning](#reasoning))                                                              |
| `choices[].message.tool_calls` | Function calls requested by the model (see [Tool Calling](#tool-calling-function-calling))                             |
| `choices[].finish_reason`      | Why generation stopped — `stop` (natural end), `length` (`max_tokens` reached), `tool_calls` (function call requested) |
| `usage`                        | Input, output, and total token counts                                                                                  |

## Streaming

Set the `stream` option to `true` to receive the response in real time as SSE (Server-Sent Events). (Default: `false`)

```python theme={null}
response = client.chat.completions.create(
    model="qwen/qwen3.5-9b",
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True,
    stream_options={"include_usage": True}  # optional: usage in the final chunk
)

for chunk in response:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

* Each chunk delivers a text fragment in `choices[0].delta.content`, and the stream ends with a `data: [DONE]` event.
* Send `stream_options={"include_usage": true}` to receive token usage (`usage`) in the final chunk.

<Note>
  Streaming is currently supported on **Qwen3.6-35B-A3B**, **Qwen3.5-9B**, **Qwen3-32B**, and **Llama3.3-70B**.
</Note>

## Tool Calling (Function Calling)

Lets the model call external functions or services. Supported on **Qwen3.6-35B-A3B**, **Qwen3.5-9B**, **Qwen3-32B**, and **Llama3.3-70B**.

Tool calling is a three-step flow: define functions and send the request, the model suggests a function call, then execute the function and send back the result. The model does not execute functions itself — it only generates which function to call and with what arguments.

### 1. Define your functions and send the request

```python theme={null}
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Look up the weather for a given location.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "City name (e.g. Seoul)"
                    }
                },
                "required": ["location"]
            }
        }
    }
]

messages = [{"role": "user", "content": "What's the weather in Seoul?"}]

response = client.chat.completions.create(
    model="qwen/qwen3.5-9b",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)
```

### 2. The model suggests a function call

When the model decides a function call is needed, it returns a `tool_calls` array with `finish_reason: "tool_calls"`.

```json theme={null}
{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_abc123",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"location\": \"Seoul\"}"
      }
    }
  ]
}
```

<Warning>
  `function.arguments` is returned as a JSON **string** — parse it with `json.loads()` (or equivalent) before use. The model may return multiple function calls at once, so iterate over the entire `tool_calls` array.
</Warning>

### 3. Execute the function and return the result

Append the result as a `role: "tool"` message and send the request again — the model generates the final answer based on the result.

```python theme={null}
import json

message = response.choices[0].message

if message.tool_calls:
    messages.append(message)  # add the model's tool_calls response to the conversation

    for tool_call in message.tool_calls:
        args = json.loads(tool_call.function.arguments)
        result = get_weather(**args)  # execute the actual function

        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps(result),
        })

    final = client.chat.completions.create(
        model="qwen/qwen3.5-9b",
        messages=messages,
        tools=tools,
    )
    print(final.choices[0].message.content)
    # e.g. "It's currently sunny in Seoul at 28°C."
```

### tool\_choice options

| Value                                                       | Behavior                                        |
| :---------------------------------------------------------- | :---------------------------------------------- |
| `"auto"` (default)                                          | The model decides whether to call a function    |
| `"none"`                                                    | No function calls are returned via `tool_calls` |
| `{"type": "function", "function": {"name": "get_weather"}}` | Forces a call to the specified function         |

<Note>
  Even with `tool_choice: "none"`, the model may still emit function-call-shaped text in the response body if `tools` definitions remain in the request. When you don't need function calls, the most reliable approach is to omit the `tools` parameter entirely.
</Note>

### Parallel tool calls

The model can return multiple function calls in a single response via the `tool_calls` array. Set `parallel_tool_calls` to `false` (default `true`) to limit the model to one function call at a time.

### Streaming tool calls

With `stream: true`, tool call data arrives split across chunks. The first chunk of each tool call carries `id` and `function.name`, and subsequent chunks deliver `function.arguments` in sequence. Group tool calls by `index`, then concatenate `function.arguments` in arrival order. The stream ends with a chunk carrying `finish_reason: "tool_calls"`.

```python theme={null}
stream = client.chat.completions.create(
    model="qwen/qwen3.6-35b-a3b",
    messages=[{"role": "user", "content": "What's the weather in Seoul?"}],
    tools=tools,
    stream=True,
)

tool_calls = {}
for chunk in stream:
    if not chunk.choices:
        continue
    for tc in chunk.choices[0].delta.tool_calls or []:
        entry = tool_calls.setdefault(tc.index, {"id": "", "name": "", "arguments": ""})
        entry["id"] = tc.id or entry["id"]
        entry["name"] = tc.function.name or entry["name"]
        entry["arguments"] += tc.function.arguments or ""
```

## Vision (Image Input)

Qwen3.6-35B-A3B and Qwen3.5-9B accept image input. Pass `messages[].content` as an array and include images with the `image_url` type. `image_url.url` accepts an externally reachable image URL or a base64-encoded data URI (`data:image/jpeg;base64,...`).

```python theme={null}
response = client.chat.completions.create(
    model="qwen/qwen3.6-35b-a3b",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What do you see in this image?"},
                {"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}},
            ],
        }
    ],
)
```

<Note>
  The number of images per request is limited per model — **Qwen3.6-35B-A3B: up to 4**, **Qwen3.5-9B: up to 1**. Video input is not supported.
</Note>

## Reasoning

Qwen3.6-35B-A3B and Qwen3.5-9B support reasoning (thinking). The reasoning trace is returned separately from the final answer (`content`) in the `reasoning` field.

```python theme={null}
response = client.chat.completions.create(
    model="qwen/qwen3.6-35b-a3b",
    messages=[{"role": "user", "content": "Calculate 127 * 43 step by step"}],
    max_tokens=4096,
)

message = response.choices[0].message
print(message.reasoning)  # reasoning trace
print(message.content)    # final answer
```

* Reasoning text also consumes `max_tokens`. Set it generously — if the value is too small, the answer may get cut off.
* When streaming, reasoning fragments arrive first via `delta.reasoning`, followed by `delta.content`.
* **Qwen3-32B** returns its reasoning inline as `<think>` tags inside `content` instead of the `reasoning` field. It can likewise be disabled with `enable_thinking: false`.

### Disabling reasoning

To get an answer directly without reasoning, turn it off via `chat_template_kwargs`.

```python theme={null}
response = client.chat.completions.create(
    model="qwen/qwen3.6-35b-a3b",
    messages=[{"role": "user", "content": "Hello!"}],
    extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)
```

## JSON Mode (Structured Outputs)

Set `response_format` to always receive valid JSON.

```python theme={null}
response = client.chat.completions.create(
    model="qwen/qwen3.5-9b",
    messages=[
        {"role": "system", "content": "Extract user info as a JSON object."},
        {"role": "user", "content": "Chulsoo Kim is a 32-year-old backend developer."},
    ],
    response_format={"type": "json_object"},
)

print(response.choices[0].message.content)
# {"name": "Chulsoo Kim", "age": 32, "job": "backend developer"}
```

<Tip>
  Also instruct the model to respond in JSON in your prompt for more reliable results. Reasoning consumes tokens and can truncate the JSON, so combining JSON mode with [disabled reasoning](#disabling-reasoning) is recommended.
</Tip>

## Embedding

Converts text into vectors, useful for semantic search, recommendation systems, and RAG. You can also pass an array of strings as `input` to embed multiple texts in a single request.

```python theme={null}
response = client.embeddings.create(
    model="qwen/qwen3-embedding-8b",
    input="Text to embed.",
)

print(response.data[0].embedding)  # 4,096-dimensional vector
```

## Supported Parameters

Air API supports the core OpenAI parameters based on the official vLLM spec.

| Parameter                   | Description                                                                     |
| :-------------------------- | :------------------------------------------------------------------------------ |
| `model`                     | The model ID to use                                                             |
| `messages`                  | Array of conversation messages                                                  |
| `stream`                    | Whether to stream the response (default: false)                                 |
| `stream_options`            | Streaming options (`{"include_usage": true}` to receive usage)                  |
| `temperature`               | Controls response randomness (0.0 – 2.0)                                        |
| `max_tokens`                | Maximum number of tokens to generate                                            |
| `top_p`                     | Nucleus sampling probability                                                    |
| `frequency_penalty`         | Penalizes repetition based on token frequency (-2.0 – 2.0)                      |
| `presence_penalty`          | Penalizes repetition based on token presence (-2.0 – 2.0)                       |
| `stop`                      | String(s) that stop generation                                                  |
| `seed`                      | Fixed seed for reproducible outputs                                             |
| `n`                         | Number of response candidates to generate                                       |
| `logprobs` / `top_logprobs` | Return per-token log probabilities                                              |
| `response_format`           | Response format (`{"type": "json_object"}`)                                     |
| `tools`                     | Tool calling function definitions                                               |
| `tool_choice`               | How tools are invoked (`auto`, `none`, or a specific function)                  |
| `parallel_tool_calls`       | Whether multiple function calls may be returned in one response (default: true) |

<Note>
  vLLM extension sampling parameters such as `top_k`, `min_p`, and `repetition_penalty` are also available. With the OpenAI SDK, pass them via `extra_body`.

  ```python theme={null}
  response = client.chat.completions.create(
      model="qwen/qwen3.5-9b",
      messages=[{"role": "user", "content": "Hello!"}],
      extra_body={"top_k": 20, "repetition_penalty": 1.05},
  )
  ```
</Note>

## Related Documentation

<Columns cols={2}>
  <Card title="API Usage Guide" icon="astroid" href="/docs/air-api/api-usage-guide" cta="View Documentation" arrow="true">
    Learn the full flow for secret key authentication and code-based API integration.
  </Card>

  <Card title="Available Models" icon="list" href="/models" cta="Browse Models" arrow="true">
    See specs and pricing for every model.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/docs/air-api/troubleshooting" cta="View Troubleshooting" arrow="true">
    Find solutions to common issues and errors.
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/introduction" cta="View API Reference" arrow="true">
    Explore detailed API specifications, including endpoints and parameters.
  </Card>
</Columns>
