Chat and completions
Two endpoints, one handler. A chat request is the OpenAI shape with a max price; a completion request is the legacy shape, remapped to a single user message on the way to the model.
Create a chat completion
curl https://api.openfill.ai/v1/chat/completions \ -H "Authorization: Bearer $OPENFILL_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "placeholder-large", "messages": [{"role": "user", "content": "hi"}], "max_price": 0.10}'Request body
The OpenAI fields first, then the six OpenFill adds. A field absent from this table is forwarded to the model untouched.
| Field | Type | Default | Meaning |
|---|---|---|---|
| model | string | the default model | A served model id. GET /v1/models lists them. |
| messages | array | required | At least one message with a role. Content is a string or an array of text parts. |
| stream | boolean | false | Server-sent events. The response is 200 once the order is queued. |
| stream_options | object | none | {include_usage: true} adds a final chunk carrying usage and x_market. |
| max_tokens | integer | the model's max output | Clamped down to the model's max output length. max_completion_tokens is read when this is absent. |
| temperature | number | none | 0 to 2. |
| top_p | number | none | 0 to 1. |
| stop | string or array | none | Up to four strings of up to 256 characters. |
| n | integer | 1 | Must be 1. Anything else is refused with 400. |
| tools, tool_choice | as OpenAI | none | Forwarded to the model. Calls are assembled from the stream and returned whole. |
| response_format, seed, user | as OpenAI | none | Forwarded to the model untouched. |
| logprobs, top_logprobs | as OpenAI | none | Forwarded. The response carries choices[0].logprobs. |
| max_price | number | key or account | The most you will pay, in USD per 1M input tokens at the reference mix (90% cached, 2% output). 0 is legal and waits for a free market. None anywhere is 400 missing_bid. |
| price_lock | boolean | account setting | Bid the published quote instead. Starts within 10 seconds or cancels with 429 capacity_unavailable. |
| max_wait | integer | account setting | Seconds the order may wait, 10 seconds to 30 days. Alias expires_after. Below the floor it is raised; above the ceiling it is 400. |
| detached | boolean | false | Answer 202 with an order id and run in the background. Refused with 400 alongside stream. |
| store | boolean | account setting | false keeps nothing durable for this request. |
| retention_days | integer | account setting | Keep this request's data for fewer days. Clamped down to the account setting. |
Response
The OpenAI chat.completion object with exactly one choice, and usage.x_market added. model echoes what you sent.
{ "id": "chatcmpl-...", "object": "chat.completion", "created": 1756700000, "model": "placeholder-large", "choices": [{ "index": 0, "message": {"role": "assistant", "content": "...", "reasoning_content": "..."}, "finish_reason": "stop", "logprobs": null }], "usage": { "prompt_tokens": 12, "completion_tokens": 40, "total_tokens": 52, "prompt_tokens_details": {"cached_tokens": 8}, "x_market": {"level_at_execution": 0.0143, "bid": 0.1, "charged_usd": 0.0000174} }}| Field | Meaning |
|---|---|
| message.content | The answer. null for a turn that is only tool calls. |
| message.reasoning_content | A reasoning model's chain of thought, when the model sends one. |
| message.tool_calls | Present when the model called tools. |
| logprobs | {content: [...]} when you asked for logprobs, else null. |
| usage.x_market.level_at_execution | The level the order was charged against. |
| usage.x_market.bid | What the order rested at: your max price, or the quote when locked. |
| usage.x_market.charged_usd | The debit, in USD to nine decimal places. |
Response with detached: true
202 { "id": "<order_id>", "object": "order", "status": "queued", "expires_at": "...", "result_url": "/v1/orders/<order_id>/result", "result_stored": true, "result_retention_days": 7}warning is added when nothing will be stored. The detached orders page has the collection semantics.
Response with stream: true
200 and a text/event-stream body: queue comments while the order waits, the model's chunks, a usage chunk when asked for, and data: [DONE]. Every frame is on the streaming page.
Statuses this endpoint answers
| Status | When |
|---|---|
| 200 | The completion, or a stream. |
| 202 | detached: true, once the order is queued. |
| 400 | Malformed body, n or best_of above 1, missing_bid, invalid_output_cap, model_deprecated, a bad X-Max-Price or X-Expires-After, or a request the model refused. |
| 401, 403 | The key, or the account behind it. |
| 402 | insufficient_balance at the current level. |
| 404 | model_not_found. |
| 408 | order_expired: the wait passed max_wait. |
| 409 | order_canceled while it waited, or idempotency_error on a mismatched replay. |
| 410 | result_unavailable on a replay of a finished order with nothing stored. |
| 413 | The body is over the limit. |
| 429 | rate_limit_error on a ceiling, capacity_unavailable on a missed lock window, result_pending on a replay still being stored. |
| 500, 502, 503 | Our side, the backend mid-stream, or the backend unavailable. Only the 500 whose message says so was charged. |
Create a completion
curl https://api.openfill.ai/v1/completions \ -H "Authorization: Bearer $OPENFILL_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "placeholder-large", "prompt": "Say hi", "max_price": 0.10}'The same handler and the same extensions, with the legacy shape's differences:
| Field | On /v1/completions |
|---|---|
| prompt | A string, or an array of one string. It becomes a single user message. Absent, it is empty. |
| tools, tool_choice, functions, function_call | Refused with 400: the response has nowhere to put a call. |
| logprobs, top_logprobs, echo, suffix | Removed before the model sees them. The response has logprobs: null. |
| max_completion_tokens | Read as the output cap when present; a non-integer or a value under 1 is 400 invalid_output_cap. |
{ "id": "cmpl-...", "object": "text_completion", "created": 1756700000, "model": "placeholder-large", "choices": [{"index": 0, "text": "...", "finish_reason": "stop", "logprobs": null}], "usage": {"prompt_tokens": 4, "completion_tokens": 2, "total_tokens": 6, "x_market": {"level_at_execution": 0.0143, "bid": 0.1, "charged_usd": 0.0000012}}}Streaming on this endpoint sends text_completion chunks with choices[0].text, and the same queue comments and usage chunk as chat.