Keep your OpenAI-compatible client. Change the base URL and API key.
openrouter.ai/api/v1www.ninjachat.ai/api/v1anthropic/claude-opus-4.6→claude-opus-4.6openai/gpt-5→gpt-5google/gemini-3.1-pro→gemini-3.1-prox-ai/grok-4→grok-4from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key="sk-or-...",
)
resp = client.chat.completions.create(
model="anthropic/claude-opus-4.6",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)from openai import OpenAI
client = OpenAI(
base_url="https://www.ninjachat.ai/api/v1", # ← changed
api_key="nj_sk_YOUR_KEY", # ← changed
)
resp = client.chat.completions.create(
model="claude-opus-4.6",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)Get an nj_sk_ key at /developers and add a credit pack. Balances are prepaid, and failed generations are refunded.
# Sanity check: list every model your key can call (no auth needed)
curl https://www.ninjachat.ai/api/v1/models
# First authenticated request
curl https://www.ninjachat.ai/api/v1/chat/completions \
-H "Authorization: Bearer nj_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-5", "messages": [{"role": "user", "content": "Say hi"}]}'OpenRouter namespaces models as vendor/model. NinjaChat uses canonical catalog IDs. Routing preferences live in the routing object and ordered fallbacks live in the models array.
# NinjaChat routing + ordered fallbacks
client.chat.completions.create(
models=["claude-opus-4.6", "gpt-5", "gemini-3.1-pro"],
routing={"strategy": "cost", "allow_fallbacks": True},
messages=[{"role": "user", "content": "Hello"}],
)Same SDK code. Streaming arrives as SSE deltas, and tool_calls round-trip with role: "tool" messages.
# Streaming
stream = client.chat.completions.create(
model="claude-sonnet-4.6",
messages=[{"role": "user", "content": "Write a haiku about ninjas"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
# Tool calling
resp = client.chat.completions.create(
model="gpt-5",
messages=[{"role": "user", "content": "Weather in Tokyo?"}],
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}],
)
call = resp.choices[0].message.tool_calls[0]
# ...run your function, then send the result back:
followup = client.chat.completions.create(
model="gpt-5",
messages=[
{"role": "user", "content": "Weather in Tokyo?"},
resp.choices[0].message,
{"role": "tool", "tool_call_id": call.id, "content": "22°C, clear"},
],
)Metered per token — prices shown are typical-request estimates at the live rates on GET /v1/models. Actual requests settle from measured token usage.
NinjaChat's v1 API covers chat, images, video, and search. If your pipeline embeds documents or calls a moderation endpoint, keep those calls on your current provider — only the completion traffic needs to move.
Every API key gets 60 requests per minute (video submissions are throttled harder because each one is a long-running job). 429 responses include Retry-After. Need more? Contact us from the console.
You buy a credit pack up front instead of getting a surprise invoice. Failed generations are automatically refunded, and you can set monthly spend limits per account, key, or project.
Chat bills per token — published $/MTok input and output rates for every model on GET /v1/models (cache-read and long-context tiers included). Requests preauthorize an estimated maximum and settle to actual usage; a typical request runs from ≈$0.002 on open models to ≈$0.05 on Claude Opus (gpt-5.4-pro ≈$0.33). No length guard — long context just needs balance.
OpenRouter has the broader long-tail marketplace. NinjaChat currently serves 169 text, 39 image, and 12 video models. Check GET /v1/models before migrating a niche model.
Save your own provider keys (Anthropic, OpenAI, Google, xAI, and more) in the developer console and matching requests serve on your key automatically — the provider bills your account and NinjaChat charges a 5% platform fee, the same shape as OpenRouter's BYOK. Provider pinning beyond that is handled for you: pick a model slug (or auto), not an upstream.
Identical mechanics: it's an OpenAI-compatible endpoint, so you change base_url and api_key. The extra step is dropping OpenRouter's vendor/ prefixes from model ids (anthropic/claude-opus-4.6 → claude-opus-4.6).
Yes — pass an ordered models array and set routing.allow_fallbacks. Provider constraints and strategy remain inside routing.
That's the main reason to switch: POST /v1/images/generations and POST /v1/videos share the same key and balance. Current models and unit prices come from GET /v1/models.
Both bill per token. NinjaChat publishes input, cached-input, and output rates in GET /v1/models and settles each request to actual token usage.