1
0 Comments

I got tired of rewriting API calls every time I tried a new Chinese model, so I built a router for all of them

TL;DR: I'm building PandasRouter, an open-source, OpenAI-compatible gateway that routes requests across Qwen, DeepSeek, GLM, and Kimi. One SDK, one API key, automatic failover when a provider rate-limits you. Still early, would love feedback from anyone else building on top of Chinese LLMs.
The problem
Over the last few months I kept hearing the same thing from other builders: DeepSeek, Qwen, GLM, and Kimi had all gotten good enough — and cheap enough — that it made no sense to only wire up one Western provider anymore. Price-to-performance on some of these models is genuinely 5-30x better than the equivalent Western flagship, depending on the task.
So I tried it myself. And the models were great. The infrastructure around using more than one of them at the same time was not.
Here's what that actually looked like day to day:
Every provider has a slightly different request/response shape. Some are OpenAI-compatible out of the box, some aren't quite, some stream SSE chunks differently.
Rate limits are unpredictable and mostly undocumented. I'd get hit with 429s during peak hours with no clear sense of when the window would reset.
Billing was scattered across four dashboards, in different currencies, with different token-counting conventions. Reconciling monthly spend was a manual spreadsheet exercise.
If I wanted to A/B test "does Kimi actually do better on this reasoning task than DeepSeek," that meant writing one-off adapter code every time, then deleting it later.
None of these problems are hard individually. But they add up to enough friction that most people just don't bother, and stick with a single vendor even when a cheaper or better-suited model is sitting right there.
What I built
PandasRouter is a thin gateway that sits between your app and the model providers. You talk to it using the standard OpenAI SDK — just point base_url at PandasRouter instead of api.openai.com — and it handles:

Protocol translation — normalizes each provider's request/response format to the OpenAI Chat Completions shape, so your code doesn't need to know or care which model actually answered.
Routing — pick a specific model, or let the router choose based on task type (code, long-context, reasoning) and current provider health.
Automatic failover — if a provider returns a 429 or times out, the router retries against the next model in a same-tier fallback chain, instead of surfacing the error to your users.
Unified usage tracking — one dashboard, one bill, per-model and per-project cost breakdown.

The migration from an existing OpenAI-based app looks like this. You swap out two values:
Old: base_url = "https://api.openai.com/v1", api_key = OPENAI_API_KEY
New: base_url = "https://api.pandasrouter.com/v1", api_key = PANDASROUTER_API_KEY
Same SDK, same call signature, four models available. Example in Python:
from openai import OpenAI
client = OpenAI(
base_url="https://api.pandasrouter.com/v1",
api_key="your-pandasrouter-key",
)
resp = client.chat.completions.create(
model="auto", # or pin to qwen3.6-plus / deepseek-v4-pro / glm-5.1 / kimi-k2.6
messages=[{"role": "user", "content": "Write a quicksort in Python"}],
stream=True,
)
That's genuinely the whole migration for most apps. No new SDK, no rewriting your prompt pipeline, no touching your streaming handler.
How the failover chain works
The part I spent the most time on wasn't the API translation — that's mostly mechanical. It was figuring out sensible fallback chains, grouped by capability rather than just "next cheapest model." Roughly:
Code tasks fall back through: deepseek-v4-pro, then qwen3.6-coder, then kimi-k2.6-code
Long-context tasks fall back through: deepseek-v4-pro, then qwen3.5-27b, then kimi-k2.6
Reasoning tasks fall back through: kimi-k2.6, then glm-5.1, then deepseek-v4-pro-thinking
Lessons learned so far:
Falling back across capability tiers is worse than just erroring out. A code task that fails over to a model tuned for long-context summarization will "succeed" but produce noticeably worse output — silently. I had to group fallbacks by what the model is actually good at, not just by price.
Timeout tuning matters more than I expected. 6-8 seconds turned out to be the sweet spot — long enough to not abandon a slow-but-successful call, short enough that users don't feel the retry.
Surfacing which model actually served the request, via a response header, turned out to be essential for debugging. Without it, "why did this response feel worse today" is impossible to diagnose after the fact.
Where it's at right now
Core gateway is open source, self-hostable via Docker, or you can use the hosted version if you just want to try it without standing up infrastructure.
Supports Qwen, DeepSeek, GLM, and Kimi today. Doubao, MiniMax, and Hunyuan are next on the list if there's interest.
Solo-built, bootstrapped, still early — this is very much a "used it myself first, then decided to open it up" project rather than something I set out to build as a company from day one.
What I'm not sure about yet
Whether "auto" routing, letting the gateway pick the model, is actually something people want, or whether everyone just wants explicit control and the failover safety net without the auto-selection part.
Pricing model for the hosted version — pass-through provider cost plus a small routing fee, or a flat subscription. Haven't committed to either.
How much people actually care about self-hosting vs. just wanting a hosted endpoint they don't have to think about.
Ask
If you're already calling more than one of these models in production, or thought about it and gave up because of exactly the friction described above, I'd genuinely like to hear how you're handling it today. Rolling your own adapter layer? Using OpenRouter or a similar aggregator? Just picking one provider and eating the downside?
Comments open, happy to answer anything about the architecture or share more of the failover/cost data if useful.

on July 2, 2026