# Usage-Based Pricing

> Store apps can charge per run using CEL expressions evaluated against task data and output metadata.

**URL:** https://inference.sh/docs/extend/pricing
**Last updated:** 2026-10-15

---

Store apps can charge per run using CEL expressions evaluated against task data and [output metadata](/docs/extend/output-meta).

---

## Overview

| Piece | Role |
|-------|------|
| **`output_meta`** | Per-task usage your app reports (tokens, pixels, seconds, and so on) |
| **`AppPricing`** | Store listing config: price variables and CEL formulas |
| **Task cost** | What the user was charged — see [Get task cost](/docs/api/rest/tasks#get-task-cost) |

All monetary values in pricing config are in **microcents** (1 microcent = $0.00000001; $1 = 100,000,000 microcents). Use `to_dollars(microcents)` in descriptions for human-readable strings.

---

## AppPricing fields

| Field | Description |
|-------|-------------|
| `prices` | Named price variables (microcents), referenced as `prices.name` in expressions |
| `upstream_pricing` | Optional note about upstream app pricing (display only) |
| `resource_expression` | GPU/CPU time cost (default: `sum(resources.map(r, r.price_per_second)) * elapsed_ms / 1000`) |
| `inference_expression` | Platform inference fee |
| `royalty_expression` | Royalty to upstream app authors |
| `partner_expression` | Partner revenue share |
| `total_expression` | Final charge (defaults to sum of fee components when empty) |
| `description` | Human-readable pricing blurb (see below) |
| `description_rendered` | Evaluated description returned by the API after save |
| `estimate` | Optional CEL expression for pre-execution cost estimation when fee formulas depend on post-execution data. Returns an int (exact microcents) or `{"min": int, "max": int}`. Only has access to `task_inputs`, `prices`, `fees`, and `task_function`. Not needed when all fee expressions are input-based (`estimable: true`). |
| `estimable` | Computed at save time — `true` when all fee expressions can be evaluated from pre-execution data alone |

Empty fee expressions use platform defaults. Patch updates merge `prices` keys; setting a price to `0` removes that key.

---

## Description field

The `description` is evaluated as CEL against the static `prices` map only (no per-task variables). Helpers like `to_dollars(prices.base)` format amounts for display.

**Plain text is supported.** If you send a normal sentence (not starting with `"`), the API wraps it as a CEL string literal on save — you do not need to quote descriptions yourself. Use explicit CEL when you want dynamic text from `prices`:

```text
"$" + to_dollars(prices.per_image) + " per image"
```

---

## Fee expressions and output_meta

Inference and total formulas can read **`output_meta`** from completed tasks — the same structure you set in app code (`inputs` / `outputs` with `type`, `tokens`, dimensions, `seconds`, `extra`, and so on).

Common CEL helpers for usage-based pricing include:

| Helper | Use |
|--------|-----|
| `text_tokens(output_meta.inputs)` | Sum prompt tokens |
| `text_tokens(output_meta.outputs)` | Sum completion tokens |
| `cached_tokens(output_meta.inputs)` | Sum prompt tokens read from the provider's cache (part of `text_tokens`) |
| `cache_write_tokens(output_meta.inputs)` | Sum prompt tokens written to the provider's cache (part of `text_tokens`) |
| `image_count(output_meta.outputs)` | Count generated images |
| `video_seconds(output_meta.outputs)` | Sum video duration |
| `resolution(width, height)` | Tier string (`720p`, `1080p`, …) for lookup in `prices` |
| `get(task_inputs, "field", default)` | Read task input fields |
| `get_extra(item, "key", default)` | Read `extra` on meta items |

An LLM with prompt caching bills the three kinds of input at their own rates:

```
(text_tokens(output_meta.inputs) - cached_tokens(output_meta.inputs) - cache_write_tokens(output_meta.inputs)) * prices.per_million_input / 1000000
  + cached_tokens(output_meta.inputs) * prices.per_million_cached / 1000000
  + cache_write_tokens(output_meta.inputs) * prices.per_million_cache_write / 1000000
  + text_tokens(output_meta.outputs) * prices.per_million_output / 1000000
```

Expressions also receive `resources`, `elapsed_ms` (task duration in milliseconds), `elapsed_seconds` (the same in whole seconds), and intermediate fee variables (`resource_fee`, `inference_fee`, and so on) where applicable.

### Run time for resource fees

GPU/CPU resource pricing uses wall time from the task's last **Dispatched** status to its terminal status (**Completed**, **Failed**, or **Cancelled**). A task dispatched more than once is billed from its final dispatch.

| Variable | Meaning |
|----------|---------|
| `elapsed_ms` | Billed duration in **milliseconds** (the value the default resource expression uses) |
| `elapsed_seconds` | `elapsed_ms / 1000` in whole seconds; the sub-second remainder is dropped |

Resource rates in `resources[].price_per_second` stay **per second**. The default multiplies by `elapsed_ms / 1000` so short runs are not rounded to whole seconds. A custom `resource_expression` that uses `elapsed_seconds` truncates sub-second time; use `elapsed_ms / 1000` for millisecond accuracy.

Populate accurate **`output_meta`** on every priced run so formulas have data to evaluate. Public task APIs do not return `output_meta` in `output` — billing reads the stored metadata internally.

---

## Cost estimation

Apps can provide pre-execution cost estimates via the `estimate` field and the public estimate endpoint.

### How it works

When a user calls `POST /store/apps/{appId}/estimate` with their intended input, the system:

1. **Checks if all fee expressions are input-based** — if they only reference `task_inputs`, `prices`, `fees`, and `task_function`, the system evaluates the real expressions directly and returns an exact cost.
2. **Falls back to the estimate expression** — if any fee expression depends on post-execution data (`outputs`, `inputs`, `elapsed_ms`, `elapsed_seconds`, etc.), the system evaluates the `estimate` CEL expression instead.
3. **Returns unknown** — if no estimate expression is configured and the pricing depends on post-execution data.

### The `estimate` field

A single CEL expression on `AppPricing` that returns the estimated total cost. Only has access to pre-execution variables: `task_inputs`, `prices`, `fees`, `task_function`.

Returns either:
- A single **int** — exact estimate in microcents (confidence: `exact`)
- A **map** `{"min": int, "max": int}` — cost range in microcents (confidence: `range`)

```json
{
  "prices": {
    "per_image": 500000
  },
  "inference_expression": "image_count(outputs) * prices.per_image",
  "estimate": "get_int(task_inputs, \"n\", 1) * prices.per_image"
}
```

The `estimate` field is not needed when all fee expressions already use only pre-execution variables — the system detects this and evaluates them directly.

### The `estimable` field

Computed automatically at save time. `true` when all fee expressions can be evaluated from pre-execution data alone. Used as a fast path to skip AST analysis on every estimate request.

### Estimate endpoint

`POST /store/apps/{appId}/estimate` (public, no auth required)

```bash
curl -X POST https://api.inference.sh/store/apps/{appId}/estimate \
  -H "Content-Type: application/json" \
  -d '{"input": {"prompt": "a cat", "quality": "high"}, "function": "run"}'
```

Response:

```json
{
  "confidence": "exact",
  "microcents": 210720,
  "pricing_description": "$0.21 per image at high quality"
}
```

| Field | Description |
|-------|-------------|
| `confidence` | `exact`, `range`, or `unknown` |
| `microcents` | Cost in microcents (when `exact`) |
| `min`, `max` | Cost range in microcents (when `range`) |
| `depends_on` | Post-execution variables needed (when `unknown`) |
| `pricing_description` | Rendered pricing description string |

### Free runs

A run is free when the reviewed pricing is certain to settle at $0 for the input and function sent: every fee expression and the total read only pre-execution variables, and the total is $0 under every fee configuration. The `estimate` expression does not count. Callers can run a free app without credits once their workspace has a saved payment method. A free run places no pre-auth hold. See [Billing API: free runs](/docs/api/rest/billing#free-runs).

---

## Example

```json
{
  "prices": {
    "per_1k_tokens": 50000,
    "per_image": 200000
  },
  "inference_expression": "text_tokens(output_meta.outputs) * prices.per_1k_tokens / 1000",
  "description": "About to_dollars(prices.per_1k_tokens) per 1K output tokens plus to_dollars(prices.per_image) per image"
}
```

Draft pricing is edited in the store admin UI; publishing moves `draft_pricing` to live `pricing` on the listing version.

Preview formulas before publish with `POST /usage/cel/evaluate` — see [Usage API](/docs/api/rest/usage#evaluate-cel-pricing). For published store apps, call `POST /store/apps/{appId}/estimate` with draft task inputs to preview what end users see before they run — see [Store API — Estimate app cost](/docs/api/rest/store#estimate-app-cost).

---

## Next

→ [Output metadata](/docs/extend/output-meta) — what to report from your app  
→ [Task cost](/docs/concepts/tasks#cost) — how users see charges  
→ [Get task cost](/docs/api/rest/tasks#get-task-cost) — REST breakdown per task  
→ [Usage API](/docs/api/rest/usage): workspace spend breakdown and CEL preview
