Metrics / routstr / model_activity

model_activity

Per-model Routstr activity over included providers - reported revenue (msats), successful requests and tokens per model identifier, at grain=day one row per (bucket, model), at grain=total one row per model summed over the from/to window (omit both for all time). The data behind the Models tab's 'Daily revenue by model' chart, served for EVERY model rather than the chart's top-6 fold.

unit

msats+requests+tokens

grain

day | total (UTC)

series

sparse, full history

key

routstr.model_activity@day

Parameters

paramaccepted values
grainday | total - default day (omit to get the default)
fromoptional inclusive ISO UTC calendar day (YYYY-MM-DD); omit for the series start
tooptional inclusive ISO UTC calendar day (YYYY-MM-DD); omit for today UTC (that day still in progress)
modeloptional model identifier, exact match on the provider-reported string as listed by routstr.model_leaderboard (max 60 chars; '(other)' is a valid value)
formatcsv | markdown

Columns per grain: day: utc_day, grain, model, msats, requests, tokens, providers - total: model, msats, requests, tokens, daily_buckets, weekly_buckets, first_day, last_day.

Query it

MCP tool call against https://dev.nostrolo.gy/mcp (connect your agent):

{
  "name": "nostrology_query_metric",
  "arguments": {
    "metric": "model_activity",
    "domain": "routstr",
    "grain": "day"
  }
}

Windowed (inclusive UTC days; both optional):

{
  "name": "nostrology_query_metric",
  "arguments": {
    "metric": "model_activity",
    "domain": "routstr",
    "grain": "day",
    "from": "2026-07-01",
    "to": "2026-07-31"
  }
}

Response: a plain-text envelope (window, freshness, column contract, this page's caveats) followed by the data table (csv, or format: "markdown") at the grain you request.

Response columns

nametypedescription
utc_daydatebucket START day (UTC); a weekly bucket covers the 7 days from it
graintextd = daily bucket; w = weekly bucket (pre-daily era only)
modeltextprovider-reported model identifier, verbatim (clipped at 60 chars); '(other)' = the providers' own residual plus any named tail folded past 100 rows
msatsintegerrevenue reported, integer millisats - per bucket at grain=day, summed over the window at grain=total
requestsintegersuccessful requests reported - per bucket at grain=day, summed over the window at grain=total
tokensintegertokens billed - per bucket at grain=day, summed over the window at grain=total
providersintegerdistinct included providers reporting the model in the bucket

What is counted

Included providers only; every model identifier that any included provider reported, plus '(other)'. Network aggregates cover INCLUDED providers only: test/staging-marked nodes and private-endpoint-only nodes are excluded (still listed, with the reason, by routstr.provider_leaderboard and the routstr.provider_profile record), and kind-38421 (routstr-provider-announcements) squatters are never providers.

Caveats

  • Every usage number is SELF-REPORTED by each provider's own node in kind-38422 (routstr-analytics-snapshots) events and is unverifiable by construction (RIP-01 payments travel as Cashu tokens in HTTP headers, invisible on Nostr): a node can overstate, understate or stay silent, so read these as what the network says about itself.
  • Model identifiers are the providers' own strings, verbatim (clipped at 60 chars): the same model appears both as 'openai/gpt-...' and 'gpt-...' and no normalization is applied; '(other)' is the providers' own residual for models they truncated off the wire and is part of the data.
  • Daily figures come from a ledger reconstructed by merging the UTC-grid buckets of EVERY archived snapshot's trailing windows (full-coverage-only, newest-wins); observed history is a FLOOR - a day a node later omits is kept, a gap longer than the windows' reach stays a gap - and today is never a ledger day.
  • Grain 'w' rows are weekly buckets (Thursday-start, keyed by their start day) kept only for a provider's PRE-daily era, so the two grains never overlap and summing every row gives the all-time figure. A from/to window includes exactly the DAILY buckets STARTING inside it and no weekly bucket at all - the page's own windowed rule - so weekly_buckets is 0 whenever a window is given.
  • Revenue is integer MILLI-satoshis (divide by 1000 for sats; sum msats first, convert once).
  • Responses are capped at 2,000 rows with an explicit truncated: line, and unfiltered this series exceeds it (every model on every active day): pass model= or a from/to window, or use grain=total. Series is sparse.

Freshness in every response is data-derived: the newest routstr refresh's generation instant (nightly 02:45 UTC; the newest ledger day is the previous UTC day).