Quick Start
Ask the router which model to use, then call it:- cURL
- TypeScript
- Python
/v1/router/classify to get the raw signals instead, and /v1/router/multimodel when you’d rather hand Morph your model list and let it choose.
/router/classify
Runs the requested classifier heads against your prompt and returns the raw labels. You decide what each label means for your stack. Request- cURL
- Python
- TypeScript
label, class_id, confidence, and meets_threshold (whether confidence cleared the head’s threshold). When difficulty does not meet its threshold, treat it as needs_info — the prompt is too ambiguous to size confidently.
Labels
The classifier heads return these labels. You decide what each means for your stack. Difficulty
Ambiguity
Domain
/router/multimodel
Hand the router your candidate models (or whole providers) plus a policy. It classifies the prompt and returns the single best model to call — no mapping table to maintain. Requestallowed_models and allowed_providers are unioned — a model qualifies if it matches either. Leave both empty to consider every model in Morph’s catalog. The example below allows every Anthropic model plus Z.ai’s GLM-5.2: GLM-5.2 takes over the Sonnet-level tier outright and contests Opus on hard-tier requests, while Fable 5 keeps the one job nothing else can do — the high-ambiguity apex.
Policies
balanced(default) — pick a capable model; break ties on cost.cost_efficient— minimize cost; tolerate a slightly underqualified model.capability_heavy— maximize capability for the request; never trade quality for cost.domain_skills— route by domain match first; pick the specialist for the request’s domain.
allowed_models and allowed_providers empty to consider every model below.
- cURL
- Python
- TypeScript
anthropic and zai both allowed: easy prompts still go to Haiku, medium (“Sonnet-level”)
prompts now go to glm-5.2 instead of Sonnet, and hard prompts go to glm-5.2 too — except
med-ambiguity hard prompts, which stay on claude-opus-4-8, and high-ambiguity prompts, which
always go to claude-fable-5. Drop zai from allowed_providers to fall back to the
Anthropic-only tiers.
Response
model is what you call next. The classifier signals (difficulty, ambiguity, domain) are echoed back so you can act on them too — e.g. show a “let’s clarify” prompt when difficulty is needs_info. The ambiguity and domain fields are present only when those heads cleared their threshold; treat a missing field as “no signal.” If the prompt resolves to needs_info and you passed a default_model, that model is returned as-is.
Real-World Example
Route dynamically in production to cut costs while keeping quality. Hand the router your candidate models, then call whatever it returns:- TypeScript
- Python
default_model already covers the needs_info case.
Integrate with Claude Code
Route every Claude Code turn through the router with no change to how developers work. A local proxy sits between Claude Code and Anthropic (ANTHROPIC_BASE_URL points at it), classifies each turn, and picks the cheapest Claude model — and reasoning effort — that can handle it, following your org’s routing policy.
Requires macOS or Linux, Node 22+, and the claude CLI.
1. Install with your Morph API key:
~/.morph/ccr-router, and gives you a morph-claude command. Re-running the one-liner upgrades in place.
2. Authenticate upstream. By default the proxy uses your Claude Pro/Max subscription — run claude login once. To use your org’s Anthropic key instead, add it to the install:
morph-claude instead of claude:
{model, effort}, then clamped to the models that user is permitted. One decision per turn, 1.5s classify timeout; if the classify ever fails, routing fails open to your default model.
Set the policy. Admins configure the routing matrix and per-user permissions in the dashboard under Administration → Model Router. Edits reach every developer within the hour — no redeploy, no reinstall. The Analytics tab shows the model mix, turn volume, and estimated savings vs sending every turn to Opus. Prefer to own the policy? Point the proxy at a local router-matrix.json or an endpoint you host via MORPH_MATRIX_FILE / MORPH_MATRIX_URL.
Routing metrics are metadata-only — no prompt or completion text ever leaves the machine. Set
MORPH_METRICS_DISABLED=1 to send nothing at all.Edge / Cloudflare Workers
fetch is available natively at the edge, so you can call the router from a Cloudflare Worker, Vercel Edge Function, or Deno with no SDK:
The
@morphllm/morphsdk/edge build ships a RawRouter helper, but it targets the legacy /router/raw endpoint. For the current endpoints, call them directly with fetch as shown above.API Reference
Both endpoints arePOST https://api.morphllm.com/... with an Authorization: Bearer YOUR_API_KEY header.
- /router/classify
- /router/multimodel
When to Use
Use the router when:- Processing varied user requests (simple typo fixes to complex architecture tasks)
- You want to minimize API costs without manually classifying prompts
- Building cost-conscious AI products with mixed complexity workloads
- All tasks need the same model tier (e.g., always Opus for agentic coding)
- The ~180ms routing latency matters more than cost savings
- You need deterministic model selection for testing or compliance
Performance
- Latency: ~180ms average
- Parallel: Can run in parallel with other work
- HTTP/2: Connection reuse for subsequent calls
Deprecated endpoints
/router/raw
Returns just a difficulty label. Use/v1/router/classify instead for new code.
- cURL
- Python
- TypeScript SDK
{ "difficulty": "easy", "confidence": 0.93 }balanced (default) balances cost and quality; aggressive optimizes harder for cost, pushing more prompts to easy. Returns difficulty (easy | medium | hard | needs_info).
For edge environments (Cloudflare Workers, Vercel Edge, Deno), use @morphllm/morphsdk/edge:
/router/
Returns a provider-specific model name directly instead of a difficulty label, foropenai, anthropic, and gemini. Use /v1/router/multimodel instead — it does the same model selection with control over the candidate set and policy.
Under the hood these now call the multimodel router constrained to that provider, so they keep working with no changes on your side.
{ "model": "claude-haiku-4-5-20251001", "confidence": 0.93 }
The SDK still exposes morph.routers.anthropic.selectModel(), morph.routers.openai.selectModel(), and morph.routers.gemini.selectModel() for backwards compatibility. Migrate to /v1/router/multimodel.