Custom Model Costs for Self-Hosted and Fine-Tuned Models
Laminar prices every LLM span from a built-in table covering the major providers. Custom model costs let you define your own per-project pricing for models that table doesn’t know about: fine-tuned deployments, self-hosted open-weight models, private inference endpoints, or a negotiated rate that differs from list price. Set a price once in project settings and every matching span, past and future, is costed with it.
Project settings: two custom prices, one scoped to a provider and one matching spans that carry no provider
Custom prices are per project and take priority over the built-in table. They apply at ingestion, so a price change affects spans ingested from that point on, not spans already stored.
Go to Settings > Model costs in the project sidebar, under Project settings.
2
Create the entry
Click Model cost and fill in the form.
The add dialog: prices are entered in dollars per million tokens
Field
Notes
Provider
Optional. Must match gen_ai.system on your spans. Leave blank to match spans that carry no provider.
Model
Required. Must match the model name on your spans exactly.
Input
Price for input (prompt) tokens.
Cache read
Price for tokens read from a prompt cache. Usually a fraction of the input price.
Output
Price for output (completion) tokens.
Enter every price in dollars per million tokens, matching how providers publish their rates. Laminar converts to a per-token rate internally. At least one of Input or Output is required; leave a field blank and that token class is not charged.
3
Confirm on a span
Send a request through the model, open the trace, and check the cost on the LLM span. If it reads $0, the provider or model string doesn’t match: see Why isn’t my custom price applying? below.
Edit and delete are the pencil and trash icons on each row. Both take effect on the next ingested span.
This is the one thing that causes a custom price to silently not apply. The model and provider you enter are matched exactly against the values on your spans, with no normalization beyond case-insensitivity:
Model is matched against gen_ai.response.model, falling back to gen_ai.request.model when there is no response model. A date or version suffix is part of the name: an entry for acme-llm-v1 does not match a span reporting acme-llm-v1-2026-01-01. Add a separate entry per deployed variant.
Provider is matched against gen_ai.system, after stripping anything past the first dot (a span reporting anthropic.messages matches an entry of anthropic). Enter anthropic, not anthropic.messages.
A blank Provider means blank, not “any”. An entry with no provider matches only spans that carry no gen_ai.system at all. If your spans set a provider, the entry must set the same one.
Case doesn’t matter: both sides are lowercased before matching. To read the exact strings your spans carry, query them:
Explicit cost attributes on the span. If a span sets gen_ai.usage.input_cost, output_cost, or cost, Laminar uses those numbers and consults no price table. This is the escape hatch when you compute cost yourself: see LLM cost tracking.
Your project’s custom prices, matched exactly as described above.
The built-in price table, which does normalize: it strips date snapshots and tries provider-prefixed variants, so gpt-5-mini-2026-04-01 still resolves to gpt-5-mini pricing.
Once a project has at least one custom price, a Copy to project button appears. Pick a target project in the same workspace and Laminar copies every entry across. This is the fast path when several projects share the same self-hosted models.
Copying replaces all custom model costs in the target project. Any entries that exist there are deleted first, not merged.
The span costs $0. The provider or model doesn’t match. Run the SQL query above and compare character for character, watching for date suffixes and a provider you didn’t expect.
The span has a cost you didn’t configure. Either your instrumentation sets explicit cost attributes (which win outright) or the model resolved against the built-in table. Check whether your code sets gen_ai.usage.*_cost.
Only older spans are wrong. Prices apply at ingestion. Spans stored before you added the price keep their original cost.
Token counts are missing. Any price needs token counts to multiply. If input_tokens and output_tokens are 0, fix the instrumentation first: LLM cost tracking covers the attributes to set.