Tags
Tags let you attribute costs to teams, environments, features, or anything else. Attach a JSON object to any request and query costs by those dimensions in the
Tags let you attribute costs to teams, environments, features, or anything else. Attach a JSON object to any request and query costs by those dimensions in the dashboard, API, or webhooks.
Sending Tags
Add the X-NullSpend-Tags header with a JSON object:
TypeScript
const response = await openai.chat.completions.create(
{
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
},
{
headers: {
"X-NullSpend-Tags": JSON.stringify({
team: "billing",
env: "production",
feature: "summarizer",
}),
},
}
);Python
import json
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={
"X-NullSpend-Tags": json.dumps({
"team": "billing",
"env": "production",
"feature": "summarizer",
}),
},
)cURL
curl https://proxy.nullspend.dev/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "X-NullSpend-Key: $NULLSPEND_API_KEY" \
-H 'X-NullSpend-Tags: {"team":"billing","env":"production"}' \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'Validation Rules
Tags are supplementary — they never cause a request to be rejected. Invalid tags are silently dropped.
| Rule | Limit |
|---|---|
| Max keys per request | 10 |
| Key pattern | [a-zA-Z0-9_-]+ |
| Key max length | 64 characters |
| Value max length | 256 characters |
| Reserved prefix | _ns_ — keys starting with this are silently dropped |
| Null bytes in values | Tag is silently dropped |
| Invalid JSON | All tags silently dropped (request proceeds with no tags) |
| Single invalid key/value | That key is dropped; valid keys are kept |
System Tags
NullSpend uses the _ns_ prefix for internal tags. You cannot set these — they are added automatically when applicable.
| Tag | Value | When |
|---|---|---|
_ns_estimated | "true" | Cost is an estimate (stream was cancelled before completion) |
_ns_cancelled | "true" | The streaming response was cancelled by the client |
Cost Attribution by Tags
The Attribution page lets you group costs by any tag value. Select a tag key from the dropdown and see a ranked breakdown of spend per value — with daily trends, model breakdowns, and CSV export.
This is the primary way to answer "how much does each customer cost me?" when you're tagging requests with customer_id or similar keys.
Querying by Tags
Dashboard
Filter cost events by tag key-value pairs in the analytics view, or use the Attribution page for aggregated per-tag-value breakdowns.
API
Use tag.* query parameters on GET /api/cost-events:
# All cost events tagged with team=billing (requires dashboard session)
curl "https://nullspend.dev/api/cost-events?tag.team=billing" \
-H "Cookie: session=..."
# Multiple tag filters (AND logic)
curl "https://nullspend.dev/api/cost-events?tag.team=billing&tag.env=production" \
-H "Cookie: session=..."Tag queries use PostgreSQL JSONB containment (@>), so they are indexed and fast.
Tags in Webhooks
Tags are included in the cost_event.created webhook payload under data.object.tags:
{
"id": "evt_abc123",
"type": "cost_event.created",
"api_version": "2026-04-01",
"created_at": 1711036800,
"data": {
"object": {
"request_id": "req_xyz",
"provider": "openai",
"model": "gpt-4o",
"cost_microdollars": 45,
"tags": {
"team": "billing",
"env": "production"
}
}
}
}See Webhook Event Types for the full payload.
Tag Budgets
You can create budgets scoped to a specific tag key-value pair. When spend for that tag exceeds the budget, the proxy blocks requests carrying that tag with 429:
{
"error": {
"code": "tag_budget_exceeded",
"message": "Request blocked: tag budget exceeded",
"details": {
"tag_key": "team",
"tag_value": "billing",
"budget_limit_microdollars": 50000000,
"budget_spend_microdollars": 49500000
}
}
}Tag budgets support the same features as user and API key budgets: reset intervals, threshold alerts, and velocity limits. See Budgets for configuration details.
Related
- Cost Attribution — group and drill into costs by any tag dimension
- Custom Headers Reference — header format and validation
- Cost Tracking — how costs are calculated
- Budgets — enforce spending limits including per-tag budgets
- Webhook Event Types — tags in webhook payloads
Session Limits
Session limits cap how much a single agent conversation can spend, regardless of the overall budget. A runaway agent loop that stays under velocity limits can s
Tracing
Tracing links related LLM requests so you can see what a multi-step agent run cost as a whole. Every request through the proxy gets a trace ID — either one yo