Leaderboards
Rank every player, live, from the events your game already emits.
players?order=wins.descPoint Nineveh at your contract's address. Get a database and an API that stay in sync with the chain — sorted, filtered, aggregated, live. No indexer to write, nothing to run.
| seller | sold | revenue |
|---|---|---|
| 0x7a3f…c41d | 1 | 515 |
| 0x1e87…8d2a | 1 | 705 |
| 0x9b02…4f77 | 1 | 894 |
| 0x6146…e554 | 1 | 220 |
revenue = price − fee, folded from every sale
What is X right now? One account's balance. One listing by id. It can't sort, total, join or give you a feed — and the data your app needs isn't even in storage. It lives in events and write sets, because keeping totals on-chain costs gas on every transaction.
So every team writes an indexer: a processor, a database, a server, a deploy pipeline. A week of work, and something to maintain forever. Nineveh is that week, done.
No processor to write, no migrations, no schema to keep in step. Change a rule and Nineveh rebuilds the table from history in the background, then swaps it in — the old data keeps serving the whole time.
# nineveh.yaml — the whole backendsources:sold: { event: "0x…::market::Sold" }state:sellers:key: [seller]columns:seller: addresssold: { type: u64, default: 0 }revenue: { type: u64, default: 0 }reduce:- on: soldset:sold: "sold + 1"revenue: "revenue + price - fee"
GET /v1/tables/sellers?order=revenue.desc&limit=2{ "rows": [{ "seller": "0x7a3f…c41d","sold": "128","revenue": "94210" },{ "seller": "0x1e87…8d2a","sold": "91","revenue": "63880" }],"count": 4 }
Wide integers come back as strings, because a u128 doesn't fit a JavaScript number. Every table gets the same treatment, plus a live change feed and signed webhooks.
Nineveh sits on Aptos' transaction firehose. Every transaction arrives in order as it commits — no polling, no cron, nothing to schedule.
Each transaction carries its events and its write set — the exact storage slots it changed. That's why resources and tables work, not just events.
Records run through your rules. Everything from one transaction lands in a single database write, with a bookmark saying how far it got.
REST over every table, with filters, sorting and paging, plus a live change feed and signed webhooks.
A balance that goes 5 → 12 → 7 lands as 7, never as 12.
Rows and cursor commit together, or neither does.
A restart resumes from the cursor. Nothing counted twice.
Move's integers, checked. A bad rule halts; it never corrupts.
Studio builds the project, then shows you what it built: the tables, the changes arriving, and whether the whole thing is keeping up with the chain.

None of these need a contract change. The data is already on-chain; it simply isn't queryable.
Rank every player, live, from the events your game already emits.
players?order=wins.descWhat's for sale right now, and what each seller has made.
listings?order=price.ascEvery holder, sortable, with the history behind each balance.
holders?order=balance.descAppend-only history you can page through, by author or by time.
posts?author=0x7a3f…Volume, fees and exposure — totals the contract never stored.
daily_volume?order=day.descWhatever your contract emits, you can fold it into a table shaped like the question you actually ask.
One plan, no card. The limits are real numbers the backend enforces, not a trial that expires — when you reach one, it tells you which and what it means.
No billing yet, so nothing to buy.
Backfills, cursors, retries, crash recovery — Nineveh's problem, not yours.
Nineveh reads the contract's modules off the chain.
Events, resources and tables become tables of your own.
REST, a change feed and webhooks, seconds later.