
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| Field | Type | Description |
|---|---|---|
input | string | The prompt to classify (required). |
classes | string[] | Which heads to run: "difficulty", "ambiguity", "domain". Optional — defaults to all three. |
- 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| Label | What it means | Example mapping |
|---|---|---|
easy | Trivial change, any model handles it | Haiku, DeepSeek Flash, Gemini Flash |
medium | Moderate complexity, benefits from a capable model | Sonnet, GPT-5.5, Gemini Flash |
hard | Complex task, needs a strong model | Opus, GPT-5.5, Gemini Pro |
needs_info | Ambiguous prompt — difficulty didn’t clear the confidence threshold | Your default model |
| Label | What it means |
|---|---|
low | Well-specified request |
med | Some detail missing |
high | Underspecified; may need clarification |
| Label | What it means |
|---|---|
general | General-purpose prompt |
summary | Summarization / extraction |
coding | Code generation or editing |
design | Design / architecture |
data | Data / analytics |
/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. Request| Field | Type | Description |
|---|---|---|
input | string | The prompt to route (required). |
allowed_models | string[] | Restrict selection to these exact models, e.g. ["gpt-5.5", "claude-opus-4-8"]. Optional. |
allowed_providers | string[] | Restrict selection to these providers (openai, anthropic, gemini, deepseek). Optional. |
policy | string | "balanced" (default), "cost_efficient", "capability_heavy", or "domain_skills". |
default_model | string | Fallback returned as-is when the prompt is too ambiguous to size (needs_info). Must satisfy the allowed filter. Optional. |
allowed_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 and Gemini model, plus the one specific OpenAI model gpt-5.5.
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.
| Provider | Models |
|---|---|
openai | gpt-5.5 |
anthropic | claude-haiku-4-5-20251001, claude-sonnet-4-6, claude-opus-4-8 |
gemini | gemini-3.5-flash, gemini-3.1-pro-preview |
deepseek | deepseek-v4-flash, deepseek-v4-pro |
- cURL
- Python
- TypeScript
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.
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.