← All articles

AI Gross Margin per Customer: How to Calculate It

AI gross margin per customer is a customer’s revenue minus what it cost you to serve that customer’s AI usage, divided by that revenue. The cost side has to come from measured usage, meaning each model call priced from its model and token counts and tagged with the customer it served, not from your total provider bill split evenly. Once you have it, sorting customers by margin shows exactly which accounts lose money.

  • Formula: (revenue − AI cost to serve) ÷ revenue, per customer, per month.
  • AI cost to serve includes every billed call: retries, embeddings, cached input and paid tool APIs, not just the final answer.
  • Averages hide the problem; a blended 65% margin can contain a customer at −41%.
  • Getting per-customer cost needs a customer ID on every span of every request.

Why does the total AI bill hide unprofitable customers?

Your provider invoice is one number per model and per API key, so it tells you what you spent, not on whom. Usage per customer is rarely uniform: a few accounts with long documents, long conversations or agent loops can consume most of the tokens while paying the same plan price as everyone else.

What is the formula?

Gross margin per customer = (revenue − cost to serve) ÷ revenue. Use the same period for both, usually a calendar month.

  • Revenue: what the customer paid for that month, from your billing system.
  • AI cost to serve: the sum of every model call made on that customer’s behalf, plus paid tool or API calls.
  • Other cost to serve: hosting, vector database and support. Allocate these separately; traces do not measure them.

What belongs in AI cost?

Everything the provider bills for a customer’s requests. That includes input and output tokens, cached input (billed at a different rate by many providers), embeddings, calls made by retries and self-correction loops, and long-context surcharges on models that bill the whole call at a higher rate above a prompt-size threshold.

Tracium’s collector prices each call this way: it reads the model and token counts the instrumentation emits, applies separate cache-read and cache-write rates and long-context tiers from its price table, and stores the cost on the span. A tool or API span with no model can carry its own tracium.cost_usd, which is counted in run, workflow and client totals (cost and usage docs).

Why do averages lie?

Because one heavy account is offset by many light ones. An illustrative month for four customers:

Customer (illustrative)RevenueAI costGross margin
A$500$6088%
B$500$21058%
C$99$140−41%
D$99$892%
All$1,198$41865%

The blended 65% looks healthy. Customer C on the cheaper plan costs more than it pays, and you only see it once cost is split by customer.

How do you get real cost per customer from traces?

Instrument your LLM calls with OpenTelemetry, then tag every span with the customer it served. With OpenLLMetry, the model, token counts and span structure come from auto-instrumentation; the only thing you add is the customer ID:

import contextvars

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import SpanProcessor
from traceloop.sdk import Traceloop

current_customer = contextvars.ContextVar("current_customer", default=None)


class CustomerIdProcessor(SpanProcessor):
    def on_start(self, span, parent_context=None):
        customer = current_customer.get()
        if customer:
            span.set_attribute("tracium.user.id", customer)


# OTLPSpanExporter() reads OTEL_EXPORTER_OTLP_ENDPOINT and _HEADERS
Traceloop.init(app_name="support-agent", exporter=OTLPSpanExporter())
trace.get_tracer_provider().add_span_processor(CustomerIdProcessor())

# In your request handler:
# current_customer.set(request.customer_id)

The processor stamps every span, including the ones OpenLLMetry creates. That matters: Tracium sums cost per trace and takes the trace’s client from its spans, and its daily rollup groups spans by their own client ID, so a span without the ID can end up under an empty client. For a service that only ever serves one customer, putting tracium.user.id on the resource is enough (resource attributes). Exporter settings are the standard OTLP environment variables (OpenTelemetry setup).

A request sets the current customer, and every span in its trace (support-agent, chat, lookup_order) carries tracium.user.id. The collector prices each span from model and tokens with its price table, cost is divided by revenue from billing, and illustrative bars show gross margin per customer: A 88%, B 58%, C minus 41%, D 92%.
Every span carries the customer ID (illustrative numbers).

Full setup: how to track LLM cost per customer with OpenTelemetry.

How do you sort customers by margin?

  1. Export cost per customer for the month: Tracium’s Clients page shows it, or query POST /v1/query with view calls, metric cost and dimension user, which returns the same reconciled cost as the dashboard.
  2. Join it with revenue per customer from your billing system on the same customer ID.
  3. Compute margin and sort ascending.
  4. Compare the AI total with your provider invoice. Traced cost is an estimate from emitted tokens and the price table, so a large gap usually means uninstrumented calls or unpriced models.

What do you do with an underwater customer?

Open that customer’s most expensive runs first. The cause is usually one workflow: a long context that grows every turn, a retry loop, or a large model used where a small one would do. Then choose between fixing the workflow, adding usage limits, or moving the customer to a plan priced for their usage.

FAQ

What is a good gross margin for an AI product?

It depends on your pricing model and market, so compare against your own plan targets rather than a general benchmark. What matters for this exercise is the spread between customers on the same plan.

Can I estimate AI cost per customer without tracing?

You can split the invoice by request counts or seats, but that assumes every request costs the same, which is exactly the assumption that hides heavy users. Measured tokens per customer are the only reliable source.

Do I need to tag every span with the customer ID?

In Tracium, yes, unless the whole service serves one customer. Cost is summed per trace with the client taken from its spans, and the daily rollup groups spans by their own client ID.

If you want cost per customer without building the pipeline yourself, try the open-source repo at github.com/antonij-tracium/tracium or sign up free at app.tracium.ai.

To top