For the complete documentation index, see llms.txt. Markdown versions are available by appending .md to documentation URLs.

AI Observability Model Pricing - Set LLM Token Costs

SigNoz Cloud - This page applies to SigNoz Cloud editions.
Self-Host - This page applies to self-hosted SigNoz editions.

Use the Model pricing tab of AI Observability to set the price for each model. SigNoz uses these prices to calculate the cost of each span in USD. For prerequisites, see the AI Observability overview.

To open the tab, select More > AI Observability in the side navigation, then select the Model pricing tab.

The tab has two parts: Model Costs lists your pricing rules, and Unpriced models lists models that no rule prices.

Model Costs tab

SigNoz Cloud includes default prices for common models. In Self-Hosted SigNoz, add a pricing rule for each of your models, or sync prices from OpenRouter.

ColumnWhat it shows
ModelThe billing model name and its ID, for example openai:gpt-4o.
ProviderThe provider of the model, for example OpenAI or Anthropic.
Input / 1MThe price for one million input tokens, in USD.
Output / 1MThe price for one million output tokens, in USD.
Extra bucketsThe prices for cached tokens (Cache Read, Cache Write), if the rule has them.
SourceAuto for a default price from SigNoz Cloud or the OpenRouter sync script (Auto-populated in the drawer), or User override for a price that an Admin set.
Last seenThe time since the rule was last updated or synced.
Model Costs tab in AI Observability Model pricing, filtered by the search text claude, with the Add model cost button
The Model Costs tab, filtered by model name

To find a rule, type a model name or a provider name in the Search by model or provider box. To show only one type of rule, select User override or Auto in the source filter.

To share the filtered table, copy the page URL.

Add a pricing rule

Only Admins can add, edit, map, or delete pricing rules.

  1. On the Model Costs tab, select Add model cost.
  2. In Billing Model ID, type an ID for the rule, for example openai:gpt-4o. You cannot change the ID or the Provider after you save.
  3. Select the Provider.
  4. In Model name patterns (prefix match), add the gen_ai.request.model values that this rule prices. To match every name that starts with a prefix, end the pattern with *, for example gpt-4o*. A pattern without * matches only that exact name.
  5. In Pricing (per 1M tokens, USD), type the Input Cost and the Output Cost. If the provider charges a different cached-token price, select Add pricing bucket. Add a cache_read or cache_write price.
  6. If you added cache prices, select the Cache mode that matches the provider:
    • Subtract (OpenAI style): the input token count includes cached tokens. SigNoz subtracts the cache-read tokens from the input tokens and bills them at the cache_read price.
    • Additive (Anthropic style): the input token count excludes cached tokens. SigNoz bills input, cache_read, and cache_write tokens at their own prices.
    • Unknown (default): SigNoz bills all input tokens at the input price and does not use the cache prices.
  7. Select Save.

Edit or delete a pricing rule

To edit a rule, open the menu (⋮) at the end of its row and select Edit.

Edit model cost drawer with Billing Model ID, Provider, Model name patterns, Source, and read-only pricing for an Auto-populated rule
The Edit model cost drawer for a rule with default pricing

You cannot change the prices of an Auto-populated rule. To set your own, change the source to User override. To go back to the default price, select Auto-populated again and confirm. Your custom prices stay until the next price sync replaces them. See When price changes take effect.

To delete a rule, open the menu (⋮) on its row. Select Delete, then confirm.

Price an unpriced model

The Unpriced models tab lists models that SigNoz found in spans from the last hour but that match no pricing rule. SigNoz cannot calculate cost for spans from these models. In this tab, the UI calls a pricing rule a pricing model.

To price a model:

  1. In the Map to billing model column of the model, open the Select / Create a pricing model dropdown.
  2. Select an existing rule.
  3. Select Map model. SigNoz adds the model name as a pattern on that rule.

To set a new price instead, select Create a new pricing model at the bottom of the dropdown. The Add model cost drawer opens with the model name in Billing Model ID. Complete it as in Add a pricing rule.

When no model from the last hour is unpriced, the tab shows "All models in your traces are priced."

Sync prices from OpenRouter in Self-Hosted SigNoz

Self-Hosted SigNoz has no default prices. To add prices for common models, run the script below. It reads current prices from the OpenRouter models API and saves one Auto pricing rule for each model. It uses the same providers and model filters as the default prices in SigNoz Cloud.

Before you start, install Python 3.8 or later, and create an API key for a service account with the Admin role. Run the script on a machine that can connect to openrouter.ai and to your SigNoz URL.

  1. Save the script as sync_llm_prices.py:
sync_llm_prices.py
"""Sync LLM model prices from OpenRouter into Self-Hosted SigNoz."""
import fnmatch
import json
import re
import urllib.request
import uuid
 
SIGNOZ_URL = "<signoz-url>"
SIGNOZ_API_KEY = "<your-api-key>"
PROVIDERS = {"openai", "anthropic", "google", "mistralai", "deepseek", "qwen", "x-ai", "meta-llama", "cohere", "amazon"}
CACHE_MODES = {"openai": "subtract", "anthropic": "additive"}
 
 
def price(pricing, key):
    try:
        return float(pricing[key]) * 1_000_000
    except (KeyError, TypeError, ValueError):
        return None
 
 
def source_id(model_id):
    return str(uuid.uuid5(uuid.NAMESPACE_URL, f"https://openrouter.ai/{model_id}"))
 
 
def to_rule(model):
    model_id = model["id"]
    provider, _, name = model_id.partition("/")
    if provider not in PROVIDERS or not name or ":" in model_id or model_id.startswith("~"):
        return None
    if provider == "qwen" and not re.search("max|plus|flash|turbo", name):
        return None
    pricing = model.get("pricing", {})
    input_price, output_price = price(pricing, "prompt"), price(pricing, "completion")
    if (input_price or 0) <= 0 or (output_price or 0) <= 0:
        return None
    rule_pricing = {"input": input_price, "output": output_price}
    read, write = price(pricing, "input_cache_read"), price(pricing, "input_cache_write")
    if read is not None or write is not None:
        rule_pricing["cache"] = {"mode": CACHE_MODES.get(provider, "unknown"), "read": read or 0, "write": write or 0}
    return {
        "sourceId": source_id(model_id),
        "modelName": name,
        "provider": provider,
        "modelPattern": [model_id, name],
        "unit": "per_million_tokens",
        "pricing": rule_pricing,
        "enabled": True,
    }
 
 
def signoz(path, body=None):
    request = urllib.request.Request(
        f"{SIGNOZ_URL}{path}",
        data=body and json.dumps(body).encode(),
        headers={"SIGNOZ-API-KEY": SIGNOZ_API_KEY, "Content-Type": "application/json"},
        method="PUT" if body else "GET",
    )
    with urllib.request.urlopen(request, timeout=60) as response:
        return json.load(response) if not body else None
 
 
existing = []
while True:
    page = signoz(f"/api/v1/llm_pricing_rules?limit=100&offset={len(existing)}")["data"]["items"]
    existing += page
    if len(page) < 100:
        break
override_patterns = [pattern for rule in existing if rule["isOverride"] and rule["enabled"] for pattern in rule["modelPattern"]]
 
 
def matches(name, pattern):
    # Same as the collector's path.Match: * and ? do not cross "/".
    names, patterns = name.split("/"), pattern.split("/")
    return len(names) == len(patterns) and all(map(fnmatch.fnmatchcase, names, patterns))
 
 
def overridden(name):
    return any(matches(name, pattern) for pattern in override_patterns)
 
 
with urllib.request.urlopen("https://openrouter.ai/api/v1/models", timeout=60) as response:
    models = json.load(response)["data"]
rules = [rule for rule in map(to_rule, models) if rule]
previous = {rule["sourceId"]: rule for rule in existing if rule.get("sourceId")}
for rule in rules:
    # Keep names that you mapped to this rule, and drop names that a User override rule prices.
    mapped = previous.get(rule["sourceId"], {}).get("modelPattern", [])
    names = rule["modelPattern"] + [name for name in mapped if name not in rule["modelPattern"]]
    kept = [name for name in names if not overridden(name)]
    rule["modelPattern"], rule["enabled"] = (kept, True) if kept else (names, False)
 
# Disable rules from earlier runs for models that OpenRouter no longer lists or prices.
synced = {rule["sourceId"] for rule in rules}
fields = ("sourceId", "modelName", "provider", "modelPattern", "unit", "pricing")
stale = [{**{field: rule[field] for field in fields}, "enabled": False}
         for rule in existing
         if not rule["isOverride"] and rule.get("sourceId") not in synced
         and rule.get("sourceId") == source_id(f"{rule['provider']}/{rule['modelName']}")]
 
enabled = sum(rule["enabled"] for rule in rules)
print(f"Syncing {enabled} model prices to {SIGNOZ_URL}")
print(f"Disabling {len(rules) + len(stale) - enabled} rules that User override rules cover or OpenRouter dropped")
signoz("/api/v1/llm_pricing_rules", {"rules": rules + stale})
print("Done")

Verify these values:

  • <signoz-url>: The URL of your SigNoz instance, for example http://localhost:8080.
  • <your-api-key>: The API key of a service account with the Admin role.

To sync other providers, edit PROVIDERS. Each value is the prefix of an OpenRouter model ID, for example openai in openai/gpt-4o.

  1. Run the script:
python3 sync_llm_prices.py

The script prints the number of rules that it synced and disabled, then Done.

  1. On the Model Costs tab, filter by Auto to see the new rules.

If an enabled User override rule prices a model name, the script removes that name from its own rule. It disables the rule when no names remain. If OpenRouter stops listing or pricing a model, the script disables that rule too. Each run updates the prices of the other rules that the script created and keeps the names that you mapped to them. To keep prices current, run the script once a day, for example with a cron job.

The rules match OpenRouter model IDs, such as claude-3.7-sonnet and anthropic/claude-3.7-sonnet. If your spans use a different name, such as claude-3-7-sonnet-20250219, map it on the Unpriced models tab. See Price an unpriced model.

When price changes take effect

A price change applies to spans that SigNoz receives after the change. If you reset a rule to the default price on SigNoz Cloud, the reset can take up to 24 hours. In Self-Hosted SigNoz, the default price returns on the next run of the sync script. Stored spans do not change.

Cost attributes on spans

You do not send cost. When SigNoz receives a span, it calculates cost from the token counts and the price in the matching pricing rule. SigNoz writes the total to signoz.gen_ai.usage.tokens.cost, and one cost attribute for each token type:

  • signoz.gen_ai.usage.input_tokens.cost
  • signoz.gen_ai.usage.output_tokens.cost
  • signoz.gen_ai.usage.cache_read.input_tokens.cost
  • signoz.gen_ai.usage.cache_write.input_tokens.cost

Validate

  1. After the new price takes effect, send a new LLM span for the model from your application.
  2. In the Explorer List View, run a filter on the model, for example gen_ai.request.model = 'gpt-4o'.
  3. Select a span. The span opens in Trace Details.
  4. In the span attributes, make sure that signoz.gen_ai.usage.tokens.cost exists. If it is missing, no rule matches the model name, so check the patterns of the rule. A value of 0 means the price or the token count is zero.

Next steps

Get Help

If you need help with the steps in this topic, please reach out to us on SigNoz Community Slack. If you are a SigNoz Cloud user, please use in product chat support located at the bottom right corner of your SigNoz instance or contact us at cloud-support@signoz.io.

Is this page helpful

Last updated—October 12, 2026

Edit on GitHub