NullSpend Docs

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.

RuleLimit
Max keys per request10
Key pattern[a-zA-Z0-9_-]+
Key max length64 characters
Value max length256 characters
Reserved prefix_ns_ — keys starting with this are silently dropped
Null bytes in valuesTag is silently dropped
Invalid JSONAll tags silently dropped (request proceeds with no tags)
Single invalid key/valueThat 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.

TagValueWhen
_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.

On this page