API
API reference
Routes, parameters, headers and errors for api.conifer.build. One key and one base URL cover every route below.
| Host | Base URL | Auth |
|---|---|---|
| Gateway | https://api.conifer.build | Authorization: Bearer $CONIFER_API_KEY |
Credentials go in Authorization: Bearer or x-api-key. The Azure SDK’s api-key header is accepted too. The OpenAI routes sit under /v1; the Anthropic base URL is the host with no suffix. conifer serve answers the same wire on http://127.0.0.1:8080 with no auth on loopback. See Serve an endpoint.
curl https://api.conifer.build/v1/chat/completions \
-H "Authorization: Bearer $CONIFER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-haiku-4-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "three names for a build cache"}]
}'Models differ in what they support. Read caps on GET /v1/models before you send tools or images. Reasoning traces and cache details appear in a response only when the model produced them.
curl -s https://api.conifer.build/v1/models \
-H "Authorization: Bearer $CONIFER_API_KEY" \
| jq '.data[] | {id, caps, context_window, pricing}'Routes
| Route | Wire | Notes |
|---|---|---|
POST /v1/chat/completions | OpenAI chat | Every model in the catalog. |
POST /v1/messages | Anthropic Messages | Anthropic model ids. Prompt caching and live streaming on this route. |
POST /v1/responses | OpenAI Responses | The stateless tool-loop wire Codex uses. Same auth, receipts and errors. |
POST /v1/completions | OpenAI | The legacy text-completions wire. |
POST /v1/embeddings | OpenAI | Models whose caps include embeddings. Same key, same receipt, same ceiling. |
GET /v1/catalog | Conifer | The public catalog with ids, caps, context windows and prices. No key needed. |
GET /v1/models | OpenAI | The same list, scoped to your key. |
GET /v1/balance | Conifer | Your balance, as integers. |
Parameters
| Field | Behavior |
|---|---|
model | An exact id from GET /v1/models. An unknown id is a 404. Conifer never swaps in another model. |
max_tokens | Optional on the chat wire, where 4096 applies when it is absent. Required on /v1/messages. |
stream | Accepted on every completion route. See Streaming below. |
tools | Passed to the model. Check caps on the catalog first. A model without tools answers 400 naming the missing capability, not 402 and not 429. |
Streaming
stream: true returns SSE on every completion route. Tokens arrive as the model produces them. A few providers hand back the whole answer at the end, in the same chunk shape. The cost header is absent on a stream because the response starts before the cost is known. On /v1/messages the settled cost arrives as a final conifer_receipt event after message_stop. On the chat and Responses wires, send stream_options.include_usage and price the final chunk’s usage from the catalog. An in-band stream error has the same shape as the buffered envelope.
Time bounds
A buffered call has a fixed time limit of a few minutes. A stream is bounded by the idle time between frames instead. For very long outputs, stream or split the request.
Receipt headers
Every response carries a receipt in its headers. On a stream the identity headers are present and the cost header is absent, because the head is sent before the cost settles.
| Header | Meaning |
|---|---|
x-conifer-requested-model | The id you asked for. |
x-conifer-effective-model | The model that answered and was billed. The same as the requested id unless you let Conifer choose with auto or default, a fallback you named served, or you sent a namespaced spelling. |
x-conifer-cost-nanousd | The settled cost of this request, in nanodollars (1e-9 USD). |
request-id | Same value as x-conifer-request-id. |
x-request-id | Same value as x-conifer-request-id. |
x-conifer-request-id | The request id. Quote it in support requests. |
One header goes the other way. x-conifer-max-cost-nanousd sets a hard ceiling for the request. If it could cost more, Conifer refuses with a 402 before the model runs and nothing is charged.
Errors
| Status | Meaning |
|---|---|
| 400 | type invalid_request_error, code context_length_exceeded when the prompt is over the model’s window. Some providers report this themselves, which relays as a 422 upstream_error naming the token counts. Other 400s name the problem in the body, such as tools on a model whose caps omit them or a non-Anthropic id on /v1/messages. Capability refusals stay 400. |
| 401 | type invalid_request_error, code invalid_api_key. Every auth failure returns this one 401. The header is WWW-Authenticate: Bearer. |
| 402 | type insufficient_allowance, code insufficient_quota when the balance cannot cover the request. A ceiling refusal is type cost_ceiling_exceeded, and a key at its own cap is key_spend_cap_exceeded. Nothing is charged for any of the three, and none is remapped to 429. See Billing & caching. |
| 404 | code model_not_found, param model. The id is not in your catalog. |
| 429 | type rate_limit_error, code rate_limit_exceeded. The header is Retry-After: 1. Conifer does not send invented x-ratelimit-* figures. |
Reasoning traces
On POST /v1/chat/completions, the trace is at choices[0].message.reasoning, the OpenRouter and OpenAI-compat name. Some models also set choices[0].message.reasoning_content, DeepSeek’s name. Both fields stream on choices[0].delta. Not every model emits either field. A missing field means there was no trace.
Two optional request fields control reasoning. Send reasoning as { effort | max_tokens }, or send reasoning_effort as one of none | minimal | low | medium | high | xhigh | max. One vocabulary covers every provider. Where a model offers fewer levels, Conifer maps yours to the nearest one it has. On Anthropic models the effort becomes a thinking budget, so set max_tokens high enough to leave room for the answer after the thinking.
curl https://api.conifer.build/v1/chat/completions \
-H "Authorization: Bearer $CONIFER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-haiku-4-5",
"max_tokens": 8192,
"reasoning": {"effort": "medium"},
"messages": [{"role": "user", "content": "three names for a build cache"}]
}'On POST /v1/messages, a thinking block is a content item with type: "thinking".
{
"type": "thinking",
"thinking": "the model scratch work",
"signature": "…"
}Prompt caching
On the chat wire, cache counts appear under usage.prompt_tokens_details as cached_tokens and cache_write_tokens. Both are 0 when nothing was cached, and the details object itself may be absent. Usage reports only the fields that exist.
{
"prompt_tokens_details": {
"cached_tokens": 80,
"cache_write_tokens": 0
}
}On /v1/messages, send cache_control breakpoints. Usage reports cache_read_input_tokens and cache_creation_input_tokens. Cached input is priced at the model’s cache-read rate, and the receipt reflects it.
curl https://api.conifer.build/v1/messages \
-H "x-api-key: $CONIFER_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-haiku-4-5",
"max_tokens": 1024,
"system": [{
"type": "text",
"text": "You are a build-cache namer. Keep answers to three words.",
"cache_control": {"type": "ephemeral"}
}],
"messages": [{"role": "user", "content": "three names for a build cache"}]
}'{
"input_tokens": 120,
"cache_creation_input_tokens": 80,
"cache_read_input_tokens": 0,
"output_tokens": 24
}