Skip to main content

Endpoint

POST /api/ai/chat/completions
Compatible with the OpenAI chat completions format. Supports streaming, multimodal input (images, video, audio, and documents), tool calling, and structured output.

Request Parameters

ParameterTypeRequiredDefaultDescription
modelstringyesModel name (e.g. claude-sonnet-4-6, gpt-5-4-2026-03-05, gemini-3-1-flash-lite-preview)
messagesarrayyesArray of message objects. Must not be empty.
streambooleannofalseStream the response as server-sent events.
max_tokensintegernovariesMaximum tokens in the response.
temperaturenumbernovariesSampling temperature (0-2).
top_pnumbernoNucleus sampling parameter.
frequency_penaltynumbernoPenalize repeated tokens.
presence_penaltynumbernoPenalize tokens already present.
toolsarraynoTool/function definitions for tool calling.
tool_choicestring/objectnoControl tool selection behavior.
parallel_tool_callsbooleannoAllow parallel tool calls.
response_formatobjectnoConstrain response format (e.g. {"type": "json_object"}). Support varies by provider.

Message Format

Each message has a role and content:
[
  {"role": "system", "content": "You are a helpful assistant."},
  {"role": "user", "content": "Hello!"},
  {"role": "assistant", "content": "Hi there!"}
]

Vision (multimodal)

Use a content array to include images or video:
{
  "role": "user",
  "content": [
    {"type": "text", "text": "What's in this image?"},
    {"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}
  ]
}
Video input:
{
  "role": "user",
  "content": [
    {"type": "text", "text": "Describe this video"},
    {"type": "video_url", "video_url": {"url": "https://example.com/clip.mp4"}}
  ]
}
Image and video URLs must be publicly accessible.

Audio understanding

Send audio to a model that supports audio input with an audio_url content part:
{
  "role": "user",
  "content": [
    {"type": "audio_url", "audio_url": {"url": "https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/audio/DoOrDoNot.m4a"}},
    {"type": "text", "text": "What is said in this clip?"}
  ]
}
You may also inline the audio as a base64 data URL:
{"type": "audio_url", "audio_url": {"url": "data:audio/mp3;base64,SUQzBAAAAAA..."}}
The URL must be publicly accessible (and unexpired, if presigned). Audio must be 20 MB or smaller; larger files return a 400. Place the audio part before the text part for the best results.
Supported formats vary by provider. OpenAI audio models (e.g. gpt-audio) accept only wav and mp3; an unsupported format returns a 400. Gemini models (e.g. gemini-3-1-pro-preview) additionally accept m4a, aac, ogg, and flac.

Files and documents (PDFs)

Attach a document to a message with a file content part. Pass the file inline as a base64 data URL in file.file_data:
{
  "role": "user",
  "content": [
    {
      "type": "file",
      "file": {
        "filename": "report.pdf",
        "file_data": "data:application/pdf;base64,JVBERi0xLjQK..."
      }
    },
    {"type": "text", "text": "Summarize the key findings in this document."}
  ]
}
You may also reference a document by URL, useful for files generated in a workspace:
{
  "type": "file",
  "file": {"file_url": "https://arxiv.org/pdf/1706.03762"}
}
Pass exactly one of file_data (a base64 data URL) or file_url. The URL must be publicly accessible (and unexpired, if presigned). A document must be 24 MB or smaller; larger files return a 400. Place the document before the text part for the best results.
Supported file types: application/pdf. Requesting an unsupported file type returns a 400 error. Referencing files by OpenAI file_id is not supported. Inline the file with file_data or pass file_url.
A 400 response for an unsupported type looks like:
{
  "error": {
    "type": "invalid_file_input",
    "title": "The provided file could not be used.",
    "detail": "Unsupported file type 'image/tiff'. Supported file types: application/pdf."
  },
  "status": "error",
  "status_message": "invalid_file_input",
  "status_description": "The provided file could not be used."
}

Examples

Basic text generation

from openai import OpenAI

client = OpenAI(
    base_url="https://hub.oxen.ai/api/ai",
    api_key="YOUR_API_KEY",
)

response = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Say hello in exactly 3 words."}],
    max_tokens=50,
    temperature=0.1,
)

print(response.choices[0].message.content)
curl -X POST https://hub.oxen.ai/api/ai/chat/completions \
  -H "Authorization: Bearer $OXEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "messages": [{"role": "user", "content": "Say hello in exactly 3 words."}],
    "max_tokens": 50,
    "temperature": 0.1
  }'

Response

{
  "id": "chatcmpl-97eab7db-fe67-4b29-900c-ed5260c654d4",
  "object": "chat.completion",
  "created": 1775090332,
  "model": "claude-sonnet-4-6",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello, how are you?"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 15,
    "completion_tokens": 5,
    "total_tokens": 20
  }
}

Analyze a PDF

import base64
from openai import OpenAI

client = OpenAI(
    base_url="https://hub.oxen.ai/api/ai",
    api_key="YOUR_API_KEY",
)

with open("report.pdf", "rb") as f:
    file_data = "data:application/pdf;base64," + base64.standard_b64encode(f.read()).decode()

response = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "file", "file": {"filename": "report.pdf", "file_data": file_data}},
                {"type": "text", "text": "Summarize the key findings in this document."},
            ],
        }
    ],
)

print(response.choices[0].message.content)
FILE_DATA="data:application/pdf;base64,$(base64 < report.pdf | tr -d '\n')"
curl -X POST https://hub.oxen.ai/api/ai/chat/completions \
  -H "Authorization: Bearer $OXEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "file", "file": {"filename": "report.pdf", "file_data": "'"$FILE_DATA"'"}},
        {"type": "text", "text": "Summarize the key findings in this document."}
      ]
    }]
  }'

Understand audio

from openai import OpenAI

client = OpenAI(
    base_url="https://hub.oxen.ai/api/ai",
    api_key="YOUR_API_KEY",
)

response = client.chat.completions.create(
    model="gemini-3-1-pro-preview",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "audio_url", "audio_url": {"url": "https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/audio/DoOrDoNot.m4a"}},
                {"type": "text", "text": "Transcribe this clip and summarize it in one sentence."},
            ],
        }
    ],
)

print(response.choices[0].message.content)
curl -X POST https://hub.oxen.ai/api/ai/chat/completions \
  -H "Authorization: Bearer $OXEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3-1-pro-preview",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "audio_url", "audio_url": {"url": "https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/audio/DoOrDoNot.m4a"}},
        {"type": "text", "text": "Transcribe this clip and summarize it in one sentence."}
      ]
    }]
  }'
To send a local file, base64-encode it into a data: URL:
Python
import base64
from openai import OpenAI

client = OpenAI(
    base_url="https://hub.oxen.ai/api/ai",
    api_key="YOUR_API_KEY",
)

with open("clip.mp3", "rb") as f:
    audio_url = "data:audio/mp3;base64," + base64.standard_b64encode(f.read()).decode()

response = client.chat.completions.create(
    model="gemini-3-1-pro-preview",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "audio_url", "audio_url": {"url": audio_url}},
                {"type": "text", "text": "What is said in this clip?"},
            ],
        }
    ],
)

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

Streaming

from openai import OpenAI

client = OpenAI(
    base_url="https://hub.oxen.ai/api/ai",
    api_key="YOUR_API_KEY",
)

stream = client.chat.completions.create(
    model="gemini-3-1-flash-lite-preview",
    messages=[{"role": "user", "content": "Say hello"}],
    stream=True,
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)
print()
curl -X POST https://hub.oxen.ai/api/ai/chat/completions \
  -H "Authorization: Bearer $OXEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3-1-flash-lite-preview",
    "messages": [{"role": "user", "content": "Say hello"}],
    "stream": true
  }'
Returns server-sent events. Each chunk has a delta instead of a full message:
data: {"choices":[{"delta":{"content":"Hello"},"finish_reason":null,"index":0}],"created":1775090334,"id":"chatcmpl-...","model":"gemini-3-1-flash-lite-preview","object":"chat.completion.chunk"}

data: {"choices":[{"delta":{"content":" there"},"finish_reason":null,"index":0}],...}

data: [DONE]

Tool calling

from openai import OpenAI

client = OpenAI(
    base_url="https://hub.oxen.ai/api/ai",
    api_key="YOUR_API_KEY",
)

response = client.chat.completions.create(
    model="gpt-5-4-2026-03-05",
    messages=[
        {"role": "system", "content": "Use tools when appropriate."},
        {"role": "user", "content": "What is the weather in San Francisco?"},
    ],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get current weather",
            "parameters": {
                "type": "object",
                "properties": {"location": {"type": "string"}},
                "required": ["location"],
            },
        },
    }],
)

tool_call = response.choices[0].message.tool_calls[0]
print(f"{tool_call.function.name}({tool_call.function.arguments})")
curl -X POST https://hub.oxen.ai/api/ai/chat/completions \
  -H "Authorization: Bearer $OXEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5-4-2026-03-05",
    "messages": [
      {"role": "system", "content": "Use tools when appropriate."},
      {"role": "user", "content": "What is the weather in San Francisco?"}
    ],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get current weather",
        "parameters": {
          "type": "object",
          "properties": {"location": {"type": "string"}},
          "required": ["location"]
        }
      }
    }]
  }'
When the model uses a tool, finish_reason is "tool_calls":
{
  "choices": [{
    "finish_reason": "tool_calls",
    "message": {
      "content": null,
      "role": "assistant",
      "tool_calls": [{
        "id": "call_GRNwPXnbuQW4Sa3QNB3FYkYw",
        "index": 0,
        "type": "function",
        "function": {
          "name": "get_weather",
          "arguments": "{\"location\":\"San Francisco\"}"
        }
      }]
    }
  }]
}

Structured output (JSON mode)

from openai import OpenAI

client = OpenAI(
    base_url="https://hub.oxen.ai/api/ai",
    api_key="YOUR_API_KEY",
)

response = client.chat.completions.create(
    model="gpt-5-4-2026-03-05",
    messages=[{"role": "user", "content": "List 3 colors as a JSON array"}],
    response_format={"type": "json_object"},
    max_tokens=100,
)

print(response.choices[0].message.content)
curl -X POST https://hub.oxen.ai/api/ai/chat/completions \
  -H "Authorization: Bearer $OXEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5-4-2026-03-05",
    "messages": [{"role": "user", "content": "List 3 colors as a JSON array"}],
    "response_format": {"type": "json_object"},
    "max_tokens": 100
  }'

Errors

ConditionError
No model specified"You must specify a model to call"
Model not found"Model not found: <name>"
Empty messages"Messages array cannot be empty"
Insufficient creditsCredit-related error message