# `Nous.Usage.Pricing`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/usage/pricing.ex#L1)

Per-model token prices for turning a `%Nous.Usage{}` into a dollar figure.

Prices are quoted **per 1M tokens in USD**, the way every vendor publishes
them. Each entry carries an `:input` and `:output` rate plus optional
`:cache_read` (a cache hit) and `:cache_write` (creating a cache entry)
rates, because cached input is billed at a different rate than fresh input —
which is the whole reason `%Nous.Usage{}` tracks `cache_read_input_tokens`
and `cache_creation_input_tokens` separately.

## Lookup

`lookup/2` is keyed by `{provider, model}` where `provider` is one of
`t:Nous.Model.provider/0` and `model` is the API model id string.

Resolution order:

  1. **Exact match** on `{provider, model}`.
  2. **Longest-prefix match** — model ids carry dated or variant suffixes
     (`"gpt-4o-2026-05-13"`, `"claude-opus-4-5-20251101"`), so the entry
     whose model string is the longest prefix of the requested id wins. The
     prefix must end on a `-` boundary. Longest wins is what keeps
     `"gemini-2.5-flash-lite-preview-09-2025"` on the Flash-Lite price
     instead of falling into the pricier `"gemini-2.5-flash"` entry.

     The trade-off: an unlisted *sibling* variant inherits its family's
     price rather than reporting `:unknown` (ask for `"o1-mini"` and you get
     the `"o1"` rate). Add the sibling through the override config below
     when that matters.
  3. **Local providers** (`:ollama`, `:lmstudio`, `:llamacpp`, `:vllm`,
     `:sglang`) resolve to all-zero rates: you run the weights on your own
     hardware, so there is no per-token invoice. Zero is the honest answer
     here, not `:unknown`.
  4. Otherwise `:unknown`. A wrong price is worse than no price, so an
     unrecognised model never gets a guessed rate and never raises.

## Operator overrides

Fine-tuned, self-hosted, brokered and enterprise-contract models cannot be
priced from a public table. Supply your own rates, which are merged over the
built-in table (so they also win on exact keys):

    config :nous, :model_prices, %{
      {:openai, "ft:gpt-4.1-2026-01-01:acme:support:abc123"} =>
        %{input: 3.0, output: 12.0, cache_read: 0.75},
      {:groq, "llama-3.3-70b-versatile"} => %{input: 0.59, output: 0.79}
    }

Omitted optional keys default conservatively: a missing `:cache_read` bills
cache hits at the full `:input` rate (no published discount to apply), and a
missing `:cache_write` bills cache creation at zero (no published surcharge,
which is how OpenAI prompt caching works).

## Price snapshot

**Prices below were recorded on 2026-08-14** from the vendors' official
pricing pages:

  * <https://developers.openai.com/api/docs/pricing>
  * <https://platform.claude.com/docs/en/about-claude/pricing>
  * <https://ai.google.dev/gemini-api/docs/pricing>

This is a snapshot and it **will go stale** — vendors reprice, and this
table is only refreshed when someone remembers to. Treat the numbers as an
estimate, and when they are wrong for you, correct them with
`config :nous, :model_prices` (above) rather than waiting for a release.

Only models whose published rates were confirmed at that date are listed.
Deliberate omissions:

  * **Audio, image, realtime and embedding models** — those are billed per
    modality (and sometimes per character or per second), which
    `%Nous.Usage{}` cannot distinguish from text tokens.
  * **`:vertex_ai`** — Gemini on Vertex is invoiced by Google Cloud on its
    own rate card, which is not the Developer API rate card.
  * **`:groq`, `:openrouter`, `:together`, `:mistral`, `:custom`** — broker
    and per-account pricing that varies by model and contract.

Anything not listed resolves to `:unknown`, which is the honest answer.

# `entry`

```elixir
@type entry() :: %{
  :input =&gt; number(),
  :output =&gt; number(),
  optional(:cache_read) =&gt; number(),
  optional(:cache_write) =&gt; number()
}
```

A price table entry as written in the table or in operator config.

# `price`

```elixir
@type price() :: %{
  input: float(),
  output: float(),
  cache_read: float(),
  cache_write: float()
}
```

A resolved price, per 1M tokens in USD. All four rates are always present.

# `lookup`

```elixir
@spec lookup(Nous.Model.provider(), String.t()) :: price() | :unknown
```

Look up the price for `provider` and `model`.

Returns a `t:price/0` with all four rates filled in, or `:unknown` when no
entry matches. See the moduledoc for the exact resolution order (exact
match, then longest prefix, then zero for local providers).

## Examples

    iex> price = Pricing.lookup(:ollama, "llama3.3:70b")
    iex> {price.input, price.output}
    {0.0, 0.0}

    iex> Pricing.lookup(:openai, "no-such-model")
    :unknown

# `table`

```elixir
@spec table() :: %{required({Nous.Model.provider(), String.t()}) =&gt; entry()}
```

The effective price table: the built-in snapshot with
`config :nous, :model_prices` merged over it.

Entries are returned as written (optional cache rates may be absent);
`lookup/2` is what fills in the defaults.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
