CostGraph pulls it
You supply read-only credentials and we call the provider once a day.
You push it
You send us FOCUS records. Credentials never leave your infrastructure.
We read your telemetry
Your gateway already measures token usage. No credentials at all.
Supported gateways
Each was run and measured rather than read about: every one reports tokens
differently, and two of them do not follow the OpenTelemetry GenAI conventions
at all. CostGraph normalises that for you. Each of the preceding pages lists the
metrics we read, how their labels map, and the upstream metrics reference it was
built against.
Connect
- Open Settings > Integrations and pick your gateway.
- Name the connection and pick the tenant this usage belongs to.
- Under Where it runs, select A Kubernetes cluster or A virtual machine, then pick the cluster or the instance.
- Select Connect.
Pick Neither and the connection takes the usage you post to it instead. See
Send usage yourself.
Kubernetes cluster
Enable the AI gateway scraper in your operator values once:operatorPrometheus.scrapeTargets takes precedence over a served one.
Pod selector
The pod selector is a comma-separated list ofkey=value terms, the same
syntax kubectl get pods -l takes. Every term must match: the list is ANDed, so
a pod is scraped only if it carries all of them.
!=, in (...), notin (...), and bare existence
checks are not supported: CostGraph resolves the same selector to the pods it
amortizes gateway cost across, and that resolution accepts key=value only.
Match the labels the data plane pods actually carry, which you can read off the
running pods:
Virtual machine
A gateway on a virtual machine is scraped by the CostGraph agent on that machine. The agent reads the gateway’s metrics endpoint on the machine’s own loopback address, so nothing is exposed to the network and no credentials are collected. To connect one:- Under Where it runs, select A virtual machine.
- Select the machine under Instance.
- Confirm Metrics port, or change it if the gateway serves its metrics somewhere else.
Default ports
CostGraph prefills Metrics port with the port the gateway serves metrics on in a stock install:
The prefill is a starting point, not a constraint. Running a gateway on another
port is ordinary, so change the field to whatever yours serves on. Clearing the
field falls back to the default.
The port is the only thing that locates the gateway. The agent confirms it found
the right process by checking the metric names the endpoint serves. You don’t
name a process, a service, or a unit.
A wrong port
A port nothing serves fails quietly. The agent finds nothing to scrape, so no metrics arrive. The connection stays pending rather than reporting an error, and no surface says why. A connection sitting pending with no usage is the symptom, and the port is the first thing to check. Read the port off the running host before you rely on it:Attribute usage to your customers
If you resell access, tell your gateway to label each request with your customer’s id. CostGraph reads that label into the FOCUS sub-account columns, so per-customer usage shows up wherever you already group by sub-account.Envoy AI Gateway
Envoy AI Gateway
Map a request header onto the metrics. This is a controller-wide setting, so
every gateway that controller manages shares the mapping.The attribute
customer.id arrives as the label customer_id. Dots become
underscores on the Prometheus side.See Envoy AI Gateway.Solo.io agentgateway
Solo.io agentgateway
Add a metric attribute computed from the request. On Kubernetes this goes in
an AgentgatewayPolicy. The value is a CEL expression, so the identifier can
come from a header, a JWT claim, or anything else in the request.The policy must live in the same namespace as the Gateway it targets.See Solo.io agentgateway.
Apache APISIX
Apache APISIX
Nothing to configure. Give each customer its own APISIX consumer; the
consumer label is already on every LLM metric, because consumers are
APISIX’s own auth identities.APISIX only starts reporting LLM metrics after the first AI request passes
through it, so an empty scrape before any traffic is expected.See Apache APISIX.vLLM
vLLM
Not available per request. vLLM is a model server, not an auth boundary, and
reports only the model name, so no configuration changes that.Either front it with a gateway that can attribute per request, or run one
deployment per customer and annotate the workload:See vLLM for the metrics we read
and how a model you host yourself is priced.
The annotation fills in a customer only where the gateway did not report one
itself. A gateway that labels each request wins, so annotating a shared
gateway does not collapse its customers onto one sub-account. Annotate the
workload only where per-request attribution is not available.
How usage is priced
Rates resolve in order, and the first match wins:- Your own rate, if the workload carries a
costgraph.ai/pricing-id.modellabel. Register the rate once in the pricing marketplace and tag with the id it returns. See Pricing IDs. - A hosted reference rate for the same model, from the model catalog. This answers “what would this have cost on someone else’s API”.
- Your own amortized rate, for a model you serve yourself. CostGraph divides what the serving namespace cost that day by the tokens it served that day, so a self-hosted model prices at what it actually cost you to run rather than at somebody else’s list price.
Only token usage prices today. Images, audio seconds and characters are
captured and stored now, and will price once the catalog carries rates for
them, so nothing is lost in the meantime.
Connection settings
Three fields tune how a connection’s usage is priced and attributed. You setmodel_map and attribution_spec when you connect the gateway, and
tenant_map on a connection that already exists.
Model map
Your gateway reports whatever model string its callers asked for.model_map
translates those raw identifiers into the keys the CostGraph model catalog
prices. Usage that matched no rate starts pricing.
Reach for it when a connection reports that models could not be priced. The
message names the raw identifiers the gateway sent, and each one is a key you
map:
Tenant map
Turns raw sub-account values into the names you want to read:Attribution spec
Declares where each attribution dimension is read from. Every rule names the dimension, its source, and the key to read:source is prom_label for a gateway we scrape, focus_field for usage you
push, or static for a fixed value carried in key. Adding
"transform": "sha256" hashes the value before it is stored, for an identifier
you would rather not keep in the clear. A rule marked "required": true fails
the batch when its key is absent, rather than storing usage with the dimension
missing.
sub_account_dimension picks which dimension fills the FOCUS sub-account
columns. The others ride along as cost extensions, so you can group by them
alongside spend.
Omit the spec and a connection still attributes usage: a single sub_account
dimension reads costgraph_sub_account on a scraped gateway, and SubAccountId
on a pushed row.
Send usage yourself
If your gateway runs outside a CostGraph-managed cluster, send the usage directly instead. The rows are FOCUS 1.2 shaped with the cost columns left empty, because CostGraph prices them:focus:write scope.
Send from anything else
CostGraph reads one metric family. Anything that can write it can be a source - a Prometheus server, an OpenTelemetry Collector, or Grafana Alloy, whether or not it runs on Kubernetes:The labels
A series missing any required label, carrying an unknown
costgraph_usage_kind
or costgraph_unit, or reporting a value of zero or less, is skipped rather
than half-priced.
Any other label rides along and is available to
attribution dimensions, which read the
labels your gateway already emits, such as team, route or gen_ai_provider_name,
into sub-account and cost extensions. Nothing needs renaming to
costgraph_ first.
Remote-write it to https://tsdb.costgraph.ai/api/v1/write with an API key in
the X-API-Key header. A gateway CostGraph has never heard of works this way
with no changes on our side.