n4nAI

Using the OpenAI Python SDK with Claude Sonnet 4.5 on n4n.ai

Hands-on tutorial: point the OpenAI Python SDK at Claude Sonnet 4.5 through an OpenAI-compatible gateway, with runnable code, streaming, and usage tracking.

n4n Team3 min read584 words

Audio narration

Coming soon — every post will get a voice note here.

The openai python sdk claude sonnet 4.5 pairing works without custom adapters if you route through an OpenAI-compatible inference gateway. This tutorial builds a minimal but production-minded integration: authenticated client, chat calls, streaming, multi-turn context, and usage inspection. You will end with code you can paste into a service and ship.

Prerequisites

  • Python 3.10 or newer
  • openai Python package (v1.40+) and python-dotenv for local env loading
  • An API key for a gateway that speaks the OpenAI protocol. We use n4n.ai as the example host, which fronts 240+ models behind one endpoint.
  • Basic comfort with asyncio is helpful but not required; the synchronous client is enough for most batch jobs.

Set your key in the environment before running anything:

export N4N_API_KEY="sk-..."

Step 1: Install and configure the client

Install the SDK. The OpenAI client is agnostic about the backend as long as the base URL returns the expected response shapes.

pip install openai python-dotenv

Point the client at the gateway’s OpenAI-compatible base URL. n4n.ai exposes a single OpenAI-compatible endpoint that addresses 240+ models, so the same client instance can call Claude Sonnet 4.5 or any other listed model without reinstantiation.

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.n4n.ai/v1",
    api_key=os.environ["N4N_API_KEY"],
)

If you prefer a .env file, load it with dotenv.load_dotenv() before reading os.environ.

Step 2: Your first chat completion

The model identifier follows the gateway’s naming convention. For Anthropic’s Sonnet 4.5, use anthropic/claude-sonnet-4.5. The openai python sdk claude sonnet 4.5 call is then a standard chat.completions.create.

response = client.chat.completions.create(
    model="anthropic/claude-sonnet-4.5",
    messages=[
        {"role": "system", "content": "You are a terse senior engineer."},
        {"role": "user", "content": "Write a Python function to retry on timeout."}
    ],
    max_tokens=200,
)

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

Expected output (abridged):

def retry_on_timeout(func, attempts=3, timeout=5):
    import time
    for i in range(attempts):
        try:
            return func()
        except TimeoutError:
            if i == attempts - 1:
                raise
            time.sleep(timeout)

The response object carries token accounting:

print(response.usage)
# CompletionUsage(prompt_tokens=24, completion_tokens=82, total_tokens=106)

Treat max_tokens as a hard ceiling, not a target. Claude will stop early when it finishes the thought.

Step 3: Streaming responses

For interactive UIs, stream tokens. Set stream=True and iterate deltas. The openai python sdk claude sonnet 4.5 streaming path uses server-sent events under the hood; you do not parse them manually.

stream = client.chat.completions.create(
    model="anthropic/claude-sonnet-4.5",
    messages=[{"role": "user", "content": "Explain asyncio in 3 bullet points."}],
    stream=True,
)

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

Terminal output accumulates live:

- asyncio lets you write concurrent code using coroutines instead of threads.
- The event loop schedules I/O-bound tasks, switching when one awaits.
- `async`/`await` syntax makes suspension points explicit and readable.

Always flush or use end="" to avoid buffered line delays. If you abort mid-stream, close the iterator to release the connection.

Step 4: Multi-turn context and system prompts

Maintain conversation state by appending messages. Do not re-send the system prompt on every turn unless you want to reset tone. The openai python sdk claude sonnet 4.5 message format is identical to OpenAI’s, so your existing helpers transfer.

messages = [
    {"role": "system", "content": "You answer only with valid JSON."},
    {"role": "user", "content": "List two European capitals."},
]

resp = client.chat.completions.create(
    model="anthropic/claude-sonnet-4.5",
    messages=messages,
)
assistant_msg = resp.choices[0].message
messages.append(assistant_msg)

messages.append({"role": "user", "content": "Now add population count."})
resp2 = client.chat.completions.create(
    model="anthropic/claude-sonnet-4.5",
    messages=messages,
)
print(resp2.choices[0].message.content)

Expected second response:

{
  "capitals": [
    {"city": "Paris", "population": 2161000},
    {"city": "Berlin", "population": 3669000}
  ]
}

Keep the messages list server-side only if you trust your storage. For long sessions, summarize older turns to stay under the context window.

Step 5: Handling rate limits and retries

Transient 429s happen. The SDK retries by default with exponential backoff, but tune it for your SLA.

client = OpenAI(
    base_url="https://api.n4n.ai/v1",
    api_key=os.environ["N4N_API_KEY"],
    max_retries=3,
    timeout=30,
)

Catch explicit rate-limit errors to add metrics or dead-letter logic:

from openai import RateLimitError

try:
    response = client.chat.completions.create(
        model="anthropic/claude-sonnet-4.5",
        messages=[{"role": "user", "content": "Ping"}],
    )
except RateLimitError as e:
    print(f"Rate limited: {e.status_code} {e.response}")
    # push to queue, back off, alert

Because the gateway may perform automatic fallback when a provider is degraded, many transient failures never surface to your process. Still, defensive retries at the edge are sane engineering.

Step 6: Inspect token usage and metering

Every completion returns a usage object. Forward it to your logging pipeline. The gateway returns standard usage objects; n4n.ai meters per-token usage so you can reconcile against your own billing records.

def log_usage(resp):
    u = resp.usage
    return {
        "model": resp.model,
        "prompt_tokens": u.prompt_tokens,
        "completion_tokens": u.completion_tokens,
        "total_tokens": u.total_tokens,
    }

print(log_usage(response))

For streaming, usage is only available on the final chunk:

stream = client.chat.completions.create(
    model="anthropic/claude-sonnet-4.5",
    messages=[{"role": "user", "content": "Short poem about TCP."}],
    stream=True,
)
final = None
for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
    if chunk.usage:
        final = chunk.usage
print("\n", final)

Cache-control hints from the provider are forwarded by the gateway. If you send extra_headers={"anthropic-cache-control": "max-age=300"} on repeated prefixes, you will see reduced prompt_tokens on cache hits in the usage breakdown where supported.

Step 7: A reusable wrapper

Wrap the client in a small function to standardize model, timeouts, and error logging. This keeps the openai python sdk claude sonnet 4.5 invocation consistent across your codebase.

from openai import OpenAI, RateLimitError

def ask_claude(system: str, user: str, max_tokens=200) -> str:
    client = OpenAI(
        base_url="https://api.n4n.ai/v1",
        api_key=os.environ["N4N_API_KEY"],
        max_retries=2,
    )
    try:
        resp = client.chat.completions.create(
            model="anthropic/claude-sonnet-4.5",
            messages=[
                {"role": "system", "content": system},
                {"role": "user", "content": user},
            ],
            max_tokens=max_tokens,
        )
        return resp.choices[0].message.content or ""
    except RateLimitError:
        # fallback to queued job or cached response
        raise

if __name__ == "__main__":
    print(ask_claude("You are helpful.", "What is idempotency?"))

Run it:

python claude_client.py

You now have a typed, retry-aware, usage-aware integration that does not depend on Anthropic’s native SDK. Swapping to another model is a one-line change in the model argument.

Tagspythonopenai-sdkclauden4n-ai

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All python + openai-compatible sdk integration posts →