skip to content
The Conifer SDK

SDK

The Conifer SDK

The open-source client: exact per-turn cost receipts, a hard spend ceiling, and errors you can branch on.


The Conifer SDK is developed in the open at ConiferKit/use-conifer. It is a thin client over the same public wire you could call with curl, so nothing it does is hidden from you — and if an integration is wrong, you can read exactly why and send a fix.

When to use it, and when not to

The official OpenAI and Anthropic packages work against the gateway and remain the right choice for a plain drop-in, or for an existing codebase you do not want to touch. Reach for this SDK when you want one of four things the OpenAI client structurally cannot give you:

What you getWhy the OpenAI client cannot
The receiptEvery response carries the exact integer nanodollar cost of that call, itemized across fresh input, cache write, cache read, and output. No second stats request, no estimating from token counts.
A spend ceilingmaxCostNanoUsd is enforced by the gateway before any upstream call. It refuses rather than serves.
Named refusalsOut of credit and your-ceiling-refused are both HTTP 402 but have opposite remedies, so they are distinct error types.
Honest migrationThe shims refuse a field Conifer cannot honor and name the replacement, instead of dropping it silently.

Install

One call

Set CONIFER_API_KEY and construct the client with no arguments. Both languages take the same shape:

chat.ts
import { Conifer, textOf } from "@conifer/sdk";

const conifer = new Conifer();          // reads CONIFER_API_KEY

const answer = await conifer.chat({
  model: "claude-haiku-4-5",
  messages: [{ role: "user", content: "three names for a build cache" }],
  maxTokens: 200,
  maxCostNanoUsd: 5_000_000,            // refuse this turn if it could cost over $0.005
});

console.log(textOf(answer));
console.log(answer.receipt.costUsd);    // "0.001250000" — this exact call
chat.py
from conifer_sdk import Conifer, ChatRequest

conifer = Conifer()                      # reads CONIFER_API_KEY

answer = conifer.chat(ChatRequest(
    model="claude-haiku-4-5",
    messages=[{"role": "user", "content": "three names for a build cache"}],
    max_tokens=200,
    max_cost_nano_usd=5_000_000,
))

print(answer.text, answer.receipt.cost_usd)

Streaming

On a streamed turn the cost headers are absent, in both languages. That is the wire being honest rather than a gap: the response head is sent before the first token, and the money settles after the last. Reconcile a stream from its terminal usage chunk, which the SDK always requests.

Errors you can branch on

ceiling.ts
import { Conifer, ConiferCostCeilingError, ConiferPaymentError } from "@conifer/sdk";

try {
  await conifer.chat({ model: "claude-opus-5", messages, maxCostNanoUsd: 1_000 });
} catch (err) {
  if (err instanceof ConiferCostCeilingError) {
    // your own ceiling refused the turn — raise it, or pick a cheaper model
  } else if (err instanceof ConiferPaymentError) {
    // the account is out of credit — top up
  }
}

The wire-level names behind these types — insufficient_quota, invalid_api_key and the rest — are listed on Errors.

Fallbacks are client-side, and opt-in

Conifer admits exactly the model you name; there is no server-side fallback list. A fallback chain is therefore a series of separate billed requests, and the SDK makes you say so with allowClientFallback. Only a retryable failure advances the chain: a 402 or a bad request would answer the same way on every member.

Contribute

Integration bugs are the ones we most want reported: they are where a gateway quietly diverges from the SDKs people actually use. See Contributing.