# Python

> Connect the OpenAI Python client to Router and handle a complete response or stream.

## Install the client

```bash
python -m pip install openai
```

Set `ROUTER_BASE_URL` to the Router endpoint ending in `/v1`, and store your API key in `ROUTER_API_KEY`.

## Create a client

```python
import os
from uuid import uuid4
from openai import OpenAI

client = OpenAI(
    base_url=os.environ["ROUTER_BASE_URL"],
    api_key=os.environ["ROUTER_API_KEY"],
    timeout=45.0,
    max_retries=0,
)
```

This example disables automatic SDK retries so your application can make retry policy explicit. Add bounded retry behavior only for the temporary failures your product can safely repeat.

## List available editions

```python
models = client.models.list()

for model in models.data:
    print(model.id)
```

The raw Router model payload also contains edition limits and provider counts. If your client version hides extension fields, inspect `model.model_dump()`.

## Create a response

```python
request_id = str(uuid4())  # Reuse for an uncertain retry or receipt lookup.
response = client.chat.completions.create(
    model="gemma3:1b",
    messages=[
        {"role": "system", "content": "Answer clearly and briefly."},
        {"role": "user", "content": "What is decentralized inference?"},
    ],
    max_tokens=160,
    temperature=0.4,
    extra_headers={"Idempotency-Key": request_id, "X-Max-Charge-Credits": "5.000000"},
)

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

## Optionally choose a provider

Review a public offer with [choosing a provider](https://router.optimai.network/docs/choosing-a-provider.md), then send Router's `provider_selection` extension through the SDK's documented [extra\_body option](https://github.com/openai/openai-python#undocumented-request-params). Replace the synthetic UUID and revision below with that offer's current values.

```python
selected_request_id = str(uuid4())
selected_response = client.chat.completions.create(
    model="gemma3:1b",
    messages=[{"role": "user", "content": "Explain retrieval briefly."}],
    max_tokens=160,
    extra_body={
        "provider_selection": {
            "offer_id": "00000000-0000-4000-8000-000000000001",
            "pricing_revision": 3,
        }
    },
    extra_headers={
        "Idempotency-Key": selected_request_id,
        "X-Max-Charge-Credits": "5.000000",
    },
)
print(selected_response.choices[0].message.content)
```

Omit `provider_selection` for Auto. Router never silently changes a manual selection. A different offer or price revision needs a new request UUID; an uncertain retry keeps the original UUID and unchanged body.

## Stream a response

```python
stream_request_id = str(uuid4())
stream = client.chat.completions.create(
    model="gemma3:1b",
    messages=[{"role": "user", "content": "Write a two-line welcome."}],
    stream=True,
    extra_headers={"Idempotency-Key": stream_request_id, "X-Max-Charge-Credits": "5.000000"},
)

completed = False

for chunk in stream:
    if not chunk.choices:
        continue
    choice = chunk.choices[0]
    if choice.delta.content:
        print(choice.delta.content, end="", flush=True)
    if choice.finish_reason is not None:
        completed = True

print()
if not completed:
    raise RuntimeError("The response stream ended before completion")
```

> **Keep partial output marked incomplete**
> If iteration raises or the connection closes before a finish reason, preserve any received text only as an incomplete draft.

See [retries and cancellation](https://router.optimai.network/docs/retries-and-timeouts.md) for application-level recovery.
