> For the complete documentation index, see [llms.txt](https://docs.nexos.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nexos.ai/gateway-api/code-examples/python-sdk.md).

# Python SDK

### Setup <a href="#setup" id="setup"></a>

Install the SDK and set your credentials:

```bash
pip install openai
```

All examples read the API key from a `.env` file:

```
API_KEY=YOUR_NEXOS_API_KEY
```

OpenAI SDK clients use the base URL `https://api.nexos.ai/v1`; the Anthropic SDK appends `/v1/messages` itself, so it takes the bare `https://api.nexos.ai`.

Model IDs on nexos.ai are the model names shown in the console (e.g. `GPT 5.6 Sol`, `Claude Sonnet 5`, `Whisper`). List them with `GET /v1/models`.

### Chat Completion

Send a conversation and get the model's next reply — the standard OpenAI-compatible endpoint, supported by most chat models on the platform.

```python
import os

from dotenv import load_dotenv
from openai import OpenAI
from openai.types.chat import ChatCompletionUserMessageParam, ChatCompletion

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(
    api_key=api_key,
    base_url="https://api.nexos.ai/v1",
)
message: ChatCompletionUserMessageParam = {
    "role": "user",
    "content": "how many letters 'r' in the word 'strawberry'",
}
response: ChatCompletion = openai.chat.completions.create(model="GPT 5.6 Sol", messages=[message])
print(response.choices[0].message.content)
```

### Messages

The `/v1/messages` endpoint implements the Anthropic Messages API and works with the official Anthropic SDK (`pip install anthropic`). It is available for models that list the `messages` endpoint in `GET /v1/models` (e.g. Claude models).

```python
import os

from anthropic import Anthropic
from anthropic.types import Message
from dotenv import load_dotenv

load_dotenv()
api_key = os.environ["API_KEY"]
# The Anthropic SDK appends /v1/messages itself, so the base URL must not include /v1

anthropic = Anthropic(
    api_key=api_key,
    base_url="https://api.nexos.ai",
)
response: Message = anthropic.messages.create(
    model="Claude Sonnet 5",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": "how many letters 'r' in the word 'strawberry'",
        }
    ],
)
# Claude Sonnet 5 responses may start with a thinking block, so pick the text block explicitly
for block in response.content:
    if block.type == "text":
        print(block.text)
```

### Messages with prompt caching

Reuse a large, stable prompt prefix across calls to cut cost (cached tokens are \~10× cheaper) and latency. `/v1/messages` forwards `cache_control` byte-for-byte, so Anthropic prompt caching works natively. The cached prefix must exceed the model's minimum cacheable length (\~1024 tokens):

```python
import os

from anthropic import Anthropic
from dotenv import load_dotenv

load_dotenv()
api_key = os.environ["API_KEY"]
# The Anthropic SDK appends /v1/messages itself, so the base URL must not include /v1

anthropic = Anthropic(
    api_key=api_key,
    base_url="https://api.nexos.ai",
)

# The cached prefix must exceed the model's minimum cacheable length (~1024 tokens)
long_system_prompt = (
    "You are a meticulous linguist who counts letters in words. "
    "Always double-check your counts by spelling the word out letter by letter. "
) * 60

def ask() -> None:
    response = anthropic.messages.create(
        model="Claude Sonnet 5",
        max_tokens=1024,
        system=[
            {
                "type": "text",
                "text": long_system_prompt,
                "cache_control": {"type": "ephemeral"},
            }
        ],
        messages=[
            {
                "role": "user",
                "content": "how many letters 'r' in the word 'strawberry'",
            }
        ],
    )
    usage = response.usage
    print(
        f"cache_creation_input_tokens={usage.cache_creation_input_tokens} "
        f"cache_read_input_tokens={usage.cache_read_input_tokens}"
    )

ask()  # first call writes the cache
ask()  # second call reads it back at a reduced rate
```

### Messages streaming

Receive the reply incrementally as server-sent events instead of waiting for the whole message — useful for chat UIs and long outputs.

```python
import os

from anthropic import Anthropic
from dotenv import load_dotenv

load_dotenv()
api_key = os.environ["API_KEY"]
# The Anthropic SDK appends /v1/messages itself, so the base URL must not include /v1

anthropic = Anthropic(
    api_key=api_key,
    base_url="https://api.nexos.ai",
)
with anthropic.messages.stream(
    model="Claude Sonnet 5",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": "how many letters 'r' in the word 'strawberry'",
        }
    ],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
print()
```

### Messages with thinking

Let the model reason internally before answering — the response starts with a `thinking` block followed by the final text.

```python
import os

from anthropic import Anthropic
from dotenv import load_dotenv

load_dotenv()
api_key = os.environ["API_KEY"]
# The Anthropic SDK appends /v1/messages itself, so the base URL must not include /v1

anthropic = Anthropic(
    api_key=api_key,
    base_url="https://api.nexos.ai",
)
# Claude 5 models use adaptive thinking with an effort level
# (older models use {"type": "enabled", "budget_tokens": N} instead)
response = anthropic.messages.create(
    model="Claude Sonnet 5",
    max_tokens=4096,
    thinking={"type": "adaptive"},
    output_config={"effort": "medium"},
    messages=[
        {
            "role": "user",
            "content": "how many letters 'r' in the word 'strawberry'",
        }
    ],
)
for block in response.content:
    if block.type == "thinking":
        print(f"[thinking] {block.thinking[:120]}...")
    elif block.type == "text":
        print(block.text)
```

### Messages with tool calls

Let the model call functions you define: it responds with a `tool_use` block, your code runs the tool and sends the result back, and the model produces the final answer.

```python
import os

from anthropic import Anthropic
from dotenv import load_dotenv

load_dotenv()
api_key = os.environ["API_KEY"]
# The Anthropic SDK appends /v1/messages itself, so the base URL must not include /v1

anthropic = Anthropic(
    api_key=api_key,
    base_url="https://api.nexos.ai",
)

tools = [
    {
        "name": "count_letters",
        "description": "Count occurrences of a letter in a word",
        "input_schema": {
            "type": "object",
            "properties": {
                "word": {"type": "string"},
                "letter": {"type": "string"},
            },
            "required": ["word", "letter"],
        },
    }
]
messages = [
    {
        "role": "user",
        "content": "Use the count_letters tool to count letters 'r' in the word 'strawberry'",
    }
]

response = anthropic.messages.create(
    model="Claude Sonnet 5",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)
tool_use = next(block for block in response.content if block.type == "tool_use")
print(f"tool call: {tool_use.name}({tool_use.input})")

result = str(tool_use.input["word"].count(tool_use.input["letter"]))
messages.append({"role": "assistant", "content": response.content})
messages.append(
    {
        "role": "user",
        "content": [
            {
                "type": "tool_result",
                "tool_use_id": tool_use.id,
                "content": result,
            }
        ],
    }
)

final = anthropic.messages.create(
    model="Claude Sonnet 5",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)
for block in final.content:
    if block.type == "text":
        print(block.text)
```

### Responses

OpenAI's newer generation API: send text or structured input items and get the model output back. Responses created with `store` can be retrieved and managed later.

```python
import os

from dotenv import load_dotenv
from openai import OpenAI
from openai.types.responses import Response

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(
    api_key=api_key,
    base_url="https://api.nexos.ai/v1",
)
response: Response = openai.responses.create(
    model="GPT 5.6 Sol",
    input="how many letters 'r' in the word 'strawberry'",
)
print(response.output_text)
```

### Responses - compact conversation

Compress a long stored conversation into a smaller context so it can keep going without hitting the model's context limit. Pass the last response's ID via previous\_response\_id — the API returns a new, compacted response whose ID you use to continue the conversation.

```python
import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(
    api_key=api_key,
    base_url="https://api.nexos.ai/v1",
)

# Compacting summarizes a stored conversation so it can continue with a smaller context
response = openai.responses.create(
    model="GPT 5.6 Sol",
    input="how many letters 'r' in the word 'strawberry'",
    store=True,
)
print(f"created: {response.id}")

compacted = openai.responses.compact(
    model="GPT 5.6 Sol",
    previous_response_id=response.id,
    input=[],
)
print(f"compacted: {compacted.id}")
```

### Responses lifecycle

Stored responses (store=true) live on the platform after creation, so you can work with them later: retrieve a response by ID, list the input items it was created from, and delete it when it's no longer needed.

```python
import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(
    api_key=api_key,
    base_url="https://api.nexos.ai/v1",
)

# Create a stored response
response = openai.responses.create(
    model="GPT 5.6 Sol",
    input="how many letters 'r' in the word 'strawberry'",
    store=True,
)
print(f"created: {response.id}")
print(f"output: {response.output_text}")

# Get a model response
retrieved = openai.responses.retrieve(response.id)
print(f"retrieved: {retrieved.id} status={retrieved.status}")

# List input items
input_items = openai.responses.input_items.list(response.id)
for item in input_items.data:
    print(f"input item: {item.type}")

# Delete a model response
openai.responses.delete(response.id)
print(f"deleted: {response.id}")
```

### Embedding

Convert text into a numeric vector for semantic search, clustering, and RAG. One vector is returned per input.

```python
import os

from dotenv import load_dotenv
from openai import OpenAI
from openai.types.create_embedding_response import CreateEmbeddingResponse

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(
    api_key=api_key,
    base_url="https://api.nexos.ai/v1",
)
response: CreateEmbeddingResponse = openai.embeddings.create(
    model="Text Embedding 3 Large",
    input="There are three letters 'r' in the word 'strawberry'.",
)
embedding = response.data[0].embedding
print(f"dimensions: {len(embedding)}")
print(f"first values: {embedding[:5]}")
```

### Audio Generation

Convert text to spoken audio (text-to-speech).

```python
import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(
    api_key=api_key,
    base_url="https://api.nexos.ai/v1",
)
response = openai.audio.speech.create(
    input="There are three letters 'r' in the word 'strawberry'.",
    model="tts-1",
    voice="alloy",
)
response.write_to_file("generated.mp3")
```

### Audio Transcription

Transcribe an audio file to text in its original language. The example reads `sound.mp3` — you can create one with the Audio Generation example above.

```python
import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(api_key=api_key, base_url="https://api.nexos.ai/v1")
response = openai.audio.transcriptions.create(file=open('sound.mp3', 'rb'), model='Whisper')
print(response.text)
```

### Audio Translation

Transcribe an audio file and translate the text into English. The example reads `sound.mp3` — you can create one with the Audio Generation example above.

```python
import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(api_key=api_key, base_url="https://api.nexos.ai/v1")
response = openai.audio.translations.create(file=open("sound.mp3", "rb"), model="Whisper")
print(response.text)
```

### Image Generation

Generate an image from a text prompt.

```python
import base64
import os

from dotenv import load_dotenv
from openai import OpenAI
from openai.types.images_response import ImagesResponse

load_dotenv()
api_key = os.environ["API_KEY"]

openai = OpenAI(
    api_key=api_key,
    base_url="https://api.nexos.ai/v1",
)
response: ImagesResponse = openai.images.generate(
    prompt="three letters 'r' with strawberry texture",
    model="GPT Image 2",
)
if response.data is not None:
    image = response.data[0]
    if image.url is not None:
        print(image.url)
    elif image.b64_json is not None:
        with open("generated.png", "wb") as f:
            f.write(base64.b64decode(image.b64_json))
        print("saved to generated.png")
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nexos.ai/gateway-api/code-examples/python-sdk.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
