Latency-Based Model Routing in Production AI
Users feel what matters most: how fast responses start and flow smoothly through completion.

Latency-based routing means picking, in real time, which AI provider or model gets a given request, based on which one is actually fastest right now, not which one was fastest last week. It sounds simple. It isn't. Getting it right means measuring the correct signals, building routing logic that adapts instead of guessing, and having fallback behavior ready for the moment a provider quietly falls apart mid-request.
The gap that trips people up: the latency number on a provider's dashboard and the latency a user actually feels are two different things. A dashboard might show a clean average. A user staring at a blank chat window doesn't experience an average, they experience the one slow request that happened to land on them.
Three signals make up what a user actually perceives. Time to first token (TTFT) is how long someone stares at nothing before words start showing up, and it drives how responsive an app feels. Inter-token latency (ITL), the gap between each token once generation starts, drives how smooth streaming feels once it's underway. Total request latency, the full round trip, is what matters when there's no streaming at all, like batch jobs or agentic pipelines chaining several calls together.
Optimize for the wrong one and the mismatch shows up fast. A setup tuned to get TTFT under 200ms can still deliver a miserable experience if ITL drags, because tokens start fast and then crawl. Multiply that across an agentic pipeline making five or six sequential calls, and a system that looked fast in a demo turns sluggish in production.
Provider latency isn't stable, either. Research on Azure inference traces (DynamoLLM, HPCA 2025) documents heavy-tailed token distributions, and a separate analysis of over 10 million traces from Azure OpenAI services (BurstGPT, Wang et al., 2024) found latency comes in bursts, not steady streams. A provider that looks fast in a single test tells you almost nothing about how it'll behave ten minutes from now under load.
And the gateway sitting between the app and the provider isn't free, either. Every layer added between a request and its destination adds some overhead. That overhead eats into the very latency budget the system is trying to protect, so it has to be measured and kept as close to zero as engineering allows.
The core tension is straightforward to state and hard to solve: everyone wants the fastest available response every single time, but providers degrade without warning. Routing is the deliberate mechanism that resolves that tension, instead of leaving it to chance.
What latency-based routing actually means, and what it does not
Latency-based routing sends each request to whichever provider or model instance is most likely to respond fastest, given conditions right now. That's the whole idea. It is not a toggle flipped once in a settings panel. It is not a static config file written six months ago and forgotten. It is not the same thing as always picking the smallest model available, and it's not random load balancing across a pool of API keys either, though it gets confused with both fairly often.
It also lives in tension with cost and quality routing, and that tension doesn't resolve itself. The fastest model available at this exact moment might not be the cheapest option, and it might not be the most accurate one either.
Sometimes the tradeoff is easy. For a simple factual query, a 1-billion-parameter model and a 70-billion-parameter model might both give the correct answer. Academic research on Llama variants found the larger model costs roughly 7x more and runs about 15x slower for that kind of task. Routing the smaller model in that case isn't really a judgment call, it's just correct.
Complex reasoning tasks flip the calculation. Route purely on speed there and quality falls apart, because the fast model simply can't do the reasoning the task needs. Routing logic has to account for task complexity as its own dimension, not just latency and cost.
Picture a frontier with quality, cost, and latency as its three edges. Every request should land somewhere deliberate on that frontier, chosen on purpose, not defaulted to whatever's most expensive or whatever's fastest in isolation without checking if it's even good enough.
Two routing modes cover most of what teams actually build. Rule-based routing maps explicit conditions, request type, token budget, SLA tier, to specific providers or models. Score-based routing uses a lightweight model or scoring function to predict which provider will likely perform best for whatever request just came in.
The latency signals worth measuring and how to measure them reliably
TTFT gets measured from the moment a request is submitted to the moment the first token byte arrives back. That clock has to be tracked at the application or gateway layer, not inferred from whatever a provider's documentation claims, because documented numbers and lived numbers rarely match.
ITL is the average gap between tokens once generation is underway. It matters most for streaming interfaces and real-time voice, where a jerky, uneven cadence is instantly noticeable even if the total time ends up fine.
End-to-end latency covers everything: gateway overhead, serialization, the network round trip, full generation start to finish. This is the number that actually matters for agentic chains, where multiple LLM calls stack on top of each other and small delays compound into something a user notices.
Averages lie, though, and P50 versus P95 versus P99 is where that becomes obvious. A provider's median latency can look perfectly fine while its P99 is a disaster for the unlucky slice of users who hit degraded capacity at the wrong moment. Because the Azure OpenAI traces mentioned earlier show heavy-tailed distributions, averages structurally hide how bad the worst case really gets. Tracking tail latency isn't optional, it's the whole point.
Sampling strategy matters too. Providers need to get polled for health often enough to catch real degradation, but not so often that the polling itself adds meaningful drag to the routing path.
Static benchmarks are close to useless here. A provider that's fastest at 9am Pacific on a Tuesday can be the slowest option by afternoon peak. Routing decisions need to run on live, rolling measurements, not a historical table someone built once and never revisited.
And the gateway's own overhead needs constant measurement, because a gateway that eats a large chunk of the latency budget defeats its own purpose. High-performance gateway implementations show this overhead can be pushed down into the microsecond range, with some architectures adding around 11 microseconds of mean overhead at 5,000 requests per second. That's overhead so small it effectively disappears from the budget.
Building routing logic around latency: from static rules to predicted-latency scheduling
Most teams start with static, rule-based routing. Code generation goes to Provider A, summarization goes to Provider B, defined once in a priority list per request type. It's simple, it's easy to audit, and anyone on the team can look at it and understand exactly what it does. The problem shows up the moment a provider's performance shifts and nobody updates the list.
Weighted load balancing across keys and providers is the next step up. Traffic gets split proportionally based on configured weights, which helps spread out rate-limit exposure. It still doesn't adapt to real-time latency changes on its own, though, it just distributes the risk more evenly.
Health-aware routing adds a circuit breaker on top. Error rates and latency spikes get monitored per provider, and degraded providers get pulled from the active pool automatically. The breaker trips when error rate or P95 latency crosses a threshold, and the provider gets re-admitted only after a defined recovery window passes.
Latency-scored routing goes further still, sending each request to whichever provider has the lowest rolling average or exponential moving average latency at that moment. It's more adaptive than static weights, but it's reactive: it responds to what already happened, not what's about to happen.
Predicted-latency scheduling is the frontier past that. A lightweight model, trained continuously on live traffic, replaces hand-tuned weights with a direct latency prediction for each incoming request before it's even sent. Research testing this approach on a production-realistic workload found a 43% improvement in P50 end-to-end latency and a 70% improvement in TTFT. Google reported deploying a version in Vertex AI clusters and saw up to a 40% reduction in both TTFT and ITL. The takeaway matters: opaque, vendor-controlled auto-routing is not the same thing as this. Teams need to actually understand and control what their own routing logic is optimizing for, rather than trusting a black box.
Semantic caching runs alongside all of this, not instead of it. A cached response can return in under 5 milliseconds, versus 2 to 5 seconds for live inference on the same query. It's not a substitute for routing logic. It's a first-pass filter that strips latency out of repeated or near-duplicate requests before any routing decision even needs to happen.
Fallback design: what happens when the preferred provider degrades mid-flight
Provider degradation isn't the exception, it's Tuesday. Rate limit errors (429s), regional outages, sudden latency spikes: these happen across providers, and treating them as edge cases is a design mistake. An application built only for the happy path ends up handing provider failures straight to its users, unfiltered.
Fallback hierarchy needs real structure: primary provider, then secondary, then tertiary, ordered by some blend of cost, latency, and how closely quality matches the primary. Every fallback target needs pre-validation for functional compatibility beforehand, since providers don't all handle the same prompt formats identically, and discovering that during an actual outage is the worst possible time.
Retry and fallback are not the same mechanism, and mixing them up wastes time exactly when time matters most. Retry means hitting the same provider again with the same request, which makes sense for a transient network blip. Fallback means switching to a different provider entirely, which is the right move for rate limits, sustained latency degradation, or an outage. Retrying against a provider that's already struggling just burns the latency budget on a bet that's unlikely to pay off.
Request replay adds another wrinkle. If a request already started and the provider fails mid-stream, does the gateway replay the whole thing from zero on the fallback, or does it just surface whatever partial response came through? That's a product decision, not something a routing config can answer by itself.
SLA thresholds need to get defined before launch, not reverse-engineered afterward. If a chat interface has a defined TTFT SLA target, the fallback trip-wire should fire comfortably before that line gets crossed, not after users have already noticed the lag.
None of this can be trusted until it's actually tested. Synthetic traffic that simulates provider failure is the only real way to confirm fallback logic works before a live outage forces the question.
Routing at the gateway layer versus routing inside application code
Routing logic buried in application code tends to look the same everywhere it shows up: provider-specific SDK imports scattered through business logic, conditional branches that multiply with every new provider added, latency measurement and fallback logic copy-pasted across services. Every provider switch then needs its own code review, its own deployment, its own testing cycle.
The cost compounds fast. Each new provider brings its own authentication scheme, its own request format, its own error codes. A comparison of OpenAI, Anthropic, and Groq APIs makes this concrete: authentication methods, request formats, role definitions, and response structures differ across all three, and a team supporting all of them in application code is maintaining three separate integration surfaces, not one.
Push routing to the infrastructure layer instead, and application code just changes a base URL, it doesn't rewrite its control flow. Provider-specific quirks get absorbed once, at the gateway, instead of once per service that happens to call an LLM. Adding a provider, adjusting weights, turning on a fallback path: all of that becomes a config change instead of a deployment.
Self-hosting a gateway means full control over routing logic and where data lives, which matters for teams with real data residency requirements. It also means the engineering team now owns that gateway's uptime, its upgrades, its operations, which can quietly become the very burden routing was supposed to remove.
A managed gateway flips that trade. The vendor handles routing updates, provider integrations, and uptime, and the team gets observability and control without owning the infrastructure underneath it.
The real question for any team weighing this: is the marginal engineering cost of running gateway infrastructure worth the control it buys, given what the managed alternatives already offer?
Gateway options that support latency-aware routing in production
Judge these on the things that actually matter for latency routing: how much overhead the gateway itself adds, how many providers it covers, how configurable the routing is, how it handles fallback, and what it shows you in observability.
Some open-source gateways, built in performance-focused languages like Go, add overhead low enough to functionally disappear from the latency budget, in the range of low tens of microseconds even at several thousand requests per second. Coverage across large numbers of models spanning multiple providers, all behind a single OpenAI-compatible API, is a key differentiator among the stronger options. Look for automatic fallback with health-aware routing and circuit breakers built in, adaptive load balancing across keys and providers, and semantic caching backed by something like Redis, alongside distributed tracing and cost analytics out of the box.
Other unified-API projects offer broad model support, often 100 or more LLMs, with strong ties to the Python AI ecosystem, which makes them a natural fit for teams already deep in that stack. That flexibility comes with self-hosting and full operational ownership, and benchmark comparisons tend to show meaningfully higher latency overhead at equivalent request volumes compared to gateways built in lower-level, performance-oriented languages.
Edge-deployed managed options bring strong caching and analytics along with a geographic advantage: running at the edge cuts latency for applications with users spread across regions, since requests don't have to travel as far to hit the routing layer.
Some gateways lean into structured inference patterns and GitOps-style operations, which suits teams that want routing policy expressed as version-controlled configuration rather than settings buried in a dashboard. Others, built with a strong focus on observability, function better as a monitoring and tracing layer than as a full routing gateway on their own.
Whatever gets chosen, benchmark it against real traffic patterns in staging before trusting it in production. P95 and P99 latency at the actual target request volume are the numbers that count, not the average latency figure sitting on a vendor's marketing page.
Observability requirements for a latency routing system you can actually trust
None of the routing logic above means anything without visibility into whether it's actually working. A routing system that can't show, in real time, which provider handled which request and how long it took, is a black box wearing a latency-optimization label.
Per-request tracing needs to capture TTFT, ITL, and total latency individually, tagged by provider, model, and route taken, so a spike can get traced back to its actual source instead of triggering a guessing game. Rolling percentile dashboards, P50, P95, P99, need to update continuously, not refresh once an hour, because a provider can degrade and recover within a window shorter than most dashboards even bother to check.
Fallback events need their own dedicated logging separate from normal request logs. When did a request fall back, from which provider to which, and did the fallback trip before or after the SLA threshold got breached? That gap, between when a provider actually started degrading and when the system reacted, is exactly the number that decides whether users noticed anything or not.
Cost and latency observability need to sit next to each other, not in separate tools, because a routing decision optimized for speed alone, without visibility into what it costs, is a decision made with one eye closed. Spend visibility broken down by team, project, key, model, and provider makes it possible to catch a routing policy that's technically hitting its latency targets while quietly burning far more budget than it needs to.
None of this is optional polish tacked onto a routing system after the fact. It's the mechanism that lets anyone verify the routing logic is doing what it's supposed to do, instead of just trusting that it probably is.


