Skip to content

Unit economics

A unit metric divides a cost figure by a usage number to produce a ratio — cost per order, cost per 1,000 API requests, infra cost per customer. Plutus doesn’t compute the two halves itself: the cost side comes from the same cost data as the rest of the account, and the usage side comes from usage ingestion — telemetry your own backend pushes in — or from a pull-based usage source such as Stripe metered billing, Orb, or Metronome. A unit metric only reports numbers for periods where both sides are present. You need usage data flowing in for a metric before you can build a unit-economics ratio off it.

Editing or saving a unit metric needs the admin role. Reading the ratios is open to any role.

There are two modes, depending on what the denominator represents.

Divide one cost scope by the total usage of one metric, for a fixed period (hour, day, week, month, or quarter). The cost scope is one of:

  • The whole account
  • One cost source (AWS, Anthropic, Snowflake, etc.)
  • One tag value (see Virtual tagging & cost allocation). A tag-scoped cost includes that tag’s splits and any proportional redistribution, so it can be higher than a raw service or account total for the same resources.

Example: you push an orders metric via usage ingestion, one row per day with the day’s order count. You define a unit metric: AWS cost ÷ orders, scoped to the AWS cost source.

Day AWS cost Orders Cost per order
Mon $840.00 2,100 $0.40
Tue $912.00 2,280 $0.40
Wed $790.00 — (not received yet) —

Wednesday shows a real cost figure but no usage figure yet, so the ratio is —, not $0.00 or a number computed against zero orders. See Null periods below — this is expected, not an error.

Divide a tag-scoped cost by usage attributed to that same tag value, matched to an external customer ID. This mode requires a tag-scoped numerator: the numerator is cost for one tag_value under a tag_key you’ve defined rules for, and the denominator is usage rows whose external_customer_id (pushed via usage ingestion) matches that same tag value.

The mapping is a convention, not a schema constraint: you name the tag value after the customer identifier your backend already sends. If your cost_tag_rules tag customers by account slug (acme-corp, initech), your usage-ingestion payload needs to push acme-corp and initech as external_customer_id values for the match to work.

Example: you tag infrastructure cost by customer (tag_key = customer, values like acme-corp), and push a seats usage metric per customer. The unit metric divides tagged cost by seat count per customer:

Customer Tagged cost Seats Cost per seat
acme-corp $1,240.00 40 $31.00
initech $650.00 0 (no usage rows this period) —

Because the mapping depends on both sides agreeing on the same identifier, mismatches are common enough that Plutus reports them explicitly rather than silently dropping rows. The response breaks customers into three sets:

  • Matched — cost and usage both present and paired.
  • cost_without_usage — a tag value has cost but no usage rows matched it. Usually means the usage-ingestion payload isn’t sending that customer’s ID yet, or the ID doesn’t match the tag value exactly.
  • usage_without_cost — usage rows arrived for a customer ID with no matching tagged cost. Usually means the tag rule for that customer doesn’t exist yet, or was scoped differently.

The three sets are computed over the whole date window, not per period. A customer counts as matched if it has both cost and usage anywhere in the window, even if some individual periods have only one side.

Use these sets to fix the mapping rather than treating a missing ratio as a bug in the feature.

A period with no usage data reports the ratio as — (null), never 0 and never an error. This matters because a $0.00 cost-per-unit would assert something false — that the ratio is genuinely zero — when what actually happened is that usage telemetry hasn’t arrived for that period, or the customer-ID mapping in Mode B doesn’t line up yet.

The same rule applies in the other direction: a period with cost data but literally zero usage rows (not just rows not yet received) also reports —, since dividing by zero has no meaningful answer either.

A period with no cost data at all also reports — for the cost side, for the same reason — a sync that hasn’t landed yet is not a period that cost nothing.

The newest period can be missing for a different reason. Cost data arrives hours to a day after usage, so Plutus trims the end of the window to the point every contributing cost source has reported. A trailing period may not appear yet, or may show a partial period (a month-to-date ratio, for example). Both halves are trimmed at the same instant, so the ratio isn’t skewed. Gaps in the middle of a series are never trimmed; those are real missing data.

If you see — where you expect a number, check:

  1. Whether the usage-ingestion connection for that metric is actually sending data for the period in question.
  2. In Mode B, whether the external_customer_id values in your usage payload match the tag values in your cost-tagging rules exactly.

The cost side of a unit metric follows your account’s display currency — the underlying figure converts the same way any other cost chart does. The usage denominator is a count, not a currency amount, and is never converted. A “cost per order” ratio shown in EUR means the cost numerator was converted to EUR before dividing; the order count itself is unaffected by currency settings.

Unit metrics use billed cost only — the same figure a budget or the tier spend limit reads, before amortization of prepaid commitments (Reserved Instances, Savings Plans, reservations) is applied. If your account has amortized-cost data available, a unit metric’s ratio can diverge slightly from an amortized view of the same cost scope shown elsewhere. See Billed vs. amortized cost for the distinction.