tokenstats¶
GitHub Repo | tokenstats.ai | Updated: November 2026
tokenstats watches the market for AI inference. It listens on Nostr for Routstr nodes announcing themselves, polls their model catalogs alongside OpenRouter and OrcaRouter, prices every model in USD and sats from a live BTC/USD rate, and serves the result as a live price board and a JSON API. It runs at tokenstats.ai.
Tech Stack: - Rust CLI with an Axum dashboard and HTTP API - Nostr (Routstr RIP-02 provider discovery, kind 38421) - SQLite (persistence and price history) - Coinbase BTC/USD spot price (USD and sats normalization) - Docker image on GitHub Container Registry, Caddy for HTTPS
License: Mozilla Public License 2.0 (MPL-2.0)
Why I Built This¶
tokenstats is the feed behind arbstr's routing. arbstr picks the cheapest qualified provider for each request, and tokenstats is where most of "qualified" comes from: arbstr 2.5 added require_healthy, which skips any endpoint tokenstats marks unhealthy. The two stay separate on purpose. tokenstats observes, arbstr executes. Observing is free, too: tokenstats never spends a sat or needs a key to measure a node.
The market it watches is messy. On any given cycle, roughly 70 Routstr nodes are announced on Nostr and fewer than half answer, and relays keep replaying announcements from nodes that are long gone.
How It Works¶
Nostr (kind 38421) ──► store.nodes ──► poll /v1/models ──┐
├──► store.quotes ──► dashboard / API
OpenRouter + OrcaRouter /v1/models ─────────────────────┘
Coinbase BTC-USD ──► oracle (USD ↔ sats)
SQLite snapshot every 30s
- Discovery and polling. Routstr nodes are discovered from Nostr announcements, and each node's
/v1/modelsis polled concurrently alongside the OpenRouter and OrcaRouter catalogs. - Signals. Every quote says how old it is, whether it's stale, and whether it's an outlier (more than 10x off that model's median). Reliability over 1 hour, 24 hours, and 7 days, plus p50 and p95 latency, are kept in SQLite, so they survive restarts. A
healthyflag on every quote combines the last fetch, 24 hour reliability of at least 90%, and freshness. - Same model, every source. Model ids are mapped to one canonical id, and the compare view (
/compare,/api/compare) shows one model's offers from every source side by side, with the spread and the cheapest healthy offer. - Capabilities. Quotes carry
tools,reasoning, andvisionflags. When a Routstr node doesn't publish them, they are filled from OpenRouter or OrcaRouter and marked as inferred. - Price history. A point is recorded whenever a catalog's published price for an offer changes: USD when the catalog lists USD, sats for sats-only nodes. Moves under 0.1% don't count, and prices converted at the BTC/USD rate are ignored, since they move on every poll.
/historydraws each offer's price as a step chart over 1, 7, 30, or 90 days with a log of the changes, and/api/historyserves the same points as JSON, grouped like/api/compare. Points are kept for 90 days by default. Quotes also carryprice_changed_atand the blended price change over 24 hours, 7 days, and 30 days. Tracking started November 1, 2026, so the longer windows are still filling in. - Safety. Nostr-announced nodes on private, loopback, or link-local addresses are never requested (SSRF guard), the node list is capped at 512, and the dashboard loads no third-party code unless analytics are configured.
What I Learned¶
My first pass at expiring dead nodes went by last-seen time alone, and it dropped 27 of 40 nodes in two cycles. Plenty of live nodes last announced months ago, and relays replay those announcements on every connect. The fix was to expire a node only after it has been neither announced nor answering for a full week of attempts. A market feed has to be skeptical of what it's told without throwing out what's still working.
Links¶
- tokenstats.ai
- GitHub Repository
- Blog post: tokenstats, the market feed behind arbstr's routing
- Routstr
A router is only as good as what it believes about the market.