Prima API
Chat completions
POST /chat/completions, streaming and non-streaming, with tool calls on both paths.
Live Built and working today on the surface this card describes.
What it does
The OpenAI chat completions request and response shape. Same fields, same names.
stream: true serves Server-Sent Events: chat.completion.chunk deltas, then a final chunk carrying usage with an empty choices array, then data: [DONE]. Tool calls stream too.
tool_calls[].function.arguments is a JSON string on both paths. It was an object on the non-streaming path until 2026-08-03, which made a stock client's JSON.parse() throw. Fixed, and re-verified against the running gateway on 2026-08-04.
Inputs and outputs
| Request | model, messages, and the usual OpenAI fields. stream: true for SSE. |
|---|---|
| Response, non-streaming | An OpenAI chat completion object, with usage carrying prompt and completion token counts. |
| Response, streaming | SSE: chat.completion.chunk deltas, a final chunk with usage and an empty choices array, then data: [DONE]. |
| Tool calls | OpenAI tool-call shape. arguments is a JSON string on both paths. |
| Errors | The OpenAI-shaped envelope. See the errors card. |
Configuration
temperature | Defaults to 0.7 on the server. The default is not published in the API, so set it explicitly if 0.7 is wrong for your work. |
|---|---|
max_tokens | Accepted. On prima-deep it is silently raised to a floor of 8000, because reasoning models return empty content below roughly that. |
tool_choice | The string forms auto, none and required, and the object form that pins one tool, both passed through. The object form was refused with a 422 when measured against the earlier in-Labs gateway on 2026-08-04; the standalone service's request model accepts it, and that has not been re-measured on api.prima.li. |
stream_options | Accepted and ignored. The final usage chunk is always sent, whether or not include_usage was asked for. |
stream | true serves SSE. |
Limits
- Context windows, binary-searched against the live gateway on 2026-08-04:
prima-quickaccepted 60,004 tokens and refused 64,000, so about 64k.prima-coreaccepted 250,016 and refused 350,000.prima-deepaccepted 200,011 and was not probed higher, so at least 200k. - The three windows differ by roughly a factor of four and nothing in
GET /modelssays so. A client that assumes one number will compact far too early onprima-core, or sail past the limit onprima-quick. - Exceeding the window comes back as HTTP 502 with
type: api_errorandcode: upstream_error, not a 400. A prompt that is simply too long is therefore indistinguishable from an outage, and retry logic will retry something that can never succeed. max_tokensdoes not bound billed tokens. Measured:prima-quickwithmax_tokens: 16billed 115 completion tokens and finished withstop;prima-corebilled 1004 for a one-sentence answer. Reasoning tokens are counted and are not reported separately inusage.- There is no
Retry-Afterheader on a 5xx. - A body that fails validation answers 400,
code: invalid_request, withparamnaming the field and a fixed message. The offending value is never echoed, because on this service the offending value is a prompt.
Example
one streaming call
curl https://api.prima.li/api/prima/v1/chat/completions \
-H "X-API-Key: $PRIMA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "prima-core",
"messages": [{"role": "user", "content": "hello"}],
"stream": true}' Source of truth
philosophers_stone/platform/primaapi.md § Context windows, measured (probed 2026-08-04)philosophers_stone/platform/primaapi.md § `stream: true` works, and changed without a signalphilosophers_stone/platform/primaapi.md § Other measured facts (tool-call arguments, max_tokens billing)philosophers_stone/platform/primaapi.md § Open items (temperature, the prima-deep floor, no Retry-After; the 2026-08-04 `tool_choice` 422 measurement against the in-Labs gateway)prima/prima/api/v1.py (`ChatCompletionRequest`: `temperature` 0.7, `tool_choice` as string or object, `stream_options` accepted and ignored)prima/prima/app.py (a validation failure is 400 `invalid_request` with `param`, never an echo of the input)