Supply, rows and status
The dashboard's own feeds: what its Statistics, Transfers and Extractions views show. Briefly documented; their shapes follow the rare-sat index and may grow.
Supply #
GET/api/supply
The latest supply of every row (supply: {height, hash, time, rows}, each row with total, stats and active_non_extracted), the row definitions (rows) and their groups (groups). A row the site shows as a sub-category of another although its types do not say so names that row in sub_of: the milestone alphas (kilosat_alpha, decasat_alpha, centasat_alpha, megasat_alpha), labelled Kilosat, Decasat, Centasat and Megasat uncommon, are sub-categories of uncommon, and the milestone omegas (kilosat_omega, decasat_omega, centasat_omega, megasat_omega), labelled Kilosat, Decasat, Centasat and Megasat black, of black. With years, every row's supply over the sats mined in those years (rows with none have zeros), and years echoed; with luck, over the sats mined in blocks of those lucks, and luck echoed ({min, max}, max null for "or more"); with month, over the sats mined in that month, and month echoed.
Query parameters #
| Parameter | Type | Required | Description |
|---|---|---|---|
years | string | no | Only the sats mined in these years: comma-separated years (YYYY) blocks were mined in, at most 64 (year is accepted too). Counters are the years' sums, total and mined their sats mined so far, holders and extracted_holders the addresses holding sats of any of them (exact). Needs a rare-sat index with a year breakdown (fork.status.year_stats in /api/status), else 501. |
luck | string | no | Only the sats mined in blocks of these lucks: N (luck N or more) or N-M, from 0 to the index's highest luck (31, which also counts any luckier block). A block's luck is the leading zero bits of its hash beyond its target's. Counters are the lucks' sums, holders and extracted_holders the addresses holding sats of any of them (exact). Not with years (400): the supply is broken down by one or the other. Needs a rare-sat index with a luck breakdown (fork.status.luck_stats in /api/status), else 501. |
month | string | no | Only the sats mined in this month (UTC, YYYY-MM), one a block was mined in (months is accepted too). Counters, holders and extracted_holders are the month's own (exact). Not with years or luck (400): the supply is broken down by one of them. Needs a rare-sat index with a month breakdown (fork.status.month_stats in /api/status, index schema 7), else 501. |
Example request #
curl 'https://raresats.net/api/supply?years=2009,2010&luck=10&month=2017-12'const response = await fetch('https://raresats.net/api/supply?years=2009,2010&luck=10&month=2017-12');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/supply", params={"years": "2009,2010", "luck": "10", "month": "2017-12"}).json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 400 | Malformed years, a year no block was mined in, a malformed luck or one past the index's highest, a malformed month or one no block was mined in, or more than one of years, a luck and a month. |
| 501 | The rare-sat index has no year (luck, month) breakdown (years, luck or month asked of an older index). |
| 502 | The index answered with an error. |
| 503 | The index is unavailable. |
By mint year #
Every number of the supply can be narrowed to the sats mined in some years: ?years=2009 on /api/supply and /api/rows/{id}, or several years (?years=2009,2010). The counters of a set of years add up, but for the holders, which count each address once however many of the years it holds sats of. ?years=2009 gives the same numbers the 2009 rows did: uncommon_2009 and rare_2009 are retired, and /api/rows/uncommon?years=2009 is what /api/rows/uncommon_2009 was (the latter now answers 404 with a retired field naming the row and years to ask for, as does every route taking row=; /collection/uncommon_2009 redirects to /collection/uncommon?year=2009). Every year's numbers at once:
GET/api/supply/years
Every row's supply split by the year its sats were mined: {height, hash, time, years: [{year, from_height, to_height}], rows: [{id, years: {"2009": {total, stats, active_non_extracted}}}]}, a row listing only the years it has sats of. What the year chips count; cached per block. Needs a rare-sat index with a year breakdown (fork.status.year_stats in /api/status), else 501.
Example request #
curl 'https://raresats.net/api/supply/years'const response = await fetch('https://raresats.net/api/supply/years');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/supply/years").json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 501 | The rare-sat index has no year breakdown. |
| 502 | The index answered with an error. |
| 503 | The index is unavailable. |
By block luck #
The same numbers can be narrowed to the sats mined in lucky blocks instead: ?luck=10 (blocks of luck 10 or more) or ?luck=10-12 on /api/supply and /api/rows/{id}. A block's luck is the leading zero bits of its hash beyond its target's: most blocks are luck 0 to 3, about one in eight hundred is 10 or more. The holders count each address once, as for years; a luck lists no top holders. Years and a luck do not combine in the supply (a 400): the index breaks it down by one or the other. Every luck's numbers at once, with the blocks of each luck:
GET/api/supply/lucks
Every row's supply split by the luck of the blocks its sats were mined in: {height, hash, time, lucks: [{luck, blocks}], rows: [{id, lucks: {"12": {total, stats, active_non_extracted}}}]}, a row listing only the lucks it has sats of (lucks: the blocks of each luck). What the luck selects count; cached per block. Needs a rare-sat index with a luck breakdown (fork.status.luck_stats in /api/status), else 501.
Example request #
curl 'https://raresats.net/api/supply/lucks'const response = await fetch('https://raresats.net/api/supply/lucks');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/supply/lucks").json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 501 | The rare-sat index has no luck breakdown. |
| 502 | The index answered with an error. |
| 503 | The index is unavailable. |
By mint month #
Or to the sats mined in one month (UTC): ?month=2017-12 on /api/supply and /api/rows/{id}. A month is one at a time, with its own holders (exact) and top holders; it does not combine with years or a luck (a 400). A month is counted as its year is, by the running latest block time, so a year's months add up to it; five blocks of mainnet (272,376 to 272,380) whose own time is 30 November 2013 count in December 2013, where the chain's time already was. Every month's numbers at once, compactly (an array of counters per month, in the order fields names them):
GET/api/supply/months
Every row's supply split by the month (UTC) its sats were mined in, compactly: {height, hash, time, months: [{month, from_height, to_height}], fields: ["mined", "located", "burned", "lost", "overwritten", "extracted", "holders", "extracted_holders", "inscribed"], rows: [{id, months: {"2017-12": [counts in the order of fields]}}]}, a row listing only the months it has sats of. What a collection's month select counts; cached per block. Needs a rare-sat index with a month breakdown (fork.status.month_stats in /api/status), else 501.
Example request #
curl 'https://raresats.net/api/supply/months'const response = await fetch('https://raresats.net/api/supply/months');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/supply/months").json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 501 | The rare-sat index has no month breakdown. |
| 502 | The index answered with an error. |
| 503 | The index is unavailable. |
The sales beside these numbers (the Statistics' Last, 30-day median and volume, and a row's sales chart) follow the same filter: /api/sales/stats?years=2009 and /api/sales?row=alpha_uncommon&years=2009 count only the sales of the row's sats mined in 2009, the very sales the collection's Activity lists with ?year=2009 (see Sales stats). So does a month (?month=2017-12), on any index: the sales are matched on this side, by each block's own day.
Sat type explainers #
The text of the site's /types pages: what each sat type, category and concept is, with its categories' live numbers.
GET/api/types
Every explainer page of /types (one per sat type, category and concept), by section: sections ([{id, label, pages: [{slug, title, summary, href}]}]), and which page explains each row (rows: {rowId: slug}) and each sat type (badges: {type: slug}). The text comes from the site's own pages; the page is /types/{slug}.
Example request #
curl 'https://raresats.net/api/types'const response = await fetch('https://raresats.net/api/types');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/types").json()Response 200 #
A JSON object.
{
"sections": [
{
"id": "rarity",
"label": "Rarity tiers",
"pages": [
{
"slug": "uncommon",
"title": "Uncommon",
"summary": "The first sat of a block: the lowest rarity tier above common in ordinal theory, one per block bar the few whose first sat ranks higher.",
"href": "/types/uncommon"
}
]
}
],
"rows": {"uncommon": "uncommon"},
"badges": {"uncommon": "uncommon"}
}Errors #
None of its own.
GET/api/types/{slug}
One explainer: slug, title, summary, kind and kind_label (its section), badges (the sat types it explains), body (its text as a tree of blocks: {t: 'h3'|'h4', id, c}, {t: 'p', c}, {t: 'ul'|'ol', items: [c]}, where c lists inlines: a string, {t: 'a', href, c, ext?}, {t: 'b'|'i', c}, {t: 'code', text} or {t: 'n', row, field}, a live number found in numbers[row][field]), rows (the categories it explains, each with total, mined, extracted, holders, inscribed and market: last_unit_price, median_30d, sales, and while the marketplace is on floor_unit and listed), also_in (other categories holding its types), related pages and as_of (the block the numbers are of).
Path parameters #
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | yes | The page's slug, as /api/types lists them. |
Example request #
curl 'https://raresats.net/api/types/uncommon'const response = await fetch('https://raresats.net/api/types/uncommon');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/types/uncommon").json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 404 | No such page. |
A row #
GET/api/rows/{id}
One row: row (its supply stats, extracted and non-extracted top holders, label, group and definition) and history (extracted and located counts over time). With years, over the sats mined in those years (years echoed): one year lists its own top holders (at most 100 per list), a set of years none (null), and the history's holder counts are null for a set. With luck, over the sats mined in blocks of those lucks (luck echoed): no top holders (null), and the history's holder counts are null but for a single luck or every luck. With month, over the sats mined in that month (month echoed): its own top holders (at most 100 per list) and holder counts in its history.
Path parameters #
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | The row id. |
Query parameters #
| Parameter | Type | Required | Description |
|---|---|---|---|
range | string | no | History range. One of: all, 4y, 1y, 90d, 30d. Default: all. |
top | integer | no | Holders per list, 1-1000. Default: 100. (min 1, max 1000) |
years | string | no | Only the sats mined in these years: comma-separated years (YYYY) blocks were mined in, at most 64 (year is accepted too). Counters are the years' sums, total and mined their sats mined so far, holders and extracted_holders the addresses holding sats of any of them (exact). Needs a rare-sat index with a year breakdown (fork.status.year_stats in /api/status), else 501. |
luck | string | no | Only the sats mined in blocks of these lucks: N (luck N or more) or N-M, from 0 to the index's highest luck (31, which also counts any luckier block). A block's luck is the leading zero bits of its hash beyond its target's. Counters are the lucks' sums, holders and extracted_holders the addresses holding sats of any of them (exact). Not with years (400): the supply is broken down by one or the other. Needs a rare-sat index with a luck breakdown (fork.status.luck_stats in /api/status), else 501. |
month | string | no | Only the sats mined in this month (UTC, YYYY-MM), one a block was mined in. Not with years or luck (400). Needs a rare-sat index with a month breakdown (fork.status.month_stats), else 501. |
Example request #
curl 'https://raresats.net/api/rows/uncommon?years=2009&luck=10&month=2017-12'const response = await fetch('https://raresats.net/api/rows/uncommon?years=2009&luck=10&month=2017-12');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/rows/uncommon", params={"years": "2009", "luck": "10", "month": "2017-12"}).json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 400 | A malformed parameter, or both years and a luck, a malformed month or one no block was mined in, or more than one of years, a luck and a month. |
| 404 | No such row. A retired row (uncommon_2009, rare_2009) answers 404 too, its retired field naming the row and years to ask for instead. |
| 501 | The rare-sat index has no year (luck, month) breakdown. |
| 503 | The index is unavailable. |
Notable transfers #
The first page of both feeds (no before, offset 0) also has pending: the notable events of transactions still in the mempool, judged by the same rules as if mined in the next block, outside total (Pending).
GET/api/transfers
Notable transfers of rare sats, newest first: {total, offset, items}. Page with before (an event id) or offset. The first page (no before, offset 0) also has pending: the notable transfers of transactions still in the mempool, newest first, outside total.
Query parameters #
| Parameter | Type | Required | Description |
|---|---|---|---|
before | string | no | An event id: the events after it. |
offset | integer | no | Events to skip. Default: 0. (min 0) |
limit | integer | no | Events, 1-1000. Default: 200. (min 1, max 1000) |
Example request #
curl 'https://raresats.net/api/transfers'const response = await fetch('https://raresats.net/api/transfers');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/transfers").json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 400 | A malformed parameter. |
| 404 | before is no longer held: reload. |
Notable extractions #
GET/api/extractions
Notable extractions (rare sats newly isolated), newest first, paged like transfers. The first page also has pending, as transfers.
Query parameters #
| Parameter | Type | Required | Description |
|---|---|---|---|
before | string | no | An event id: the events after it. |
offset | integer | no | Events to skip. Default: 0. (min 0) |
limit | integer | no | Events, 1-1000. Default: 200. (min 1, max 1000) |
Example request #
curl 'https://raresats.net/api/extractions'const response = await fetch('https://raresats.net/api/extractions');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/extractions").json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 400 | A malformed parameter. |
| 404 | before is no longer held: reload. |
Pending (the mempool) #
GET/api/pending
Every pending sale, notable transfer and extraction of transactions still in the mempool, the live listings being spent, and the transactions that left the mempool lately, from the index's mempool overlay (polled every 5 s). Pending items are marked pending: true with first_seen and fee_rate, and count in no stat. A pending sale is txid:vin, a move its txid: the confirmed record with the same key replaces it. The SSE mempool event says when to read it again. available is false against an index without the overlay.
Example request #
curl 'https://raresats.net/api/pending'const response = await fetch('https://raresats.net/api/pending');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/pending").json()Response 200 #
| Field | Type | Description |
|---|---|---|
available | boolean | Whether the index serves the mempool overlay and it has been read. |
seq | integer | null | The overlay's change counter: it moves with every change. |
height | integer | null | The index tip the overlay is consistent with; pending items are judged as of the next block. |
txs | integer | Pending transactions that move tracked sats. |
sales | array of object | Pending sales, newest first. |
sales[].seq | null | No sequence number until it confirms. |
sales[].t | integer | null | first_seen: sorted by time, it leads the confirmed sales. |
sales[].h | null | No block yet. |
sales[].row | string | The sale's own row. |
sales[].u | number | null | Unit price: premium per sat of the row. |
sales[].c | integer | Sats of the row sold. |
sales[].p | integer | null | Price in sats. |
sales[].n | integer | Tracked sats the sale sold. |
sales[].s | integer | null | The first special sat sold. |
sales[].sh | integer | null | The block that mined it. |
sales[].sd | integer | null | The day it was mined (UTC days since 1970). |
sales[].m | string | Marketplace id (magisat, magic_eden, unknown, …). |
sales[].x | string | null | The txid's first 8 and last 4 hex digits. |
sales[].ins | boolean | An inscription was on a sold sat. |
sales[].out | boolean | Always false: a pending sale is judged by no median. |
sales[].secondary | map of integer | A sale's secondary rows: the market rows of the other sats it sold (not its own row, a row it is a sub-category of, or a label row), each with its sats, primary-first: {"black_omega": 1} for a black omega sold with an alpha uncommon; {} for none. A sale never counts in its secondary rows' markets (the primary-sat rule, in Concepts). |
sales[].seller | string | null | The seller's address. |
sales[].buyer | string | null | The buyer's address. |
sales[].key | string | txid:vin: its identity. The confirmed sale has the same txid and vin and replaces it. |
sales[].pending | true | Always true: the sale is still in the mempool. |
sales[].first_seen | integer | null | When the transaction was first seen in the mempool (bitcoind's entry time), unix seconds. |
sales[].fee_rate | number | null | Its fee rate, sat/vB (one decimal). |
sales[].txid | string | The sale transaction, whole. |
sales[].vin | integer | null | The seller's input. |
sales[].seller_outpoint | string | null | The output it sells. |
sales[].buyer_outpoint | string | null | The output the sold sats go to. |
sales[].rows | array of string | Its own row, then the rows of which it is a rarer sub-category. |
sales[].led_rows | array of string | The rows of its other sats (it is led by row there). |
sales[].labels | array of string | The label rows it lies in. |
sales[].kind | string | null | The fork's sale kind (listing, offer, …). |
transfers | array of object | Pending notable transfers, newest first. |
transfers[].id | string | pending:transfer:<txid> or pending:extraction:<txid>. |
transfers[].kind | string | One of: transfer, extraction. |
transfers[].txid | string | |
transfers[].pending | true | |
transfers[].first_seen | integer | null | When the transaction was first seen in the mempool (bitcoind's entry time), unix seconds. |
transfers[].fee_rate | number | null | Its fee rate, sat/vB (one decimal). |
transfers[].height | null | |
transfers[].time | integer | null | first_seen. |
transfers[].reasons | array of object | Why it is notable. |
extractions | array of object | Pending notable extractions, newest first. |
extractions[].id | string | pending:transfer:<txid> or pending:extraction:<txid>. |
extractions[].kind | string | One of: transfer, extraction. |
extractions[].txid | string | |
extractions[].pending | true | |
extractions[].first_seen | integer | null | When the transaction was first seen in the mempool (bitcoind's entry time), unix seconds. |
extractions[].fee_rate | number | null | Its fee rate, sat/vB (one decimal). |
extractions[].height | null | |
extractions[].time | integer | null | first_seen. |
extractions[].reasons | array of object | Why it is notable. |
listings | map of PendingSpend | Live marketplace listings whose UTXO a pending transaction spends, by outpoint: they show as pending. |
removed | array of object | Transactions that left the mempool lately (the live event: those since the last one). |
removed[].txid | string | |
removed[].status | string | confirmed: in a block (its confirmed items replace the pending ones); replaced (RBF, replaced_by set), conflicted (a block spent an input otherwise) or evicted: its pending items are dropped. |
removed[].height | integer | null | The block, when confirmed. |
removed[].replaced_by | string | null | The replacement, when replaced. |
removed[].at | integer | null | When it left, unix seconds. |
Errors #
None of its own.
The index watches the mempool (its overlay, polled every 5 s and right after each block): a transaction still waiting there that moves tracked sats is shown wherever confirmed activity is, marked pending: true with first_seen (unix seconds) and fee_rate (sat/vB). The site shows it with a Pending chip.
- Identity. A pending sale is
txid:vin(itskey), a pending transfer or extraction itstxid(id: pending:<kind>:<txid>). When the transaction confirms, the confirmed record with the same key replaces the pending one at once; nothing is listed twice. - Leaving.
removednames the transactions that left the mempool:confirmed(now in a block),replaced(RBF;replaced_by),conflicted(a block spent an input otherwise) orevicted. Their pending items are gone from the lists. - Never counted. Pending sales are in no stat, median, volume, floor, leaderboard or supply until they confirm.
- Where else.
pendingin Sales and the market overview; the first page of/api/transfersand/api/extractions; the first page of Address activity;spent_by_pendingandpending_outputson Address;pendingon Sat;spent_inandpendingon UTXO;spent_by_pendingon the listing of a sat or UTXO page. - Live. The SSE stream sends
mempoolwhenever what is pending changes: an invalidation,{version, keys: {sales, transfers, extractions, rows, addresses, outpoints}, counts}, naming what changed (the rows, addresses and outpoints of the transactions added, removed or changed), never the items. Read this route, or the answer you show, again when a key you care about is set. - Against an index without the mempool overlay,
availableis false and every list is empty.
Everything at once #
GET/api/state
Everything the site shows at once: thresholds, limits, fork, feed, rows, groups, supply, sales, and the newest transfers and extractions. pending is everything pending in the mempool (/api/pending).
Query parameters #
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | no | Events per list, 1-1000. Default: 500. (min 1, max 1000) |
Example request #
curl 'https://raresats.net/api/state'const response = await fetch('https://raresats.net/api/state');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/state").json()Response 200 #
A JSON object.
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 400 | A malformed limit. |
Live events #
Every change the site shows (blocks, sales, listings, offers, floors, activity, prices, chat) comes down one Server-Sent Events stream: see Live events.
Status #
GET/api/status
The rare-sat index's reachability, mode and progress (fork) and the dashboard's tip (feed).
Example request #
curl 'https://raresats.net/api/status'const response = await fetch('https://raresats.net/api/status');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/status").json()Response 200 #
A JSON object.
Errors #
None of its own.
Health #
GET/healthz
{ok, fork, mode, height, tip}: fork is up, stale or down.
Example request #
curl 'https://raresats.net/healthz'const response = await fetch('https://raresats.net/healthz');
const data = await response.json();import requests
data = requests.get("https://raresats.net/healthz").json()Response 200 #
| Field | Type | Description |
|---|---|---|
ok | boolean | Always true when the dashboard answers. |
fork | string | The index. One of: up, stale, down. |
mode | string | null | The index's mode. |
height | integer | null | The index's height. |
tip | integer | null | The dashboard's tip. |
{"ok": true, "fork": "up", "mode": "live", "height": 969001, "tip": 969001}Errors #
None of its own.