The API speaks Chat Completions. If you are using the OpenAI SDK, you change the base URL and the key.
Anything that speaks to an OpenAI-compatible endpoint works the same way — LangChain, LlamaIndex, Vercel AI SDK, Cline, or your own fetch call.

What works unchanged

Responses come back in the standard shape: choices[].message, finish_reason, usage, and chatcmpl--prefixed ids.

What is different

The upstream’s own default spends multiple seconds thinking before the first token on even trivial prompts, and reasoning is billed as output tokens. If you want deeper reasoning, ask for it explicitly.
The hold against your balance is priced at max_tokens, not at what the model writes. On OpenAI an over-large max_tokens is free; here it locks up balance until the request settles. Name a real ceiling — the default is 8,192.
The relayed models have no internet access, so the router searches for them. If your request includes its own tools, the router’s are not injected. To control it explicitly, pass "web_search": false / "web_fetch": false. See Web search.
x-nanorouter-request-id on every response; x-nanorouter-authorized-usdc for what was held; x-nanorouter-charged-usdc for what it cost — non-streaming, and only when the response finishes inside 45 seconds.

What is not supported

These return a 400 rather than being silently ignored:
  • n > 1 — one completion per call.
  • Non-text content parts. Message content must be a string or an array of {"type": "text"} parts. Images and audio are refused.
  • Message roles outside system · developer · user · assistant · tool.
  • response_format types other than text, json_object, json_schema.
And these endpoints do not exist — the surface is /v1/chat/completions and /v1/models:
  • /v1/embeddings
  • /v1/completions
  • /v1/responses
  • /v1/images/*, /v1/audio/*, /v1/files, /v1/batches
Other parameters OpenAI accepts — top_p, presence_penalty, frequency_penalty, seed, logprobs, stop, logit_bias, user — are accepted by the request parser but not forwarded upstream. Do not rely on them taking effect.

Model names

There is no gpt-4o here. The catalogue is:
An unknown model is a 404 with code: "model_not_found", not a fallback. Map your model names at the boundary rather than letting an old constant fail in production. GET /v1/models is public — no key needed — so you can fetch the live list at startup.

Error handling

The error envelope is OpenAI’s, so openai.APIError and friends work as they do today:
The one status you will not have handled before is 402 — your USDC balance will not cover the quote. Catch it and top up; retrying will not help. 429 here also means something different from an OpenAI rate limit: too much of your money is already in flight, unsettled. It clears on its own as requests settle, so a short backoff works.

Full error reference

Every status the API returns, and whether a retry is worth attempting.