Skip to main content
When one Opper account serves many customers, teams, or features, per-model spend isn’t enough — you want to know whose usage it was. Tags solve that: attach up to 8 key:value pairs to any call (customer_id:acme, team:eu, env:prod), and they’re recorded on the call’s billing and metrics rows. From there you can group spend and token counts by any tag key. Every call already carries built-in tags like model, so group_by=model works with no setup at all. Custom tags add your own dimensions on top. There are three ways to attach tags. They differ only in where the tags travel — pick whichever your client can send:

Tag with the X-Opper-Tags header

Works on every gateway (/v3/compat) call. The value is comma-separated key:value pairs; spaces after commas are fine, and sending the header more than once accumulates pairs.
Header values are taken literally — no escaping needed. A value may contain : (only the first colon splits key from value) but not ,. A malformed header is rejected with a 400 rather than silently dropped, so a templating bug on your side can’t quietly produce untagged (unattributable) usage.

Tag with the SDK

Opper’s native call takes a tags object directly:
Python

Tag with the URL

Some clients let you configure nothing but a base URL — a coding agent’s ANTHROPIC_BASE_URL, a vendor integration with a single “endpoint” field. For those, tags can ride in the URL path itself:
Everything after the tag segments is routed exactly like /v3/compat/..., so the client’s SDK appends its usual path (/chat/completions, /v1/messages, …) and every request through that base URL carries the tags:
Two things are specific to this mechanism:
  • session_id comes free. The sess_<uuid> segment (a lowercase UUIDv4 you generate) is recorded as a session_id tag on every call, so you can also pull usage for one session: GET /v2/analytics/usage?session_id=sess_.... This is how opper launch accounts for each coding-agent session.
  • Percent-encode tag values. Values are URL path segments and are decoded exactly once — encode any character that would break a path (/%2F, and so on).
If a request carries both URL tags and an X-Opper-Tags header, the URL’s value wins for any key present in both.

Limits and validation

The same rules apply to all three mechanisms. Invalid tags fail the request with a 400 — they are never silently dropped.

Slice usage by tag

Analytics API

GET /v2/analytics/usage aggregates cost (and any metadata fields you ask for) over time, and group_by=<tag key> splits every time bucket by that tag’s values:
Untagged usage shows up with the group key null, so the rows always sum to your total spend — nothing hides just because it wasn’t tagged.

CLI

The Opper CLI wraps the same endpoint:

Dashboard

The usage page at platform.opper.ai has a Tag filter that scopes the whole page — spend chart, breakdowns, attribution, CSV export — to one key:value tag. The dropdown lists your organization’s recently seen tags, so anything you tag shows up there automatically.
The dashboard Tag filter is rolling out and may not be visible on your account yet. The analytics API and CLI above work today.

What’s next

Drop-in SDKs

Point your existing OpenAI, Anthropic, or Google SDK at the gateway.

Traces

Tags on spans make individual traces filterable, too.

Integrations

LiteLLM, Vercel AI SDK, and more — with per-tenant attribution built in.

Models & pricing

Per-token pricing for every model the gateway serves.