Prices and model names in this article were updated on 31 October 2026.

Most production AI applications use one model for everything. Someone picks a flagship, ships it, and nobody revisits the choice. The price gap between a vendor's flagship and its small model is wide as of October 2026. Claude Opus 5.5 costs $4 per million input tokens and $20 per million output. Claude Haiku 5.5 costs $0.10 and $0.50, a 40x difference. OpenAI's GPT-6 Astra ($10 / $50) and GPT-6 Luna ($0.10 / $0.50) are 100x apart. Your traffic may be mostly "summarize this", "format this JSON" or "classify this text". If so, you pay flagship prices for work a small model can do.

Model routing fixes this. A classifier sits in front of your LLM calls, decides which model each request needs, and sends it there. Simple tasks go to a cheap, fast model. Hard tasks go to the flagship. Done well, the difference shows up on the invoice and nowhere else.

What signals to route on

A router can decide on any of the signals below, alone or combined.

Query length

Short queries are usually simple. A 15-word question rarely needs a flagship. Pick a length threshold from your own logs, for example 50 tokens. Below it, requests default to the small model unless another signal says otherwise.

Task type

Run a lightweight classifier, or prompt a cheap model, to tag the query: summarization, classification, extraction, generation, reasoning, code. Map each tag to a model tier.

Complexity tags

Some systems let users or upstream code tag requests with "complexity": "high". Crude, but it works.

Keywords

Send a query to the stronger model if it contains "analyze", "compare", "explain why" or domain jargon.

Output requirements

Some requests need structured JSON where schema compliance matters. Route those to a model you have tested for that. A request that only reformats text can go to the cheap model.

Routing tiers

A two-tier system covers most use cases. Prices are per million tokens, input / output, as of October 2026.

Tier Models Use for
Economy Claude Haiku 5.5 ($0.10 / $0.50), GPT-6 Luna ($0.10 / $0.50), Gemini 3.1 Flash-Lite ($0.25 / $1.50), DeepSeek V4.1-Flash ($0.30 / $1.20 at peak) Summarization, classification, extraction, simple Q&A
Frontier Claude Opus 5.5 ($4 / $20), GPT-6 Astra ($10 / $50), Gemini 4 Argon ($2 / $10 until 30 November 2026, $4 / $20 from 1 December) Multi-step reasoning, code generation, nuanced analysis

Two caveats on the economy row. Claude Haiku 5.5 moves to $0.50 / $2.50 once a prompt exceeds 100,000 tokens. DeepSeek charges half the peak price off-peak.

A middle tier sits between them. Claude Sonnet 5.5 and GPT-6.1 Sol cost $2 / $10. Gemini 3.8 Flash costs $0.75 / $3.75 until 31 December 2026 and $1.50 / $7.50 from 1 January 2027. Add this tier if your evaluation shows a class of tasks the small model fails and the middle model passes. Most of the price gap lies below the middle tier. Inside Anthropic's line, Opus 5.5 to Sonnet 5.5 is a 2x step and Sonnet 5.5 to Haiku 5.5 is 20x. For more models per tier, see the AI model map and the October 2026 price snapshot.

A router sends 1M monthly queries through a cheap classifier: 70% go to Claude Haiku 5.5 and 30% to Claude Opus 5.5, cutting the bill from $14,000 to $4,478 1M queries a month, 1,500 tokens in and 400 out each 1M queries per month Classifier GPT-6 Luna, $33 70% 30% Claude Haiku 5.5 $245 Claude Opus 5.5 $4,200 All on Opus 5.5 $14,000 Routed $4,478
A 70/30 split cuts the monthly bill in the worked example below by 68%; measure the split on your own traffic.

Python implementation with LiteLLM

LiteLLM is an open-source library that calls many vendors' models through one function (its documentation says "100+ LLMs"). It handles each provider's SDK and authentication underneath.

from litellm import completion

CLASSIFIER = "openai/gpt-6-luna"
ECONOMY = "openai/gpt-6-luna"
FRONTIER = "anthropic/claude-opus-5-5"

def classify_complexity(query: str) -> str:
    """Quick, cheap classifier to route queries."""
    result = completion(
        model=CLASSIFIER,
        messages=[{
            "role": "user",
            "content": f"""Classify this query as 'simple' or 'complex'.
Simple: summarization, classification, formatting, basic Q&A.
Complex: multi-step reasoning, code generation, nuanced analysis.

Query: {query}

Reply with exactly one word: simple or complex"""
        }],
        max_tokens=5
    )
    return result.choices[0].message.content.strip().lower()


def routed_completion(query: str, system_prompt: str = "") -> str:
    complexity = classify_complexity(query)

    model = FRONTIER if complexity == "complex" else ECONOMY

    messages = []
    if system_prompt:
        messages.append({"role": "system", "content": system_prompt})
    messages.append({"role": "user", "content": query})

    response = completion(model=model, messages=messages, max_tokens=1024)

    # Log for cost tracking
    print(f"[route] complexity={complexity} model={model} "
          f"tokens={response.usage.total_tokens}")

    return response.choices[0].message.content

The classifier call costs little. Assume 300 input tokens (instructions plus query) and 5 output tokens on GPT-6 Luna. That is 300 × $0.10 / 1M + 5 × $0.50 / 1M = $0.0000325 per call, or $32.50 per million calls.

Routing with the LiteLLM Router

LiteLLM's Router class groups deployments under a model name and adds load balancing, retries and fallbacks. The routing strategies its documentation lists are simple-shuffle (the default), least-busy, usage-based-routing, latency-based-routing and cost-based-routing. A strategy chooses among the deployments inside one model group. It does not decide which group a request belongs to; your classifier still does that.

from litellm import Router

router = Router(
    model_list=[
        # Two deployments in the "economy" group; cost-based routing picks between them
        {
            "model_name": "economy",
            "litellm_params": {
                "model": "openai/gpt-6-luna",
                "api_key": "your-openai-key"
            }
        },
        {
            "model_name": "economy",
            "litellm_params": {
                "model": "gemini/gemini-3.8-flash",
                "api_key": "your-google-key"
            }
        },
        {
            "model_name": "frontier",
            "litellm_params": {
                "model": "anthropic/claude-opus-5-5",
                "api_key": "your-anthropic-key"
            }
        }
    ],
    routing_strategy="cost-based-routing",
    fallbacks=[{"economy": ["frontier"]}],  # escalate if the cheap group fails
    num_retries=2
)

response = router.completion(
    model="economy",
    messages=[{"role": "user", "content": "Summarize this article: ..."}]
)

Set routing_strategy="latency-based-routing" if response time matters more than price. The fallbacks entry sends a request to the frontier group when every economy deployment fails or hits its rate limit. Load balancing across several gateways is covered in AI load balancers.

OpenRouter as a simpler alternative

If you don't want to run your own routing code, OpenRouter sells access to many vendors' models through one OpenAI-compatible endpoint. You send one request to openrouter.ai/api/v1 and name a model.

curl https://openrouter.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $OPENROUTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-6-luna",
    "messages": [{"role": "user", "content": "Summarize: ..."}]
  }'

OpenRouter also has an Auto Router, "model": "openrouter/auto". Its documentation says a lightweight classifier assigns each prompt one of about 30 task types. It then ranks models by what OpenRouter users spend on for that task type over the trailing 7 days. A cost_tier setting (low to max) narrows the candidates, and allowed_models restricts them, for example to anthropic/*. The router adds no fee; you pay the selected model's price. Its ranking reflects popularity. It runs no quality test on your data, so treat its pick as a starting point.

On cost, OpenRouter's FAQ says it passes through provider prices "without any markup" on inference. A fee applies when you buy credits. You can also bring your own provider keys. Pay-as-you-go accounts get $25,000 a month of such usage with no fee. Above that, they pay 5% of what the same model and provider would cost on OpenRouter.

Measuring the impact

Track these numbers before and after you add routing:

# In your logging middleware
cost_per_model = {
    "anthropic/claude-opus-5-5": 0.0,
    "openai/gpt-6-luna": 0.0,
}
requests_per_model = {
    "anthropic/claude-opus-5-5": 0,
    "openai/gpt-6-luna": 0,
}

# After 1 week, calculate:
# - share of requests routed to the economy tier
# - cost savings vs an all-frontier baseline
# - quality regression rate (human review or LLM-as-judge on a sample)

The saving depends on one number that only your own traffic can give. That number is the share of requests the small model handles acceptably. Here is a worked example with an assumed 70% share. The workload is 1M queries a month, each with 1,500 input tokens and 400 output tokens.

Line Arithmetic Monthly cost
All on Claude Opus 5.5 1.5B in × $4 + 400M out × $20 $14,000
Routed: 700,000 to Haiku 5.5 1.05B in × $0.10 + 280M out × $0.50 $245
Routed: 300,000 to Opus 5.5 450M in × $4 + 120M out × $20 $4,200
Routed: classifier on GPT-6 Luna 1M calls × $0.0000325 $32.50
Routed total $4,477.50

That is a 68% cut. A shortcut gives the same figure: savings ≈ routed share × (1 − small price ÷ flagship price) = 0.7 × (1 − 1/40) ≈ 68%. At a 40% routed share the same formula gives 39%. The cost audit checklist shows how to find the routing candidates in your logs.

Don't route blindly. For the first two weeks, log every routing decision. Regularly sample economy-tier outputs and compare them with what the frontier model would have produced. Tune the classifier until the error rate is one you accept.

What routing doesn't fix

Routing helps with model selection. It does nothing about inefficient prompts, unnecessary agent loops or excess context. Before you build a routing layer, trim your system prompts, set max_tokens per endpoint and remove unused tool definitions. A bloated request sent to a cheap model can still cost more than a lean one sent to a flagship.

What to do next

Sources