https://api.sdvm.ai/v1 speaks the OpenAI chat-completions protocol. Any client that can talk to OpenAI — the official SDKs, LangChain, LiteLLM, a curl — can talk to SDVM by changing the base URL and the key. Nothing to install.
The model is picked by the model id; the samples are the user message; the result is the assistant message. Your sdvm_ key authenticates and is billed exactly as on the native routes.
Models
The unversioned ids
sdvm/audit, sdvm/fix and sdvm/refine still work and are not listed.
GET /v1/models lists them with pricing and context limits, in the OpenAI shape, and GET /v1/models/{id} (client.models.retrieve("sdvm/audit-1")) returns the card for one. An unknown id is a 404 with code: model_not_found.
The message
The last user message carries the samples. Three forms are accepted:- Envelope (all options)
- Bare array
- Plain text
A JSON object with
data plus any of the options below.
Out-of-range or wrongly-typed options are a
400 before any work is done.
The reply
choices[0].message.content is a JSON string: the input array, each sample carrying its audit block (audit, refine), its fix block where the fix changed it — {"changes": [...], "flagged": bool, "attempts": int}; a text sample left untouched carries none — and, on refine, its reaudit block (null for a sample the fix left alone). Parse it with json.loads. finish_reason is always "stop".
usage reports the tokens the whole pipeline consumed upstream — every audit pass, every fix pass — which is what the request billed on, at the rates in GET /v1/models. It is deliberately not the size of the strings on the wire: one visible request fans out into votes audits and up to max_attempts fixes per sample, and that fan-out is the work being paid for.
Sampling parameters (temperature, top_p, max_tokens, n, tools, response_format) are accepted and ignored. Audit and fix are constrained transformations and always run at the lowest temperature the underlying model allows.
Streaming
stream: true returns server-sent events. The pipeline is not incremental, so the content arrives at the end — but the connection is held open with comment keepalives while it runs, which is what stops a proxy or client timeout from killing a multi-minute refine. The final chunk always carries usage, whether or not you asked for it via stream_options.
{"error": ...} event in the body (the HTTP status is already 200), followed by data: [DONE] — the shape OpenAI-compatible clients already parse.
curl
Errors
Errors use OpenAI’s envelope,{"error": {"message", "type", "code"}}, so client libraries raise their usual exceptions.
Limits and billing
- Any number of samples per request. Requests larger than a native call is allowed are split into batches internally and processed in order; only the estimated cost is capped, at $5.00 per request.
- 600 requests per minute per key on this endpoint (the native routes allow 100).
- Billing is identical to the native routes: 1¢ is held when the request starts, the real token cost is settled when it finishes, and the hold is released if the request fails. If the balance cannot cover the settled cost the request returns
402and no result. /fixand/refineinput and output are kept for your history for 30 days, the same as natively. Audit stores nothing.