Skip to content

About

Open-source Bittensor network explorer API, self-hostable alternative to TaoStats and TaoMarketCap with no API-level rate limits

Resources

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

OpenTaoAPI

Self-hosted Bittensor analytics: REST API, live SSE stream, webhooks, embeddable widgets, wallet watchlists with portfolio value over time. Everything reads direct from chain. No API key, no rate limit, no vendor lock-in.

A self-hosted alternative to TaoStats, TaoMarketCap, and tao.app. Run it on your own box, own your data, pay nothing.

Built solo by Ryan Mercier. Open to roles in Bittensor infra, crypto analytics, or trading systems. Contact.

Paper and live trading live in the sister repo OpenTaoTrader, which reads snapshots from this one over HTTP/SSE.

Web UI at http://localhost:8000 · Swagger docs at http://localhost:8000/docs

Live Demo

opentao.rpmsystems.io · running 24/7, polling chain on a 30-min cadence, no API key required.

Subnets dashboard ranked by market cap with live sparklines

Portfolio page with value-over-time chart and per-subnet token breakdown

What's actually here

Three things hosted alternatives can't do:

  • Webhooks, SSE stream, and embeddable widgets. Subscribe to threshold crossings, listen on the live event stream for snapshot inserts, drop an inline-SVG sparkline into any page without an API key. Webhooks speak Discord natively: point a subscription at a channel webhook URL with format: discord and get formatted embeds with no relay service in between. Whale alerts included (tao_flow fires when a subnet's TAO reserves jump by more than your threshold in one poll cycle).
  • A trader leaderboard built from chain truth. A background scanner decodes every StakeAdded / StakeRemoved event from every block, so each trade lands in the ledger with its exact fill price, slippage included. The P&L engine replays the ledger with average-cost accounting and ranks every coldkey by realized + unrealized return. Find who's actually good, watch them, or point OpenTaoTrader at them and copy their trades.
  • Wallet watchlist with persistent portfolio value over time. Add a coldkey; the background poller saves its portfolio value on a configurable cadence and the portfolio page charts it. Webhook on a tracked wallet's staked balance and you get pinged when a whale you follow moves. Self-hosted means nobody but you knows which wallets you watch.
  • TaoStats-compatible /miner/ endpoint. Drop-in replacement for the equivalent paid endpoint, so existing tooling keeps working.

Paper and live trading run in a separate process, OpenTaoTrader, which reads from this API over HTTP and SSE. Keeping the data layer and the agent layer in different repos means each one stays focused.

How it stacks up

Feature OpenTaoAPI TaoStats TaoMarketCap tao.app
Open source MIT no no no
Self-hostable yes no no no
API key required no yes yes yes
Rate-limited (free tier) none on self-host 5 req/min yes yes
Subnet prices and market caps yes yes yes yes
OHLC candles yes no yes yes
Miner and validator tables yes yes yes yes
Coldkey portfolio view yes yes yes yes
Persistent portfolio value over time (per-wallet history) yes partial partial partial
Full metagraph export yes limited no limited
Stake transfer tracking no yes no yes
Holder breakdowns no yes yes yes
Block and extrinsic data no yes no yes
TaoStats-compatible /miner/ endpoint yes n/a no no
Webhook alerts yes no no no
Live event stream (SSE) yes no no no
Embeddable no-auth widgets yes no no no

TaoMarketCap and tao.app are further along on breadth (stake transfers, holder analytics, block-level data). The point of OpenTaoAPI isn't to win that race; it's to be the only option you can run on your own box with webhooks, SSE, and a trading runner baked in.

Why I built this

TaoStats and TaoMarketCap both rate-limit and require API keys. I needed reliable subnet data for a mining dashboard and trading experiments, and the hosted services were either too slow on what I needed or paywalled it. The webhook, SSE, and embed primitives don't exist on the hosted side at all, so even paying wouldn't fix it. So I built it. The trading layer came later, once the snapshot pipeline was stable enough to backtest against.

Quick Start

With conda

conda create -n tao python=3.11 -y
conda activate tao
pip install -r requirements.txt
uvicorn api.main:app --host 0.0.0.0 --port 8000

First startup takes ~15-20s for the initial metagraph sync. Subsequent requests are instant from cache.

With Docker

docker compose up -d

The compose file publishes the API on http://localhost:3000.

Admin key

Reads are public. Every write (registering webhooks, adding or removing watched wallets, triggering backfills) requires an X-Admin-Key header matching OPENTAO_ADMIN_KEY. With the variable unset, writes return 503, so a fresh public deploy can't be abused. Generate one and put it in .env:

python -c "import secrets; print('OPENTAO_ADMIN_KEY=' + secrets.token_urlsafe(32))" >> .env

The web UI prompts for the key the first time you do a write action and keeps it in your browser's local storage. Webhook URLs are redacted to their host in public responses, since a Discord webhook URL is itself a credential.

Pre-seeded history

On first boot (no database file yet) the server downloads a seed database with the last ~90 days of subnet snapshots, so charts and candles work immediately instead of accumulating from empty. The seed is refreshed daily from the live demo instance and contains only subnet_snapshots: no webhooks, no watchlists, nothing operator-specific.

Set SEED_URL="" to start from a blank database instead.

If you run a long-lived instance and want to publish your own seed, scripts/upload_seed.sh exports a scrubbed copy and pushes it to a rolling GitHub release. Cron it daily:

15 4 * * * cd /path/to/OpenTaoAPI && ./scripts/upload_seed.sh >> /tmp/opentao-seed.log 2>&1

Architecture

See ARCHITECTURE.md for the design decisions behind the supervisor + per-RPC timeout pattern, the snapshot broker fan-out, and on-demand backfill coalescing.

Web UI

Page URL Description
Subnets dashboard / All subnets ranked by market cap with sparklines, live SSE-ticking prices
Subnet detail /subnet/{netuid} Interactive candlestick chart, miners/validators tabs, embed snippet, alert subscribe
Wallets /wallets Watchlist of tracked coldkeys with sparklines and last-poll age
Traders /traders Leaderboard of every coldkey seen trading, ranked by P&L, with one-click watch
Webhooks /webhooks Create, list, and delete webhook subscriptions
Portfolio /portfolio/{coldkey} Coldkey balance across all subnets, with portfolio value over time chart for tracked wallets

The subnet detail page uses lightweight-charts (Apache-2.0), vendored locally at frontend/vendor/ so self-hosted installs work offline. Refresh the vendored copy with:

curl -L -o frontend/vendor/lightweight-charts.standalone.production.js \
  https://unpkg.com/lightweight-charts@4.2.3/dist/lightweight-charts.standalone.production.js

Features

Full feature list (click to expand)
  • REST API with full subnet, neuron, emission, and portfolio data
  • TaoStats-compatible /miner/ endpoint for drop-in replacement
  • OHLC candles for every subnet (5m/15m/1h/4h/1d)
  • Wallet watchlist with portfolio value persisted over time
  • Webhook subscriptions for threshold crossings, with native Discord formatting
  • Whale alerts: per-cycle pool flow (tao_flow) and tracked-wallet stake changes (wallet_staked_change)
  • Stake-event scanner: every on-chain stake/unstake decoded into a queryable ledger with exact fills
  • Trader leaderboard: every coldkey ranked by realized + unrealized P&L, win rate, volume
  • Pre-seeded history on first boot via a daily-refreshed seed database
  • Server-Sent Events stream of live snapshot inserts
  • Embeddable SVG sparkline widgets (no auth required)
  • Web dashboard: portfolio viewer with history chart, wallet watchlist, subnets overview, miners/validators tables
  • Direct chain queries via Bittensor SDK (no third-party APIs except MEXC for price)
  • Historical data: SQLite storage with epoch-resolution snapshots via public archive node, live polling
  • Backfill scripts pull directly from chain and MEXC, no third-party API keys needed
  • In-memory caching with configurable TTLs, per-RPC timeouts, supervised background workers
  • /health returns HTTP 503 when the poller is stale. Wire it directly to docker healthcheck, Fly, Kubernetes, etc.
  • Self-hostable with Docker or conda

API Endpoints

Price

GET /api/v1/price/tao

Current TAO/USDT from MEXC. Cached 30s.

Portfolio

GET /api/v1/portfolio/{coldkey}
GET /api/v1/portfolio/{coldkey}/history?hours=168

/portfolio/{coldkey} is the live cross-subnet portfolio: total balance (TAO + USD), free balance, staked balance, and per-subnet breakdown with alpha balance, TAO equivalent, price, daily yield.

/portfolio/{coldkey}/history returns a time series of total portfolio value for any coldkey on the watchlist (see Wallets below). Empty array if the coldkey isn't tracked yet. Drop into a charting library or query straight from a notebook.

Paper and live trading

Trading is its own service. See OpenTaoTrader for the runner, the dashboard, the plugin strategy registry, and the live CLI. It points at any OpenTaoAPI instance (self-hosted or hosted) via OPENTAO_API_URL.

Traders (leaderboard + whale feed)

GET /api/v1/traders/leaderboard?days=30&min_volume=10&sort=pnl_pct
GET /api/v1/traders/{coldkey}                  # per-subnet P&L breakdown
GET /api/v1/traders/{coldkey}/events           # trade feed with position annotations
GET /api/v1/stake-events?min_tao=100           # recent whale moves, newest first

The stake scanner decodes StakeAdded / StakeRemoved from every block into a ledger, then the P&L engine replays it per coldkey with average-cost accounting. Fill prices come straight from the events (TAO paid / alpha received), so slippage is real, not estimated. Sort by pnl_pct, total_pnl_tao, volume_tao, or win_rate.

History accrues from when your instance starts scanning. Wallets that sell positions opened before the watch window get coverage: false; their realized numbers only reflect in-window activity. Root (netuid 0) stake is excluded since it's 1:1 with TAO.

The /events feed annotates each event with the position size after it and, for sells, the fraction of the position sold. That feed is what OpenTaoTrader's copy mode polls. The web UI lives at /traders, with a Watch button that adds any trader to the wallet watchlist in one click.

Scanner status (last scanned block, lag) is in /health. Set STAKE_SCANNER_ENABLED=false to turn it off.

Wallets (watchlist)

POST   /api/v1/wallets
GET    /api/v1/wallets
DELETE /api/v1/wallets/{coldkey}

Add a coldkey to the watchlist and the background poller refreshes it on the configured cadence (default 5 minutes), persisting one row per cycle to wallet_portfolio_snapshots. The portfolio page then has a real value-over-time chart instead of a single point. POST is idempotent: re-adding an existing coldkey reactivates polling and updates the label/interval without losing the snapshot history.

curl -X POST http://localhost:8000/api/v1/wallets \
  -H 'Content-Type: application/json' \
  -H "X-Admin-Key: $OPENTAO_ADMIN_KEY" \
  -d '{
    "coldkey": "5EhrSbeGeiLgsXcJTXXaBCcqrrMubvWcykSwk4Ho6KUd5sQG",
    "label": "demo",
    "poll_interval_seconds": 300
  }'

# list watchlist with each wallet's most recent snapshot inline (public)
curl http://localhost:8000/api/v1/wallets

# soft-delete; history is preserved, polling stops
curl -X DELETE -H "X-Admin-Key: $OPENTAO_ADMIN_KEY" \
  http://localhost:8000/api/v1/wallets/5EhrSbe...

poll_interval_seconds accepts 60-86400. The poller uses the same supervised-restart pattern as the subnet poller, so a stuck RPC on one wallet doesn't block the others.

Miner (TaoStats-compatible)

GET /api/v1/miner/{coldkey}/{netuid}

Response format matches the TaoStats /api/miner/ endpoint. Includes coldkey balance, alpha balances across all subnets, hotkey details with emission data, and mining rank.

Subnets

GET /api/v1/subnets                          # All subnets with market cap, emission %, price, volume
GET /api/v1/subnet/{netuid}/info             # Subnet hyperparams and pool data
GET /api/v1/subnet/{netuid}/neurons          # Paginated neuron list (?page=1&per_page=50)
GET /api/v1/subnet/{netuid}/metagraph        # Full metagraph (?refresh=true to bypass cache)
GET /api/v1/subnet/{netuid}/miners           # Miners with daily emission (?sort=incentive&order=desc)
GET /api/v1/subnet/{netuid}/validators       # Validators with stake, dividends, daily emission

Neurons

GET /api/v1/neuron/{netuid}/{uid}             # Single neuron by UID
GET /api/v1/neuron/coldkey/{coldkey}          # All neurons for a coldkey
GET /api/v1/neuron/hotkey/{hotkey}            # Neuron by hotkey

Emissions

GET /api/v1/emissions/{netuid}/{uid}

Emission breakdown: alpha per epoch, alpha per block, TAO per block, daily/monthly estimates in alpha, TAO, and USD.

Historical Data

GET /api/v1/history/{netuid}/price?hours=720      # Alpha price history (last 30 days)
GET /api/v1/history/{netuid}/snapshots?hours=168   # Full snapshots (last 7 days)
GET /api/v1/history/{netuid}/stats                 # Data coverage stats

Historical data is stored in SQLite. The backfill script queries the public archive node (wss://archive.chain.opentensor.ai) at epoch-level resolution (~30 min intervals). The live poller adds new snapshots on a cadence controlled by HISTORY_POLL_INTERVAL: default 30 minutes, -1 for every block (~12s), 0 to disable.

# Backfill last 30 days for one subnet (MEXC price fill runs automatically at the end)
python -m scripts.backfill --netuid 51 --days 30

# All subnets, last 7 days, 8 in parallel
python -m scripts.backfill --all-subnets --days 7 --concurrency 8

# From a specific block, keep TAO/USD zero (e.g. you'll fill later)
python -m scripts.backfill --netuid 51 --start-block 5000000 --skip-prices

# Resume where each subnet last stopped
python -m scripts.backfill --all-subnets --resume

# Also scrape metagraph totals (stake, emissions, neuron count, slower)
python -m scripts.backfill --netuid 51 --days 7 --full

# Re-fill tao_price_usd standalone (also runs automatically after backfill)
python -m scripts.backfill_prices

OHLC candles

GET /api/v1/subnet/{netuid}/candles?interval=1h&hours=168

Returns TradingView-style [{t, o, h, l, c, n}]. Valid intervals: 5m, 15m, 1h, 4h, 1d. Drop straight into lightweight-charts or Grafana.

Live stream

GET /api/v1/stream?netuid=1&netuid=64      # filter to specific subnets
GET /api/v1/stream                         # all subnets

Server-Sent Events. Each event is the JSON body of a freshly inserted snapshot. Heartbeat comments (: ping) every 15s. Works with any SSE client; try curl -N.

Webhooks

POST   /api/v1/webhooks/subscribe
GET    /api/v1/webhooks/{id}
DELETE /api/v1/webhooks/{id}

Subscribe to a price crossing:

curl -X POST http://localhost:8000/api/v1/webhooks/subscribe \
  -H 'Content-Type: application/json' \
  -H "X-Admin-Key: $OPENTAO_ADMIN_KEY" \
  -d '{
    "url": "https://your.endpoint/alerts",
    "netuid": 51,
    "metric": "alpha_price_tao",
    "direction": "cross_up",
    "threshold": 0.05
  }'

Level metrics (alpha_price_tao, tao_in, alpha_in, market_cap_tao) fire once per threshold crossing, not on every poll. Directions: above, below, cross_up, cross_down.

Discord alerts, no relay required

Set "format": "discord" and point url at a Discord channel webhook (Channel settings > Integrations > Webhooks). Events arrive as formatted embeds. Works for every metric.

Whale alerts

Two flow metrics measure per-poll deltas and fire on every qualifying event (above/below only):

  • tao_flow: change in a subnet's TAO reserves between consecutive snapshots. Someone staking 500+ TAO into SN64 in one poll cycle looks like this:
curl -X POST http://localhost:8000/api/v1/webhooks/subscribe \
  -H 'Content-Type: application/json' \
  -H "X-Admin-Key: $OPENTAO_ADMIN_KEY" \
  -d '{
    "url": "https://discord.com/api/webhooks/<id>/<token>",
    "netuid": 64,
    "metric": "tao_flow",
    "direction": "above",
    "threshold": 500,
    "format": "discord"
  }'

Use direction: below, threshold: -500 for dump alerts, and omit netuid to watch every subnet at once.

  • wallet_staked_change: change in a tracked wallet's total staked TAO between polls. Add the coldkey to the watchlist first (POST /api/v1/wallets), then:
curl -X POST http://localhost:8000/api/v1/webhooks/subscribe \
  -H 'Content-Type: application/json' \
  -H "X-Admin-Key: $OPENTAO_ADMIN_KEY" \
  -d '{
    "url": "https://discord.com/api/webhooks/<id>/<token>",
    "metric": "wallet_staked_change",
    "direction": "above",
    "threshold": 100,
    "coldkey": "5Whale...",
    "format": "discord"
  }'

Because the instance is self-hosted, nobody but you knows which wallets you watch. Resolution follows the wallet poll interval (default 5 minutes); a hosted provider can see your watchlist, this can't.

Security: subscribing requires the admin key, target URLs that resolve to private, loopback, link-local, or cloud-metadata addresses are rejected, and stored URLs are redacted in public reads.

Embeddable sparkline

GET /embed/subnet/{netuid}/sparkline?hours=24&w=240&h=60&stroke=%2300d4aa

Returns an inline image/svg+xml. Drop into any README or landing page:

<img src="https://your-opentao.example.com/embed/subnet/51/sparkline?hours=168" />

No API key, cache-friendly (Cache-Control: public, max-age=60).

Usage Examples

curl

# TAO price
curl http://localhost:8000/api/v1/price/tao

# Portfolio for a coldkey
curl http://localhost:8000/api/v1/portfolio/5EhrSbeGeiLgsXcJTXXaBCcqrrMubvWcykSwk4Ho6KUd5sQG

# All subnets ranked by market cap
curl http://localhost:8000/api/v1/subnets

# Subnet 51 miners sorted by incentive
curl "http://localhost:8000/api/v1/subnet/51/miners?sort=incentive&order=desc"

# Subnet 51 validators sorted by stake
curl "http://localhost:8000/api/v1/subnet/51/validators?sort=stake&order=desc"

# Miner info (TaoStats-compatible format)
curl http://localhost:8000/api/v1/miner/5GEP69yPWi3qB2tLQdsbv3Fa2JA6wH6szFNP77EqXizEufvM/51

# Emission breakdown for subnet 51, UID 40
curl http://localhost:8000/api/v1/emissions/51/40

# Historical alpha price for subnet 51 (last 30 days)
curl "http://localhost:8000/api/v1/history/51/price?hours=720"

# Historical data coverage stats
curl http://localhost:8000/api/v1/history/51/stats

Python

import os

import httpx

BASE = "http://localhost:8000/api/v1"
ADMIN_KEY = os.environ.get("OPENTAO_ADMIN_KEY", "")  # only needed for writes

# Portfolio
r = httpx.get(f"{BASE}/portfolio/5EhrSbeGeiLgsXcJTXXaBCcqrrMubvWcykSwk4Ho6KUd5sQG")
p = r.json()
print(f"Balance: {p['total_balance_tao']:.4f} TAO (${p['total_balance_usd']:.2f})")
for sn in p["subnets"]:
    print(f"  SN{sn['netuid']} {sn['name']}: {sn['balance_tao']:.4f} TAO  yield {sn['daily_yield_tao']:.4f}/day")

# Miner data (TaoStats-compatible)
r = httpx.get(f"{BASE}/miner/5GEP69yPWi3qB2tLQdsbv3Fa2JA6wH6szFNP77EqXizEufvM/51")
data = r.json()["data"][0]
print(f"Total balance: {int(data['total_balance']) / 1e9:.4f} TAO")
for hk in data["hotkeys"]:
    print(f"  UID {hk['uid']} rank #{hk['miner_rank']} emission {int(hk['emission']) / 1e9:.4f} alpha/epoch")

# Emissions
r = httpx.get(f"{BASE}/emissions/51/40")
em = r.json()
print(f"Daily: {em['daily_tao']:.4f} TAO (${em['daily_usd']:.2f})")

# OHLC candles, drop straight into a charting library
r = httpx.get(f"{BASE}/subnet/51/candles", params={"interval": "1h", "hours": 48})
for bar in r.json()[-5:]:
    print(f"{bar['t']}  O={bar['o']:.6f}  H={bar['h']:.6f}  "
          f"L={bar['l']:.6f}  C={bar['c']:.6f}")

# Live SSE stream (one event per inserted snapshot)
with httpx.stream("GET", f"{BASE}/stream", params={"netuid": 51}, timeout=None) as s:
    for line in s.iter_lines():
        if line.startswith("data: "):
            event = __import__("json").loads(line[6:])
            print(event["netuid"], event["alpha_price_tao"])

# Subscribe a webhook for a price cross
r = httpx.post(f"{BASE}/webhooks/subscribe", headers={"X-Admin-Key": ADMIN_KEY}, json={
    "url": "https://your.endpoint/alerts",
    "netuid": 51,
    "metric": "alpha_price_tao",
    "direction": "cross_up",
    "threshold": 0.05,
})
print("Subscription:", r.json()["id"])

Configuration

All settings via environment variables (or .env file):

Variable Default Description
BITTENSOR_NETWORK finney Network: finney, testnet, local
SUBTENSOR_ENDPOINT (empty) Custom subtensor websocket URL (e.g. ws://localhost:9944). Overrides BITTENSOR_NETWORK when set. Use this to connect to your own node and avoid public RPC rate limits.
CACHE_TTL_METAGRAPH 300 Metagraph cache seconds
CACHE_TTL_PRICE 30 Price cache seconds
CACHE_TTL_DYNAMIC_INFO 120 Subnet pool data cache seconds
CACHE_TTL_BALANCE 60 Balance/stake cache seconds
RPC_TIMEOUT 20.0 Per-RPC timeout (seconds). Prevents a single slow chain call from blocking the whole poll cycle.
ARCHIVE_ENDPOINT wss://archive.chain.opentensor.ai:443/ Archive node for historical backfill
DATABASE_PATH data/opentao.db SQLite database path for historical data
HISTORY_POLL_INTERVAL 1800 Seconds between live snapshots. 0 disables polling; -1 polls every block (~12 s).
HISTORY_POLL_NETUIDS (empty) Comma-separated netuids to poll (empty = all active)
OPENTAO_ADMIN_KEY (empty) Required in the X-Admin-Key header for every write endpoint. Unset means all writes return 503.
SEED_URL GitHub release asset Seed database downloaded on first boot when no DB file exists. Empty string disables.
STAKE_SCANNER_ENABLED true Decode stake events from every block into the trader ledger (2 RPCs per ~12s block).
STAKE_SCANNER_MAX_CATCHUP 1200 Max blocks of backlog to grind through after downtime before jumping to the tip. The public finney RPC only keeps about the last 256 blocks; older ones are skipped automatically. Point SUBTENSOR_ENDPOINT at an archive node to recover longer gaps.
API_HOST 0.0.0.0 Bind address
API_PORT 8000 Port

Project Structure

OpenTaoAPI/
├── api/
│   ├── main.py                 # FastAPI app, lifespan, poller + evaluator supervisors
│   ├── config.py               # Settings from environment
│   ├── routes/
│   │   ├── price.py            # TAO price from MEXC
│   │   ├── miner.py            # TaoStats-compatible miner endpoint
│   │   ├── neuron.py           # Neuron lookup by UID/hotkey/coldkey
│   │   ├── subnet.py           # Subnet info, metagraph, miners, validators, /subnets
│   │   ├── emissions.py        # Emission breakdown
│   │   ├── portfolio.py        # Cross-subnet portfolio + history endpoint
│   │   ├── wallets.py          # Watchlist CRUD (add/list/remove tracked coldkeys)
│   │   ├── traders.py          # Trader leaderboard, P&L detail, whale feed
│   │   ├── history.py          # Historical snapshots + OHLC candles
│   │   ├── stream.py           # Server-Sent Events live feed
│   │   ├── webhooks.py         # Threshold-crossing webhook subscriptions
│   │   └── embed.py            # Embeddable SVG sparkline widget
│   ├── services/
│   │   ├── chain_client.py     # Bittensor SDK wrapper with per-RPC timeouts
│   │   ├── price_client.py     # MEXC live price + historical klines
│   │   ├── cache.py            # In-memory TTL cache
│   │   ├── database.py         # SQLite storage (snapshots, webhooks, watchlist, stake events)
│   │   ├── broker.py           # Fan-out broker for live snapshot events
│   │   ├── pnl.py              # Average-cost P&L replay over the stake-event ledger
│   │   ├── portfolio_service.py # Shared portfolio compute (route + wallet poller)
│   │   ├── metagraph_compat.py # SDK version compatibility layer
│   │   └── calculations.py     # Emission math
│   └── models/
│       └── schemas.py          # Pydantic response models
├── scripts/
│   ├── backfill.py             # Historical chain scraper (parallel, resumable)
│   ├── backfill_prices.py      # MEXC kline backfill for tao_price_usd
│   ├── export_seed.py          # Scrubbed seed database export for first-boot installs
│   └── upload_seed.sh          # Daily seed publish to the rolling GitHub release
├── data/
│   └── opentao.db              # SQLite database (created on first run)
├── frontend/
│   ├── common.css              # Shared styles
│   ├── subnets.html            # Subnets dashboard (landing page)
│   ├── subnet-detail.html      # Per-subnet view: chart, miners, validators, embed
│   ├── webhooks.html           # Webhook management UI
│   ├── wallets.html            # Watchlist UI (/wallets)
│   ├── traders.html            # Trader leaderboard (/traders)
│   ├── index.html              # Portfolio page (/portfolio) with value-over-time chart
│   └── vendor/
│       └── lightweight-charts.standalone.production.js  # TradingView charts, Apache-2.0
├── docs/
│   ├── deploy-fly.md           # Fly.io deployment guide
│   └── images/                 # README screenshots
├── ARCHITECTURE.md
├── SUPPORT.md
├── docker-compose.yml
├── Dockerfile
├── requirements.txt
├── .env.example
├── .gitignore
└── LICENSE

How It Works

Data sources:

  • Bittensor chain via AsyncSubtensor for metagraph, balances, stake info, subnet data
  • MEXC public API for TAO/USDT price (no auth required, 500 req/10s limit)

Emission calculation:

alpha_per_day = meta.E[uid] / tempo * 7200
tao_per_day   = alpha_per_day * (pool.tao_in / pool.alpha_in)
usd_per_day   = tao_per_day * tao_price

Where meta.E[uid] is alpha per epoch, tempo is blocks per epoch (usually 360), and 7200 is blocks per day.

Validator yield is proportional to stake share: yield = emission * (my_stake / total_stake_on_hotkey).

Caching: metagraph syncs are expensive (~10-20s cold). All queries are cached in-memory with configurable TTLs. Use ?refresh=true on metagraph endpoints to force a fresh sync.

Deployment

docker compose up -d or the conda path in Quick Start gets you a local instance in under a minute. For Fly.io specifically, see docs/deploy-fly.md.

Contact

Built by Ryan Mercier (github.com/ryanmercier). Open to roles in Bittensor infrastructure, analytics, or trading systems. Issues and PRs welcome, or reach out directly.

ryan.mercier@rpmsystems.io

Donations: see SUPPORT.md.

License

MIT

About

Open-source Bittensor network explorer API, self-hostable alternative to TaoStats and TaoMarketCap with no API-level rate limits

Resources

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages