Models Sign in

Cost tools

On this page
  1. Analyze a prompt
    1. Request body
    2. Example
    3. Response
  2. Estimate a monthly bill
    1. Request body
    2. Example
    3. Response
  3. Errors

Two public endpoints answer the question prefix caching raises: what is your prompt costing you that it doesn't need to? The analyser shows where a prompt stops a prefix cache from reusing it; the estimator projects a monthly bill at a given cache hit rate. Neither needs a key, and neither stores or logs what you send.

Note

Figures are at sample pricing unless you supply your own rates: Mortar's rates are derived from on-demand GPU list prices and measured throughput at 65% utilization, not quoted. Every response says so in disclaimer.

Analyze a prompt

POST /v1/cost/analyze

Finds the values in a prompt that change from request to request and sit before content that doesn't, so a prefix cache recomputes that content every time. Each finding says how many tokens it wastes per request, what that costs and what to change. A well-ordered prompt returns no findings.

A wasted token costs what fixing it takes off the bill. On every request it's prefilled and written to the cache at the cache-write price, when it could have been read back at the cache-read price. At Mortar's sample prices that difference is 1.25 − 0.1 = 1.15 times the input price; send pricing to use your provider's rates instead.

The analyser has its own rate limit: 30 requests per minute per key or, without a key, per client IP address.

Request body

Field Type Required Description
prompt string One of three The text sent at the start of every request: system prompt, tool definitions, instructions.
messages array One of three A conversation. Every turn but the last is treated as the reusable prefix.
messages[].role string Yes Who wrote the turn.
messages[].content string Yes The turn's text.
variants array One of three Two to 16 prompts rendered from real requests. With these the analyser measures where requests actually differ instead of recognising values that look volatile.
model string No The model whose sample price to use. Defaults to Qwen/Qwen2.5-7B-Instruct.
requestsPerMonth number No Adds monthly totals. Leave it out and no traffic is assumed.
pricing.inputPerMillion number No Your own input price per million tokens, in US dollars, used instead of the sample price.
pricing.outputPerMillion number No Your own output price per million tokens.
pricing.cacheReadPerMillion number No What your provider charges for an input token read from its prompt cache, per million. Leave it out and a cached token counts as free, which overstates the waste if your provider charges for one.
pricing.cacheWritePerMillion number No What it charges to prefill a token and write it to the cache, per million. Leave it out and that's your input price.

Send prompt, messages or at least two variants. All the text in one request, across all three, is limited to 64 KiB, about 16,000 tokens; more is refused with 413 and the code input_too_large.

Example

This system prompt opens with a request ID and a timestamp, so nothing after them can be reused:

Requestbash
curl https://api.mortar.one/v1/cost/analyze \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Request 3f1c9a2e-7b4d-4c1e-9a6f-0d2b8e5c7a19 at 2026-10-29T09:30:02Z. You are the support assistant for Acme Cloud. Answer questions about billing, API keys and deployments. Be concise, cite the documentation section you used, and never reveal internal ticket numbers or customer data.",
    "requestsPerMonth": 1000000
  }'
Responsejson
{
  "object": "cost.analysis",
  "model": "Qwen/Qwen2.5-7B-Instruct",
  "basis": {
    "samples": 1,
    "method": "heuristic",
    "note": "Inferred from one prompt by recognising values that change per request. Send two or more real requests as variants for a measured result."
  },
  "prompt": {
    "totalTokens": 113,
    "cachedTokens": 2,
    "potentialCachedTokens": 65,
    "recoverableTokens": 63,
    "currentReuseRatio": 0.0177,
    "potentialReuseRatio": 0.5752
  },
  "findings": [
    {
      "id": "cache_hostile_ordering",
      "title": "A per-request value comes before stable content",
      "severity": "critical",
      "detail": "A prefix cache matches from the first token, so the first value that changes between requests invalidates everything after it. Here that is 63 tokens of otherwise identical prompt, prefilled again on every request.",
      "confidence": "heuristic",
      "evidence": [
        {
          "kind": "uuid",
          "label": "A UUID",
          "text": "3f1c9a2e-7b4d-4c1e-9a6f-0d2b8e5c7a19",
          "start": 8,
          "end": 44
        }
      ],
      "wastedTokensPerRequest": 63,
      "wastedUsdPer1kRequests": 0.0023,
      "wastedUsdPerMonth": 2.27,
      "fix": {
        "summary": "Move every per-request value after the text that never changes, for example into a final \"Request context\" section or the user message. The stable part then matches byte for byte on every request.",
        "before": "Request 3f1c9a2e-7b4d-4c1e-9a6f-0d2b8e5c7a19 at 2026-10-29T09:30:02Z. You are the support assistant for Acme Cloud. …",
        "after": "Request  at . You are the support assistant for Acme Cloud. …\n\nRequest context: 3f1c9a2e-7b4d-4c1e-9a6f-0d2b8e5c7a19; 2026-10-29T09:30:02Z"
      }
    }
  ],
  "summary": {
    "wastedTokensPerRequest": 63,
    "wastedUsdPer1kRequests": 0.0023,
    "projectedMonthlyWasteUsd": 2.27,
    "headline": "Fixing this makes 63 more tokens reusable per request: $0.0023 per 1,000 requests at $0.035995 per million, the difference between prefilling a token and reading it from the cache."
  },
  "pricing": {
    "inputPerMillion": 0.0313,
    "outputPerMillion": 0.2373,
    "cacheReadPerMillion": 0.00313,
    "cacheWritePerMillion": 0.039125
  },
  "notes": [
    "Even fixed, the reusable prefix is 65 tokens. Hosted APIs cache only a prefix above a minimum, and this one can fall below it on OpenAI and Azure OpenAI (1,024), Anthropic (512–4,096 by model), Google Gemini (2,048–4,096 by model). An engine that caches in 16-token blocks, as vLLM does, still reuses it."
  ],
  "disclaimer": "Sample pricing: Mortar's rates are derived from on-demand GPU-hour list prices and measured throughput at 65% utilisation, not quoted. See costPerMillionInput, costPerMillionOutput, costPerMillionCacheRead and costPerMillionCacheWrite on /v1/platform."
}

Response

Field Type Description
object string Always cost.analysis.
model string The model whose price was used.
basis.samples integer How many prompts were analysed.
basis.method string measured when two or more variants showed where requests differ, heuristic when values were recognised in a single prompt.
basis.note string What the method means for how far to trust the findings.
prompt.totalTokens integer Tokens in the prompt.
prompt.cachedTokens integer The prefix a cache can reuse as the prompt stands.
prompt.potentialCachedTokens integer The prefix it could reuse with every finding fixed.
prompt.recoverableTokens integer The difference: what fixing the findings recovers on every request.
prompt.currentReuseRatio number cachedTokens as a share of the prompt, from 0 to 1.
prompt.potentialReuseRatio number potentialCachedTokens as a share of the prompt.
findings array What wastes prefill, in the order to fix it. Empty when nothing does.
findings[].id string cache_hostile_ordering: a per-request value comes before stable content. nondeterministic_ordering: the same JSON arrives in a different key order.
findings[].title string A one-line summary.
findings[].severity string critical, high, medium or low, by how many tokens it wastes and what share of the prompt that is.
findings[].detail string What the finding means for this prompt.
findings[].confidence string measured or heuristic, as in basis.method.
findings[].evidence array Up to 8 of the values responsible, first in the prompt first.
findings[].evidence[].kind string What kind of value it is, such as uuid or timestamp.
findings[].evidence[].label string The kind in words.
findings[].evidence[].text string The value as it appears in your prompt.
findings[].evidence[].start integer Where it starts, as a byte offset into the analysed text.
findings[].evidence[].end integer Where it ends.
findings[].wastedTokensPerRequest integer Prompt tokens this finding stops the cache from reusing.
findings[].wastedUsdPer1kRequests number What those tokens cost per 1,000 requests, in US dollars: each prefilled and written to the cache instead of read from it.
findings[].wastedUsdPerMonth number The monthly cost. Present only when you sent requestsPerMonth.
findings[].fix.summary string The change to make.
findings[].fix.before string An excerpt of your prompt around the first value.
findings[].fix.after string The same excerpt with the values moved to the end.
summary.wastedTokensPerRequest integer All findings together. Findings are measured in order, so theirs add up to this.
summary.wastedUsdPer1kRequests number The total cost per 1,000 requests.
summary.projectedMonthlyWasteUsd number The monthly total. Present only when you sent requestsPerMonth.
summary.headline string The result in one sentence.
pricing.inputPerMillion number The input price.
pricing.outputPerMillion number The output price.
pricing.cacheReadPerMillion number What a token read from the cache costs, per million. Absent when your own pricing left it out: a cached token then counts as free.
pricing.cacheWritePerMillion number What a token prefilled and written to the cache costs, per million. Absent when your own pricing left it out: that's then the input price.
notes array Caveats, such as a prefix too short for hosted APIs to cache.
disclaimer string Whose prices the figures use.

Estimate a monthly bill

POST /v1/cost/estimate

Projects what a workload costs a month on Mortar at the cache hit rate you give, and compares it with what you pay today. Every input is echoed back and every step of the arithmetic is written out in math, so you can check the result. When the inputs say Mortar costs more, the response says so.

Request body

Field Type Required Description
requestsPerMonth number One of two Requests a month.
requestsPerSecond number One of two Requests a second, converted at 730 hours a month. Send this or requestsPerMonth, not both.
inputTokensPerRequest integer Yes Prompt tokens in a typical request.
outputTokensPerRequest integer Yes Completion tokens in a typical request.
cacheHitRate number Yes The share of input tokens Mortar serves from the prefix cache, from 0 to 1. There's no default, because it decides the answer.
model string No The model to price. Defaults to Qwen/Qwen2.5-7B-Instruct.
baseline.label string No A name for what you pay today.
baseline.inputPerMillion number No Today's input price per million tokens, in US dollars.
baseline.outputPerMillion number No Today's output price per million tokens.
baseline.cacheHitRate number No The share of input tokens today's provider already serves from its own prompt cache, from 0 to 1.
baseline.cacheReadMultiplier number With baseline.cacheHitRate What a cached input token costs there, as a fraction of the input price: 0.5 is half price.

Leave out baseline and the comparison is the same model with no prefix cache, which isolates what caching is worth.

Mortar's cache isn't free, and the estimate prices it as per-request telemetry does. An input token read from the cache costs a tenth of the input price. Any other input token is prefilled and written to the cache, at 1.25 times the input price. Each model's rates are costPerMillionCacheRead and costPerMillionCacheWrite on GET /v1/platform. At a 0% cache hit rate every token pays to be written and none is read back, so Mortar costs more than the same model with no prefix cache. Against that baseline the output cancels out, and the break-even is the write premium over the read discount: (1.25 − 1) / (1.25 − 0.1), about 22%.

Example

Requestbash
curl https://api.mortar.one/v1/cost/estimate \
  -H "Content-Type: application/json" \
  -d '{
    "requestsPerMonth": 1000000,
    "inputTokensPerRequest": 2000,
    "outputTokensPerRequest": 300,
    "cacheHitRate": 0.6,
    "baseline": {
      "label": "Current provider",
      "inputPerMillion": 2.5,
      "outputPerMillion": 10,
      "cacheHitRate": 0.3,
      "cacheReadMultiplier": 0.5
    }
  }'
Response, abbreviatedjson
{
  "object": "cost.estimate",
  "model": "Qwen/Qwen2.5-7B-Instruct",
  "assumptions": {
    "requestsPerMonth": 1000000,
    "requestsPerSecond": 0.3805,
    "inputTokensPerRequest": 2000,
    "outputTokensPerRequest": 300,
    "cacheHitRate": 0.6,
    "hoursPerMonth": 730,
    "inputTokensPerMonth": 2000000000,
    "outputTokensPerMonth": 300000000
  },
  "mortar": {
    "label": "Mortar",
    "pricing": {
      "inputPerMillion": 0.0313,
      "outputPerMillion": 0.2373,
      "cacheReadPerMillion": 0.00313,
      "cacheWritePerMillion": 0.039125
    },
    "cacheHitRate": 0.6,
    "cacheReadMultiplier": 0.1,
    "cacheWriteMultiplier": 1.25,
    "breakdown": {
      "uncachedInputTokens": 800000000,
      "cachedInputTokens": 1200000000,
      "outputTokens": 300000000,
      "uncachedInputUsd": 31.3,
      "cachedInputUsd": 3.76,
      "outputUsd": 71.19,
      "monthlyUsd": 106.25,
      "perRequestUsd": 0.000106,
      "blendedPerMillionTokensUsd": 0.0462
    }
  },
  "savings": {
    "monthlyUsd": 7143.75,
    "annualUsd": 85725.05,
    "percent": 98.53,
    "cheaper": true,
    "verdict": "Mortar is $7,143.75 per month cheaper (98.5%) at these inputs."
  },
  "breakEven": {
    "cacheHitRate": 0,
    "reachable": true,
    "verdict": "Mortar costs no more than the baseline even with no cache hits."
  },
  "math": [
    "requests/month = 1,000,000 (given)",
    "Mortar uncached input = 2,000,000,000 × (1 − 0.6) = 800,000,000 × $0.0313/M × 1.25 = $31.30, prefilled and written to the cache",
    "Mortar cached input = 2,000,000,000 × 0.6 = 1,200,000,000 × $0.0313/M × 0.1 = $3.76"
  ],
  "notes": [
    "Mortar bills an input token read from the prefix cache at 0.1× the input price and one prefilled and written to it at 1.25×. Output tokens are always billed: decoding cannot be cached."
  ],
  "disclaimer": "Sample pricing: Mortar's rates are derived from on-demand GPU-hour list prices and measured throughput at 65% utilisation, not quoted. See costPerMillionInput, costPerMillionOutput, costPerMillionCacheRead and costPerMillionCacheWrite on /v1/platform."
}

Response

Field Type Description
object string Always cost.estimate.
model string The model priced.
assumptions object Every input the arithmetic used, including the derived requestsPerSecond, hoursPerMonth, inputTokensPerMonth and outputTokensPerMonth.
baseline object What you pay today, itemised the same way as mortar.
mortar object Mortar at your cache hit rate.
mortar.label string The side's name.
mortar.pricing.inputPerMillion number The input price used.
mortar.pricing.outputPerMillion number The output price used.
mortar.pricing.cacheReadPerMillion number What an input token read from the prefix cache costs, per million. Mortar only: the model's costPerMillionCacheRead.
mortar.pricing.cacheWritePerMillion number What an input token prefilled and written to the cache costs, per million. Mortar only: the model's costPerMillionCacheWrite.
mortar.cacheHitRate number The cache hit rate applied.
mortar.cacheReadMultiplier number What a cached input token costs, as a multiple of the input price: 0.1 for Mortar. On baseline it's the baseline.cacheReadMultiplier you sent.
mortar.cacheWriteMultiplier number What an uncached input token costs, as a multiple of the input price: 1.25 for Mortar, which writes every token it prefills to the cache, and 1 for baseline, which is charged no write premium.
mortar.breakdown.uncachedInputTokens number Input tokens prefilled each month. On Mortar each is also written to the cache.
mortar.breakdown.cachedInputTokens number Input tokens served from the cache each month.
mortar.breakdown.outputTokens number Output tokens each month.
mortar.breakdown.uncachedInputUsd number What the uncached input costs: at the cache-write price on Mortar.
mortar.breakdown.cachedInputUsd number What the cached input costs, at the cache-read price.
mortar.breakdown.outputUsd number What the output costs.
mortar.breakdown.monthlyUsd number The monthly total.
mortar.breakdown.perRequestUsd number The cost of one request.
mortar.breakdown.blendedPerMillionTokensUsd number Total spend over every token handled, cached or not: the figure to compare across vendors.
savings.monthlyUsd number Baseline minus Mortar, per month. Negative when Mortar costs more.
savings.annualUsd number The same, per year.
savings.percent number The saving as a share of the baseline's monthly cost.
savings.cheaper boolean Whether Mortar costs less at these inputs.
savings.verdict string The comparison in one sentence.
breakEven.cacheHitRate number or null The Mortar cache hit rate at which both cost the same, or null when caching can't close the gap.
breakEven.reachable boolean Whether a hit rate between 0 and 1 reaches it.
breakEven.verdict string The break-even point in one sentence.
math array The calculation, one line per step.
notes array The assumptions behind the figures.
disclaimer string Whose prices the figures use.

Errors

Status Code When
400 A required field is missing or out of range, such as cacheHitRate outside 0 to 1, or both requestsPerMonth and requestsPerSecond. The message names the field.
404 model_not_found model isn't in the catalog.
413 input_too_large The analyser was sent more than 64 KiB of text.
429 More than 30 analyses in a minute.
An estimate without a cache hit ratebash
curl https://api.mortar.one/v1/cost/estimate \
  -H "Content-Type: application/json" \
  -d '{"requestsPerMonth": 1000000, "inputTokensPerRequest": 2000, "outputTokensPerRequest": 300}'
Responsejson
{
  "error": {
    "message": "`cacheHitRate` is required: the share of input tokens served from the prefix cache, between 0 and 1.",
    "type": "invalid_request_error"
  }
}