Nebul Docs

Examples

Runnable examples for streaming, embeddings, vision, function calling, and reasoning on the Inference API

Working code for the common capabilities, in Python, TypeScript, JavaScript, Go, Rust, Java, C++, and cURL. Every example reads your key from NEBUL_API_KEY, so export it first if you haven't:

export NEBUL_API_KEY="sk-your-key"

The Rust and C++ tabs note any extra crate or dependency they need. Each section links to the page that covers the capability in depth.

Streaming responses

Stream tokens as the model generates them, instead of waiting for the full response:

import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["NEBUL_API_KEY"],
    base_url="https://api.inference.nebul.io/v1",
)

stream = client.chat.completions.create(
    model="zai-org/GLM-5.3",
    messages=[{"role": "user", "content": "Write a haiku about the sea."}],
    stream=True,
)

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

Text arrives in data: chunks as it is generated; the stream ends with a chunk whose finish_reason is stop. For the event format and error handling mid-stream, see Chat Completions.

Embeddings

Turn text into vectors for search, clustering, and retrieval:

import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["NEBUL_API_KEY"],
    base_url="https://api.inference.nebul.io/v1",
)

response = client.embeddings.create(
    model="Qwen/Qwen3-Embedding-8B",
    input="Privacy is fundamental to maintaining trust in AI systems.",
)

print(len(response.data[0].embedding))

The example prints 4096, the embedding size for Qwen/Qwen3-Embedding-8B. Pass a list of strings as input to embed several texts in one request; the response keeps your order in data. See Embeddings for models and dimensions.

Vision

Send images to a vision model as part of a chat message. Use a public URL, or base64 for local files.

Image URL

import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["NEBUL_API_KEY"],
    base_url="https://api.inference.nebul.io/v1",
)

response = client.chat.completions.create(
    model="zai-org/GLM-5.3-Flash",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What is in this image?"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://fastly.picsum.photos/id/669/200/200.jpg?hmac=lAa_bMRK0BRBCTEvl1acVqTfEDrXQc0yNwi683-13cE"
                    }
                }
            ]
        }
    ]
)

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

For this photo, the model answers with something like A person wearing a grey beanie and brown jacket facing away.

Base64-encoded image

import base64
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["NEBUL_API_KEY"],
    base_url="https://api.inference.nebul.io/v1",
)

with open("image.jpg", "rb") as image_file:
    base64_image = base64.b64encode(image_file.read()).decode("utf-8")

response = client.chat.completions.create(
    model="zai-org/GLM-5.3-Flash",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Describe this image in detail."},
                {
                    "type": "image_url",
                    "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
                }
            ]
        }
    ]
)

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

The data: URL form works anywhere an image URL works. See Chat Completions for limits on image size and count.

Function calling

Give the model functions it can ask you to run. The full guide, including parallel calls and streaming, is in Function Calling & Tools.

import json
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["NEBUL_API_KEY"],
    base_url="https://api.inference.nebul.io/v1",
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and country, e.g. London, UK"
                    }
                },
                "required": ["location"]
            }
        }
    }
]

response = client.chat.completions.create(
    model="zai-org/GLM-5.3",
    messages=[{"role": "user", "content": "What's the weather in Paris?"}],
    tools=tools,
    tool_choice="auto",
)

message = response.choices[0].message
if message.tool_calls:
    call = message.tool_calls[0]
    print(call.function.name, json.loads(call.function.arguments))

If the model decides to call the tool, tool_calls contains get_weather with {"location": "Paris, France"} as the arguments. If it answers directly instead, tool_calls is empty. Run the function, append the result as a tool message, and send the conversation back for the final answer; the advanced guide shows the loop end to end.

Reasoning

Reasoning models produce a chain of thought before the answer. Both arrive in the same response, in separate fields:

import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["NEBUL_API_KEY"],
    base_url="https://api.inference.nebul.io/v1",
)

response = client.chat.completions.create(
    model="zai-org/GLM-5.3",
    messages=[{"role": "user", "content": "Which is larger, 9.11 or 9.8?"}],
    extra_body={"reasoning_effort": "low"},
)

message = response.choices[0].message
print(getattr(message, "reasoning_content", "") or "(no reasoning)")
print(message.content)

You'll see the model's working in reasoning_content and the verdict 9.8 in content. reasoning_effort controls how much thinking happens: for zai-org/GLM-5.3 the values are low, high, and max. Not every model puts the trace in the same field, and the accepted values differ per model; see Reasoning Models for the per-family details and the Model Catalog for each model's accepted values.

Streaming with reasoning

In a streaming response, reasoning tokens arrive first in delta.reasoning_content, then the answer in delta.content:

stream = client.chat.completions.create(
    model="zai-org/GLM-5.3",
    messages=[{"role": "user", "content": "What is 15% of 80?"}],
    stream=True,
    extra_body={"reasoning_effort": "low"},
)

for chunk in stream:
    delta = chunk.choices[0].delta
    reasoning = getattr(delta, "reasoning_content", None)
    if reasoning:
        print(reasoning, end="", flush=True)
    if delta.content:
        print(delta.content, end="", flush=True)
print()

The reasoning prints first, then the answer, without a gap between them.

On this page