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 openaito 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:
AsyncOpenAIworks identically with the new base URL. - Fine-tunes / files: Some provider-specific admin endpoints may not be proxied; stick to inference calls.
- Timeouts: Set
timeouton 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.