---
title: "Metering rules"
description: "What one unit of a billable metric costs — flat, per-unit, or tiered by volume."
---

# Metering rules

What one unit of a metric costs.

A [metering rule](/docs/concepts/metering) prices one
[billable metric](/docs/concepts/metering). The rule sets how many
[millicredits](/docs/concepts/credits) one unit costs.

The price can be flat, per unit, or tiered by how much a customer used.
QuotaStack finds the active rule and debits the balance each time a usage
event comes in.

## Which call do I want?

- You are setting or changing the price of a billable metric → `POST /v1/metering-rules`
  This step turns off the old rule and makes the [billable metric](/docs/concepts/metering), if needed.
- You want the current or past rules for a metric → `GET /v1/metering-rules`

2 of 2 operations documented in full.

## List metering rules

`GET /v1/metering-rules`

- **Idempotency-Key:** Not used. This is a read.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Nothing. A read has no side effects.
- **Fires:** No webhooks.

This endpoint lists [metering rules](/docs/concepts/metering), oldest first. Filter by `billable_metric_key` for one metric’s price history. Add `active_only=true` to see only the rule charged today.

### Query parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `billable_metric_key` | string | optional | Only rules for this metric key. If you leave it out: You get every rule for this metric. |
| `active_only` | boolean | optional | Only the one rule charged today for each metric — the rule with no `effective_until` set. If you leave it out: You get every rule, old and new. |
| `cursor` | string | optional | Opaque pagination cursor from the previous page's `pagination.next_cursor`. Omit to start at the first page. |
| `limit` | integer | optional | Page size. Default 20, max 100. |

### Response 200

```json
{
  "data": [
    {
      "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a53",
      "billable_metric_key": "chat_message",
      "cost_type": "per_unit",
      "base_cost": 0,
      "unit_cost": 500,
      "tier_config": null,
      "effective_from": "2026-07-20T09:00:00Z",
      "effective_until": null,
      "metadata": {},
      "created_at": "2026-07-20T09:00:00Z",
      "updated_at": "2026-07-20T09:00:00Z"
    }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `data` | array | required | One entry per [metering rule](/docs/concepts/metering), oldest first. Old rules stay in the list unless you add `active_only=true`. |
| `pagination` | object | required | Cursor-based pagination envelope. Returned on every list endpoint. No `total` — cursor-only. |

### See also

- [Metering](/docs/concepts/metering)
- [Billable metrics](/docs/api/billable-metrics)

## Create a metering rule

`POST /v1/metering-rules`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** One new [metering rule](/docs/concepts/metering), active right away. If the metric had an old rule, QuotaStack turns it off. If the [billable metric](/docs/concepts/metering) itself did not exist yet, QuotaStack makes one.
- **Fires:** No webhooks.

Creating a rule sets the price for one [billable metric](/docs/concepts/metering). If `billable_metric_key` does not match one that exists, QuotaStack makes a plain one — type `metered`, named after the key. If a rule is already active for this key, QuotaStack turns it off first. Only one rule stays active per metric.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | A unique string you choose, up to 256 characters. It stops a retry from doing the work twice. This header is required on every POST and PATCH. Leave it out and the request fails with `422`, before any change is made. Send the same key twice and QuotaStack replays the first response. The second call's status and body match the first, and the response carries `X-Idempotent-Replayed: true`. Replays are available for 24 hours. Derive the key from the business event rather than a random value, so a retry from anywhere reuses it — for example `topup:{payment_intent_id}` or `renewal:{subscription_id}:{period_start}`. |

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `billable_metric_key` | string | required | The key of the [billable metric](/docs/concepts/metering) this rule prices. If you leave it out: QuotaStack rejects the call. This field is required. |
| `cost_type` | string | required | How QuotaStack turns units used into a charge. If you leave it out: QuotaStack rejects the call. This field is required. `flat` — One fixed charge per [consume](/docs/concepts/metering) call, no matter how many units. Set it with `base_cost`; `unit_cost` and `tier_config` are unused. `per_unit` — `unit_cost` times the units in that call. Set `unit_cost`; `base_cost` is stored but never charged for this type. `tiered` — Priced by `tier_config`, a price ladder keyed to how many units the call used. `base_cost` is stored but never charged for this type. |
| `base_cost` | integer (int64) | optional | The flat charge for a `flat` rule. Stored either way, but never charged for `per_unit` or `tiered`. Unit: millicredits, where 1 credit is 1000 millicredits. If you leave it out: Required for `flat`. QuotaStack sets it to 0 by default for `per_unit` and `tiered`. |
| `unit_cost` | integer (int64) | optional | The charge per unit for a `per_unit` rule. Unused for `flat`. A `tiered` rule sets its own `unit_cost` per tier instead. Unit: millicredits, where 1 credit is 1000 millicredits. If you leave it out: Required, and must be over zero, for `per_unit`. Unused otherwise, so you may leave it out. |
| `tier_config` | unknown | optional | The price ladder for a `tiered` rule. `mode` is `graduated` (each tier prices its own slice of the units) or `volume` (one tier prices every unit). Each tier sets `up_to` (its top unit count, `null` on the last tier only). Each tier also sets `unit_cost` ([millicredits](/docs/concepts/credits) per unit in that tier) and `flat_cost` (a one-time [millicredit](/docs/concepts/credits) charge for entering the tier). If you leave it out: Required for `tiered`. QuotaStack rejects the call without it. Unused for `flat` and `per_unit`. |
| `metadata` | object | optional | Your own key-value tags. QuotaStack stores them unchanged and returns them on every read. If you leave it out: Left out, QuotaStack stores `{}`. |

### Request

```json
{
  "billable_metric_key": "chat_message",
  "cost_type": "per_unit",
  "unit_cost": 500
}
```

### Response 201

```json
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a53",
  "billable_metric_key": "chat_message",
  "cost_type": "per_unit",
  "base_cost": 0,
  "unit_cost": 500,
  "tier_config": null,
  "effective_from": "2026-07-20T09:00:00Z",
  "effective_until": null,
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T09:00:00Z"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |
| `tenant_id` | string (uuid) | required |  |
| `billable_metric_key` | string | required | The key of the [billable metric](/docs/concepts/metering) this rule prices. |
| `cost_type` | string | required | How QuotaStack turns units used into a charge. `flat` — One fixed charge per [consume](/docs/concepts/metering) call, no matter how many units. Set it with `base_cost`; `unit_cost` and `tier_config` are unused. `per_unit` — `unit_cost` times the units in that call. Set `unit_cost`; `base_cost` is stored but never charged for this type. `tiered` — Priced by `tier_config`, a price ladder keyed to how many units the call used. `base_cost` is stored but never charged for this type. |
| `base_cost` | integer (int64) | optional | The flat charge for a `flat` rule. Stored either way, but never charged for `per_unit` or `tiered`. Unit: millicredits, where 1 credit is 1000 millicredits. |
| `unit_cost` | integer (int64) | optional | The charge per unit for a `per_unit` rule. Unused for `flat` and `tiered`. Unit: millicredits, where 1 credit is 1000 millicredits. |
| `tier_config` | unknown | optional | The price ladder for a `tiered` rule. `mode` is `graduated` (each tier prices its own slice of the units) or `volume` (one tier prices every unit). Each tier sets `up_to` (its top unit count, `null` on the last tier only). Each tier also sets `unit_cost` ([millicredits](/docs/concepts/credits) per unit in that tier) and `flat_cost` (a one-time [millicredit](/docs/concepts/credits) charge for entering the tier). `null` for `flat` and `per_unit` rules. |
| `effective_from` | string (date-time) | required | When this rule started pricing its metric, the moment you made it. |
| `effective_until` | string (date-time) | optional | `null` until a new rule takes over. Then QuotaStack sets the time here. |
| `metadata` | object | required | Your own key-value tags, unchanged since the last write. |
| `created_at` | string (date-time) | required |  |
| `updated_at` | string (date-time) | required |  |

### Errors

- `422` — `billable_metric_key` or `cost_type` is missing, or a field `cost_type` needs is wrong: `base_cost` for `flat`, a `unit_cost` over zero for `per_unit`, or a valid `tier_config` for `tiered`. The response names the field. See [validation-error](/docs/api/errors/validation-error).

### See also

- [Metering](/docs/concepts/metering)
- [Billable metrics](/docs/api/billable-metrics)
