docketrouter
DocumentationBrowse
API reference

Catalog and routing

Public, no key required. These endpoints exist so that the prices, context windows and benchmark scores on this site can be checked against the same data the product runs on, rather than taken on trust.

GET /models

The whole upstream catalog, annotated.

GET/modelspublic
200 OK, abridged
{
  "object": "list",
  "callable_models": ["deepseek/deepseek-v4-flash"],   // the allowlist, stated up front
  "data": [
    {
      "id": "deepseek/deepseek-v4-flash",
      "name": "DeepSeek V4 Flash 0423",
      "owned_by": "deepseek",
      "context_window": 1048576,
      "pricing": { "input": "9.7475e-8",              // USD per token, markup already applied
                   "output": "1.9495e-7",
                   "input_cache_read": "1.9495e-8" },
      "callable": true,                               // listed is not the same as callable
      "legal": null                                   // benchmark summary, when we have run one
    }
  ]
}
FieldMeaning
callable_modelsTop-level array of the ids the chat endpoint will accept. Filter on this rather than discovering the allowlist through a 400.
data[].callablePer-model boolean, true for the same set. 398 models are listed today and 1 is callable.
data[].pricingUSD per token, as strings, with the 1.25 multiple already applied. Multiply by 1,000,000 for the per-million figure shown on the site.
data[].legalBenchmark summary when the model has been run: overall, coverage, illustrative and a per-task score map. null otherwise.
Callable and benchmarked are currently disjoint

10 models carry benchmark results and 1 model is callable, and today they are not the same model. That is the honest state of a benchmark suite that runs ahead of the allowlist, and it has a consequence for POST /route described below.

GET /models/:id

GET/models/:idpublic

One catalog entry plus legal, an array of the latest benchmark run rows for that model. The id contains a slash and is used as a path, so deepseek/deepseek-v4-flash becomes /models/deepseek/deepseek-v4-flash. An unknown id returns 404 with the standard envelope.

curl https://docketrouter.ai/api/v1/models/deepseek/deepseek-v4-flash

POST /route

Pick the best-scoring callable model under a set of ceilings.

POST/routepublic
FieldTypeDefaultMeaning
taskstring"overall"A benchmark task id, or overall for the aggregate. An unknown task returns 400.
max_input_price_per_mnumbernoneCeiling on input price per million tokens.
max_latency_msnumbernoneCeiling on measured benchmark latency.
min_scorenumber 0 to 1noneFloor on the task score.
include_illustrativebooleanfalseWhether to consider seeded example runs rather than only real ones.
request
curl -X POST https://docketrouter.ai/api/v1/route -H 'content-type: application/json' \
  -d '{ "task": "hallucination", "max_input_price_per_m": 5, "min_score": 0.9 }'
This endpoint returns 404 for every query today

Routing only recommends models the chat endpoint will actually accept, because a routing answer that then 400s is worse than no answer. The single callable model has no benchmark rows yet, so the candidate set after filtering is always empty and the response is:

{"error":{"message":"no callable model satisfies constraints","type":"not_found_error","task":"overall"}}

That is correct behaviour and a useless answer, and it stays that way until a benchmarked model becomes callable or the callable model is benchmarked. Until then, treat callable_models from GET /models as the routing answer and do not build a hard dependency on this endpoint.

the shape it returns when a candidate exists
{ "task": "hallucination",
  "model": "…", "score": 0.94, "latency_ms": 2100,
  "illustrative": false, "input_price_per_m": 1.2,
  "alternatives": [ … up to 5 … ] }

Health

GET/api/healthpublicNote the path: this one is outside /api/v1.
200 OK, abridged
{ "status": "ok",
  "checks": { "db": "ok", "fused_index": "online", … },
  "ms": 285, "version": "0.1.0" }
  • fused_index is the check that predicts degraded.index_unreachable on chat requests. Watch it.
  • The check does real work, so it can take several seconds under load. Do not put it on a one-second liveness probe.
  • For a per-key health check that costs nothing and proves your credentials work, use GET /auth/key instead.

Something here wrong or missing? Mail hello@docketrouter.ai with the request_id and we will fix the docs or the API, whichever is broken.