n4nAI

Migrating from openai-python to n4n: a step-by-step guide

Step-by-step tutorial to migrate openai-python library to n4n with runnable Python code, covering base URL, models, streaming, and fallback.

n4n Team3 min read571 words

Audio narration

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

If you want to migrate openai-python library to n4n, the good news is that the openai Python SDK is just a thin HTTP client with a typed wrapper. Because n4n.ai exposes an OpenAI-compatible surface, most of your existing call sites stay intact: you change a base URL, swap model identifiers, and gain access to 240+ models behind one endpoint. This guide walks through a real migration with runnable snippets and the output you should expect at each checkpoint.

Prerequisites

  • Python 3.10 or newer
  • An existing project using openai >= 1.0 (pip show openai to confirm)
  • An API key from n4n.ai exported as N4N_API_KEY
  • pip install --upgrade openai (the same package works, no fork required)
export N4N_API_KEY="sk-..."
python -c "import openai; print(openai.__version__)"
# Expected: 1.40.0 (or similar >=1.0)

Why the OpenAI client works unchanged

The OpenAI class accepts base_url and api_key. It builds request bodies and parses responses according to the OpenAI REST contract. As long as the upstream gateway mirrors that contract—same paths, same JSON shapes—the client does not care where the bytes go. When you migrate openai-python library to n4n, you are not adopting a new SDK; you are repointing the same one.

Step 1: Swap the base URL

Create a single client instance with the gateway endpoint.

import os
from openai import OpenAI

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

That is the only mandatory code change for a minimal chat call. Everything else is optional tuning.

Step 2: List available models

OpenAI’s catalog is a subset of what the gateway routes. Model IDs are namespaced by provider.

models = client.models.list()
for m in models.data[:3]:
    print(m.id)

Expected output (truncated):

openai/gpt-4o
anthropic/claude-3-5-sonnet
meta/llama-3-70b-instruct

If you see a list, the credentials and base URL are correct.

Step 3: Run your first chat completion

Use a namespaced model string. The request shape is identical to OpenAI.

resp = client.chat.completions.create(
    model="anthropic/claude-3-5-sonnet",
    messages=[{"role": "user", "content": "Return a JSON object with key 'hi'."}],
)
print(resp.choices[0].message.content)

Expected output:

{"hi": "hello"}

Step 4: Inspect per-token usage metering

The gateway returns standard usage objects. Track them for cost attribution.

print(resp.usage)

Expected output:

CompletionUsage(prompt_tokens=13, completion_tokens=9, total_tokens=22)

n4n meters per token across all providers, so you get uniform accounting without writing provider-specific code.

Step 5: Stream tokens

Streaming uses the same stream=True flag. The delta objects are byte-compatible.

stream = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Count to 3 separated by spaces."}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)

Expected terminal output (no newlines between numbers):

1 2 3

Step 6: Pass routing and cache-control hints

When you migrate openai-python library to n4n, you keep using extra_headers to send provider-specific hints. The gateway forwards cache-control directives to the upstream provider when supported.

resp = client.chat.completions.create(
    model="anthropic/claude-3-5-sonnet",
    messages=[{"role": "user", "content": "Repeat: cache me."}],
    extra_headers={"cache-control": "max-age=3600"},
)

This works because the gateway honors client routing directives and forwards provider cache-control hints rather than stripping them.

Step 7: Handle degradation with automatic fallback

Providers rate-limit or degrade. The gateway performs automatic fallback when a provider is unavailable. You can also suggest a fallback target via header.

try:
    resp = client.chat.completions.create(
        model="meta/llama-3-70b-instruct",
        messages=[{"role": "user", "content": "Briefly explain TCP."}],
        extra_headers={"x-n4n-fallback": "openai/gpt-4o-mini"},
        timeout=10,
    )
    print(resp.choices[0].message.content)
except Exception as e:
    print("Gateway error:", type(e).__name__, e)

If the primary model is throttled, the gateway routes to the fallback and the call still returns content. Your try/except catches only hard failures (network timeout, 5xx after retries).

Step 8: Batch-rewrite existing code

Most legacy code sets base_url implicitly (default OpenAI). If you hardcoded it, rewrite in place.

# Replace explicit OpenAI base URL
find . -name "*.py" -exec sed -i \
  's#base_url="https://api.openai.com/v1"#base_url="https://api.n4n.ai/v1"#g' {} +

# Replace env var reads if needed
find . -name "*.py" -exec sed -i \
  's#os.environ\["OPENAI_API_KEY"\]#os.environ["N4N_API_KEY"]#g' {} +

Run your test suite. Requests that previously hit OpenAI now hit the gateway with zero logic changes.

Step 9: Smoke-test with pytest

A minimal test confirms the wiring before you ship.

import os
from openai import OpenAI

def test_gateway_chat():
    client = OpenAI(
        base_url="https://api.n4n.ai/v1",
        api_key=os.environ["N4N_API_KEY"],
    )
    r = client.chat.completions.create(
        model="openai/gpt-4o-mini",
        messages=[{"role": "user", "content": "ping"}],
    )
    assert r.choices[0].message.content
    assert r.usage.total_tokens > 0
pytest test_gateway.py -q
# Expected: 1 passed

Compatibility notes

  • Embeddings, images, audio, and function calling use the same endpoint paths and payloads. No client changes beyond base_url.
  • Async: AsyncOpenAI works identically with the new base URL.
  • Fine-tunes / files: Some provider-specific admin endpoints may not be proxied; stick to inference calls.
  • Timeouts: Set timeout on the client or per call; the gateway respects standard HTTP semantics.

Migrating openai-python library to n4n is fundamentally a configuration change, not a rewrite. Repoint, rename models, and optionally leverage routing headers—your application code stays stable while your model options expand.

Tagsopenai-sdkpythonmigrationtutorial

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 migrating from openai sdk to a unified gateway posts →