Metrics / routstr / provider_activity
provider_activity
Per-provider Routstr activity from the reconstructed ledger - reported revenue (msats), successful requests and tokens per provider, at grain=day one row per (bucket, provider), at grain=total one row per provider summed over the from/to window (omit both for all time). This is the data behind the Providers tab's 'Daily revenue by provider' chart, served for EVERY included provider rather than the chart's top-20 fold.
unit
msats+requests+tokens
grain
day | total (UTC)
series
sparse, full history
key
routstr.provider_activity@day
Parameters
| param | accepted values |
|---|---|
| grain | day | total - default day (omit to get the default) |
| from | optional inclusive ISO UTC calendar day (YYYY-MM-DD); omit for the series start |
| to | optional inclusive ISO UTC calendar day (YYYY-MM-DD); omit for today UTC (that day still in progress) |
| provider_pubkey | optional 64-char lowercase hex Routstr provider key (npub must be decoded to hex first); narrows rows to that provider, and reaches an excluded provider that the unfiltered call leaves out |
| format | csv | markdown |
Columns per grain: day: utc_day, grain, provider_pubkey, provider_name, msats, requests, tokens, excluded - total: provider_pubkey, provider_name, msats, requests, tokens, daily_buckets, weekly_buckets, first_day, last_day, excluded.
Query it
MCP tool call against https://dev.nostrolo.gy/mcp (connect your agent):
{
"name": "nostrology_query_metric",
"arguments": {
"metric": "provider_activity",
"domain": "routstr",
"grain": "day"
}
} Windowed (inclusive UTC days; both optional):
{
"name": "nostrology_query_metric",
"arguments": {
"metric": "provider_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
| name | type | description |
|---|---|---|
| utc_day | date | bucket START day (UTC); a weekly bucket covers the 7 days from it |
| grain | text | d = daily bucket; w = weekly bucket (pre-daily era only) |
| provider_pubkey | text | the provider's Nostr key - provider identity (the announcement's provider_id is informational only) |
| provider_name | text | announcement content.name, clipped to 80 chars; the 12-hex pubkey prefix when never announced |
| msats | integer | revenue reported, integer millisats - per bucket at grain=day, summed over the window at grain=total |
| requests | integer | successful requests reported - per bucket at grain=day, summed over the window at grain=total |
| tokens | integer | tokens billed - per bucket at grain=day, summed over the window at grain=total |
| excluded | boolean | true = the provider is outside the network aggregates (only reachable via the provider_pubkey filter) |
What is counted
Without a provider_pubkey filter the rows cover INCLUDED providers only, so the per-day sums equal routstr.network_activity exactly; with a filter the named provider is returned whether or not it is excluded (the excluded column says which). 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.
- 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 (the whole ledger was ~1,100 rows on 2026-08-24, so an unfiltered call fits today); pass from/to or provider_pubkey to keep a response small. Series is sparse: a (bucket, provider) row exists only where the provider reported activity.
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).