# AI pricing

> [!NOTE] **Not enabled yet.** AI enrichment ships when a production model is connected; every number on this page comes from the pre-release test provider and will change. It is switched on per account, so nothing here describes real-world judgment quality yet. Where it is not enabled, a request that asks for it with the default `strict=true` is refused with `503 AI_UNAVAILABLE` at no charge; with `strict=false` you get the rows back unjudged. Enrichment is also switched on per platform: only the endpoints in the [table on the Labels page](https://onekey.dev/docs/labels.md#which-endpoints-accept-it) accept these parameters.

Enrichment is metered separately from the endpoint. A call costs the endpoint's normal [price](https://onekey.dev/docs/credits.md) plus one extra charge per enrichment step that judged something. The extra credits are in `metadata.extra_credits` and in each step's `extra_credits`, and `metadata.credits_charged` already includes them.

> [!WARN] **These are not final prices.** Every figure on this page was measured on the pre-release test provider, which reports synthetic usage. It shows how the charge scales, not what production judgments will cost. Do not budget from these numbers.

## How a step is charged

1. **Reserved.** Before judging, the most the step could cost for the rows it is allowed to judge is reserved, the way a paid data route is. If your balance or the key's [credit limit](https://onekey.dev/docs/key-limits.md) cannot cover it, nothing is judged (see below).
2. **Settled to actual.** After judging, the charge becomes what the step actually used, and the difference is returned. You pay for what was judged, not for the reservation.
3. **Recorded per step.** Labels, relevance and themes are separate lines, so `extra_credits` on each block tells you what that step cost.

## What is free

- **Cached judgments.** A text judged for the same preset (or the same relevance query) in the last 30 days is reused at no charge. A text judged for `sentiment` and `question` is a free hit for either one alone later.
- **Skipped rows.** Rows with no text, non-text, or under 3 characters are never sent to the provider.
- **Unjudged rows.** A row that could not be judged is not charged; `extra_credits` reflects only what was judged.
- **Repeats.** Identical texts within one call are judged once. The same call again is a data cache hit plus judgment cache hits, so it is free.
- **Idempotent replays.** A repeat with the same [`Idempotency-Key`](https://onekey.dev/docs/idempotency.md) returns the stored body, labels included, at 0 credits.
- **Refusals.** A request refused for an unknown preset ([`INVALID_LABEL`](https://onekey.dev/docs/errors.md#invalid-label)) or an enrichment parameter on an endpoint that does not accept it is rejected before any charge. With enrichment not enabled and `strict=true`, the call is refused before the data is fetched, so it is free too.

## Refunds

- With `strict=true` (the default), a step that ends with nothing judged is an error, and its reservation is refunded in full.
- A **partial** result charges only the rows that were judged.
- If your balance or your key's limit cannot cover the reservation, the call answers [`INSUFFICIENT_CREDITS`](https://onekey.dev/docs/errors.md#insufficient-credits) or [`KEY_BUDGET_EXCEEDED`](https://onekey.dev/docs/errors.md#key-budget-exceeded) naming the step. No enrichment charge is made. The endpoint's own charge stands and its data is cached, so retrying once you have topped up is a free cache hit for the data.

## What moves the charge

Output dominates: the charge grows with the number of rows judged and with the number of fields you ask for. **Fewer presets are cheaper**, so name only the ones you need with `label=` rather than taking the defaults.

## Measured examples

Recorded on the dev API with the pre-release test provider (synthetic usage, real arithmetic):

| Call | Judged | Extra credits | Per row |
| --- | --- | --- | --- |
| Hacker News story comments, the 4 comment presets | 32 rows | 60.225 | 1.88 |
| Search results, `label=sentiment` | 10 rows | 9.134 | 0.91 |
| Search results, `relevance` | 10 rows | 4.969 | 0.50 |
| `themes` (12 themes named in one call) | 26 rows | 7.822 | n/a |

Repeating the first call was 0 extra credits: a data cache hit and every judgment cached. A real provider will have its own figures; these pages will be updated with measured ones before enrichment leaves preview.

## Limits

| Limit | Value |
| --- | --- |
| Rows judged per request | 200. The rest stay `null` with `reason: request_cap` and `status: partial`. |
| Judged rows per account per UTC day | 5,000. Past it, nothing more is judged that day (`reason: account_cap`). |
| Service-wide daily allowance | A shared daily ceiling across all accounts (`reason: global_cap`). |
| Text per row | Clipped to 2,000 characters before judging; under 3 characters is skipped. |
| Judgment cache | 30 days. |

When a limit stops a call that would judge nothing, `strict=true` answers [`AI_BUDGET_EXCEEDED`](https://onekey.dev/docs/errors.md#ai-budget-exceeded) (`429`) with a `Retry-After` in seconds until the next UTC day, and `strict=false` returns the rows unjudged with no charge. A limit that stops only part of a call gives a partial result, never an error.

## Streams

On a [stream](https://onekey.dev/docs/streaming.md), the platform frames go out before the judgments, so their `credits_charged` excludes enrichment. The `enrich` frames follow, and `done` carries `extra_credits` and the full `credits_charged`. A failure after the platforms settled (402, 429 or 503) arrives as a single `error` frame, then `done`.

## See also

[Labels](https://onekey.dev/docs/labels.md), [Relevance](https://onekey.dev/docs/relevance.md), [Themes](https://onekey.dev/docs/themes.md), [Credits & pricing](https://onekey.dev/docs/credits.md) and [Errors](https://onekey.dev/docs/errors.md).
