router.Nyuro.ai

Unified API

One OpenAI-compatible endpoint for every model, including your own. Request shape, model-name forms, and authentication.

Point your existing OpenAI SDK at https://api.routing.nyuro.ai/v1/chat/completions and Nyuro routes the request to the best (provider, model) — cloud or your own VPS — and returns an identical response shape.

Base URL

The gateway is served from a single host:

https://api.routing.nyuro.ai

Whether you include /v1 depends on your client

An OpenAI SDK appends the path itself, so it needs the /v1. A raw HTTP client does not, so it must omit it and write the full path. Getting this backwards produces /v1/v1/... and a bare 404 that looks exactly like an endpoint that was never deployed.

ClientBase URL to configureResulting request
OpenAI SDK (Python, Node, …)https://api.routing.nyuro.ai/v1SDK appends /chat/completions
curl / fetch / raw HTTPhttps://api.routing.nyuro.aiyou write /v1/chat/completions
# ✅ 200
curl https://api.routing.nyuro.ai/v1/chat/completions ...

# ❌ 404 — the SDK-style base URL with a hand-written path
curl https://api.routing.nyuro.ai/v1/v1/chat/completions ...

Endpoint

POST https://api.routing.nyuro.ai/v1/chat/completions
Authorization: Bearer nyu_live_…
Content-Type: application/json

The request and response bodies mirror OpenAI's Chat Completions API. Any field the underlying provider supports (temperature, max_tokens, tools, tool_choice, stream, …) is forwarded through.

Two surfaces: /v1 and /api/v1

The API exposes two prefixes, and the distinction is deliberate:

  • /v1/… — the OpenAI-compatible surface. Five routes, shaped so an unmodified OpenAI client works against them.
  • /api/v1/… — the full platform surface, including everything in /v1 plus keys, budgets, governance, observability, media, and collections.

These five routes are aliases — identical handlers, reachable under either prefix, so pick whichever suits the client:

/v1/chat/completions      ≡  /api/v1/chat/completions
/v1/embeddings            ≡  /api/v1/embeddings
/v1/models                ≡  /api/v1/models
/v1/industries            ≡  /api/v1/industries
/v1/router/preview        ≡  /api/v1/router/preview

Everything else is /api/v1-only. Notable endpoints beyond chat:

EndpointPurpose
POST /v1/embeddingsEmbeddings, OpenAI-compatible
GET /v1/modelsList routable models
POST /v1/router/previewAsk the router what it would pick, without spending
GET /api/v1/catalog/modelsFull model catalog with pricing and capabilities
GET /api/v1/media/modelsImage / video generation models
POST /api/v1/collections/{collection}/searchManaged RAG search — see Collections
GET /api/v1/metrics/usageUsage rollups — see Observability

The complete, always-current surface is the OpenAPI document at /openapi.json. It does not yet declare a servers block, so SDK generators, Postman and Swagger UI will ask you for the base URL above rather than inferring it.

Authentication

Issue and rotate keys from Settings → API Keys in the console. Keys are shown once on creation and begin with nyu_live_. Send the key as a bearer token (recommended) or via X-API-Key:

Authorization: Bearer nyu_live_<suffix>
# or
X-API-Key: nyu_live_<suffix>

Scopes

Keys carry per-capability scopes. A scope grants one family of endpoints, and a key only works where it has been granted:

ScopeGrants
chat/v1/chat/completions
embeddings/v1/embeddings and the collections endpoints
models.read/v1/models

A working chat key is not automatically an embeddings key

Scopes are granted per key, so a key that routes chat perfectly can still be rejected on embeddings. The failure is a 403, not a 401:

{"detail": "api key missing required scope 'models.read'"}

From the client side that is easy to mistake for an outage or an undeployed endpoint. If you get a 403, check the key's scopes before you check anything else.

Scopes are set when a key is created and can be changed on an existing key from Settings → API Keys. The table above covers the scopes gating the public OpenAI-compatible endpoints; administrative endpoints under /api/v1 carry their own scopes.

The model field

You can pass any of these forms — all resolve through the router:

  • Concrete aliasgpt-4o-mini, claude-3-5-sonnet, Qwen/Qwen3-32B-AWQ, …
  • Industry tagindustry:legal, industry:code, …
  • Strategy hintstrategy:cost, strategy:quality, …
  • Autoauto
  • Fallback array"models": ["gpt-4o", "gpt-4o-mini"] (OpenRouter-style)

See Models & aliases for the full reference.

Collections

Collections are a managed RAG API. You send plain text; chunking, embedding and vector storage happen server-side, so there is no vector database to run and no embedding pipeline to build.

All four endpoints require a key with the embeddings scope. A collection name matches ^[A-Za-z0-9._-]+$ (max 128 chars).

Ingest a document

PUT /api/v1/collections/{collection}/documents/{source_id} — chunk, embed and store under a source_id you choose. Re-sending the same source_id replaces that document, which makes re-indexing idempotent.

curl -X PUT https://api.routing.nyuro.ai/api/v1/collections/handbook/documents/onboarding-v2 \
  -H "Authorization: Bearer nyu_live_…" \
  -H "Content-Type: application/json" \
  -d '{"text": "Full plain text of the document…", "chunk_chars": 1200}'

chunk_chars is optional (100–8000); omit it to use the server default. The response reports what was written, and in which vector space:

{"source_id": "onboarding-v2", "chunks_written": 14,
 "model": "Qwen/Qwen3-Embedding-0.6B", "dimensions": 1024}

POST /api/v1/collections/{collection}/search — semantic search over the collection.

curl -X POST https://api.routing.nyuro.ai/api/v1/collections/handbook/search \
  -H "Authorization: Bearer nyu_live_…" \
  -H "Content-Type: application/json" \
  -d '{"query": "how do I request leave?", "limit": 5}'

limit defaults to 5 (max 50). Each returned passage carries content, score, source_id and chunk_index — enough to cite the source chunk directly in a grounded answer.

Leave the instruction alone unless you know why

Queries are embedded with the instruct prefix the embedding model expects, while documents are embedded bare. That asymmetry is what the model is trained for. The optional instruction field overrides the query-side prefix — setting it incorrectly quietly degrades result quality without raising an error.

Inspect and delete

GET /api/v1/collections/{collection} returns chunk and document counts grouped by embedding model. Two models in one collection means a migration is in flight, and this is how you see which rows sit in which vector space.

DELETE /api/v1/collections/{collection} removes the collection. Pass ?model=… to delete only one model's vectors — which is what makes an embedding-model migration reversible: embed into the new space alongside the old, cut reads over, then delete only the old.

The 30-second migration from OpenRouter

Same OpenAI SDK, two-line change:

from openai import OpenAI

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key="sk-or-v1-…",
)

resp = client.chat.completions.create(
    model="anthropic/claude-3-5-sonnet",
    messages=[{"role": "user", "content": "Hi"}],
)
from openai import OpenAI

client = OpenAI(
    base_url="https://api.routing.nyuro.ai/v1",
    api_key="nyu_live_…",
)

resp = client.chat.completions.create(
    model="claude-3-5-sonnet",            # plain alias
    messages=[{"role": "user", "content": "Hi"}],
    extra_body={"metadata": {"industry": "legal"}},
)

In return you get a single observability dashboard, a single bill, automatic fallback, industry-aware routing, and the option to run inference on your own VPS without touching client code. Full walkthrough: Migrating from OpenRouter.

Response headers

Routing decisions are surfaced on every response via X-Nyuro-* headers — see Routing → Transparency headers.

On this page