Sales
GET/api/sales
Every sale of tracked sats the index found, on every marketplace, oldest first, as compact columns (gzip when accepted). Follow new sales with after=<seq>. Addresses are left out: ask /api/sales/parties. Cacheable 30 s. With years, month or luck, only the sales that pass (the filter echoed as filter); 501 on a server without the filters, 503 (retry: true) while the mint days or the luck are still being read, 429 (retry: true, with Retry-After) past your network's budget of new filtered answers (a cached one is always served). Sales still in the mempool come apart, in pending (newest first, marked pending: true with first_seen and fee_rate); they never enter the columns, stats or markets.
Query parameters #
| Parameter | Type | Required | Description |
|---|---|---|---|
row | string | no | Only sales of this row. |
group | string | no | Only sales of this group's rows (e.g. rarity). Ignored with row. |
after | integer | no | Only sales after this sequence number (an answer's seq). A cursor at most 5,000 sales behind the newest is answered as asked; one further back is answered whole (after: 0 in the answer: replace what you hold rather than append), and one past the newest as the newest. Default: 0. (min 0) |
sub | string | no | With row: 1 adds its rarer sub-categories' sales, flagged 4. One of: 0, 1. Default: 0. |
led | string | no | With row or group: 1 adds the sales led by another row that sold sats of these rows (their secondary rows), flagged 8, outside every market and median. One of: 0, 1. Default: 0. |
years | string | no | Only the sales of the row's sats mined in these years (comma-separated YYYY; year too): a sale passes when a sat of the row (of any row, without row) it sold was mined in one of them, as /api/sales/query and the collection's Activity match it. The Statistics' and a row detail's sales under a year filter. Years 2009 to this one (400 otherwise). |
month | string | no | Only the sales of each row's sats mined in this month (UTC, YYYY-MM, by each block's own time), matched on the dashboard: any rare-sat index. |
blocks | string | no | Only the sales of each row's sats mined in these blocks: heights from-to, inclusive (1000-5000), or one block (9), up to the tip (400 past it or for a malformed or reversed range), matched on the dashboard: any rare-sat index. |
luck | string | no | Only the sales of the row's sats mined in blocks of these lucks: N (N or more) or N-M, matched as years is. With years, both. |
Example request #
curl 'https://raresats.net/api/sales?row=uncommon&years=2009&month=2017-12&blocks=1000-5000&luck=10'const response = await fetch('https://raresats.net/api/sales?row=uncommon&years=2009&month=2017-12&blocks=1000-5000&luck=10');
const data = await response.json();import requests
data = requests.get("https://raresats.net/api/sales", params={"row": "uncommon", "years": "2009", "month": "2017-12", "blocks": "1000-5000", "luck": "10"}).json()Response 200 #
| Field | Type | Description |
|---|---|---|
available | boolean | Whether the sales index is available. |
generation | integer | Sales generation: a reorg starts a new one; reload from after=0. |
seq | integer | The newest sale's sequence number: pass it as after to get only newer sales. |
after | integer | The after asked for. |
count | integer | Sales in this answer (the length of every column). |
as_of | object | null | {height, time} of the sales. |
rows | array of string | Row ids, indexed by r, o and w. |
markets | array of string | Marketplace ids, indexed by m. |
sub_count | integer | With one market row: sales of its rarer sub-categories. |
sub_rows | map of integer | Those sales by their own row. |
led_count | integer | With one market row: sales led by another row that sold sats of it (it is one of their secondary rows). |
led_rows | map of integer | Those sales by their own row. |
t | array of integer | Sale time, unix seconds, as deltas: the first from 0, each next from the one before. |
q | array of integer | Sequence number, as deltas: the first from after. |
u | array of number | null | Unit price under the row shown. |
p | array of integer | null | Price in sats. |
c | array of integer | Sats of the row sold. |
r | array of integer | Index into rows: the row the sale is shown as. |
m | array of integer | Index into markets. |
f | array of integer | Flags: 1 inscribed, 2 outlier, 4 a rarer sub-category's sale (with sub=1), 8 a sale led by another row, shown under one of its secondary rows (with led=1), 16 the sale has secondary rows (an item in w). |
md | array of number | null | The row's rolling median unit price at the sale (4 significant digits). |
o | array of integer | null | With sub=1 or led=1: index into rows of a sub-category's or a led sale's own row, else null. |
s | array of integer | null | The first special sat sold. |
sh | array of integer | null | The block that mined it. |
sd | array of integer | null | The day it was mined (UTC days since 1970). |
n | array of integer | Tracked sats sold. |
x | array of string | null | The txid's first 8 and last 4 hex digits (/api/sales/{seq}/tx redirects to the full one). |
w | array of array of integer | One item per sale flagged 16, in their order: its secondary rows, primary-first, as [row index, sats, row index, sats, …] (indexes into rows). |
k | array of integer | The exact types each sale sold: an index into kinds. |
kinds | array of array of integer | The kinds of sale k names, each [type set index, sats, type set index, sats, …] (indexes into type_sets): the sats it sold grouped by exact type combination, its own row's combination first, then rarest first. A piece longer than one sat (a block 9 chunk) counts under its first sat's types. [] when the fork typed none. |
type_sets | array of array of string | The exact type combinations kinds names, each in the badges' order (["uncommon", "alpha"]). |
error | string | null | The sales index's last error, if any. |
pending | array of object | The selection's sales still in the mempool, newest first (the same row, group, sub and led rules). Never in the columns, count, seq or any market: they confirm as new sales. |
pending[].seq | null | No sequence number until it confirms. |
pending[].t | integer | null | first_seen: sorted by time, it leads the confirmed sales. |
pending[].h | null | No block yet. |
pending[].row | string | The sale's own row. |
pending[].u | number | null | Unit price: premium per sat of the row. |
pending[].c | integer | Sats of the row sold. |
pending[].p | integer | null | Price in sats. |
pending[].n | integer | Tracked sats the sale sold. |
pending[].s | integer | null | The first special sat sold. |
pending[].sh | integer | null | The block that mined it. |
pending[].sd | integer | null | The day it was mined (UTC days since 1970). |
pending[].m | string | Marketplace id (magisat, magic_eden, unknown, …). |
pending[].x | string | null | The txid's first 8 and last 4 hex digits. |
pending[].ins | boolean | An inscription was on a sold sat. |
pending[].out | boolean | Always false: a pending sale is judged by no median. |
pending[].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). |
pending[].seller | string | null | The seller's address. |
pending[].buyer | string | null | The buyer's address. |
pending[].key | string | txid:vin: its identity. The confirmed sale has the same txid and vin and replaces it. |
pending[].pending | true | Always true: the sale is still in the mempool. |
pending[].first_seen | integer | null | When the transaction was first seen in the mempool (bitcoind's entry time), unix seconds. |
pending[].fee_rate | number | null | Its fee rate, sat/vB (one decimal). |
pending[].txid | string | The sale transaction, whole. |
pending[].vin | integer | null | The seller's input. |
pending[].seller_outpoint | string | null | The output it sells. |
pending[].buyer_outpoint | string | null | The output the sold sats go to. |
pending[].rows | array of string | Its own row, then the rows of which it is a rarer sub-category. |
pending[].led_rows | array of string | The rows of its other sats (it is led by row there). |
pending[].labels | array of string | The label rows it lies in. |
pending[].kind | string | null | The fork's sale kind (listing, offer, …). |
Errors #
{error} (a message, no code):
| Status | When |
|---|---|
| 400 | A malformed after, sub, years or luck. |
| 404 | An unknown row or group. A retired row (uncommon_2009, rare_2009) answers 404 too, its retired field naming the row and years to ask for instead. |
| 429 | Too many new filtered answers (each a pass over every sale) from your network (an IPv4 address or IPv6 /64), or from everyone at once: retry: true; try again after Retry-After seconds. Answers already built are served all the same. |
| 501 | years or luck asked of a server without the sat filters. |
| 503 | The mint days or the block luck are still being read (retry: true), or the luck is not available. |
Reading the columns #
The answer is column-oriented: entry i of every column is sale i, oldest first. Decode it like this:
const data = await (await fetch('https://raresats.net/api/sales?row=uncommon')).json();
let time = 0;
let seq = data.after;
let w = 0; // the next item of `w`
const pairs = list => Object.fromEntries(list.flatMap((v, j) => (j % 2 ? [] : [[data.rows[v], list[j + 1]]])));
const kind = list => list.flatMap((v, j) => (j % 2 ? [] : [{ types: data.type_sets[v], sats: list[j + 1] }]));
const sales = data.t.map((dt, i) => {
time += dt;
seq += data.q[i];
return {
seq,
time, // unix seconds
row: data.rows[data.r[i]], // the row it is shown as
unit_price: data.u[i], // premium per sat of that row
price: data.p[i], // sats
count: data.c[i], // sats of the row sold
marketplace: data.markets[data.m[i]],
inscribed: (data.f[i] & 1) !== 0,
outlier: (data.f[i] & 2) !== 0,
sub_category: (data.f[i] & 4) !== 0,
led: (data.f[i] & 8) !== 0, // led by another row (with led=1)
median: data.md[i], // the row's rolling median at the time
first_sat: data.s[i],
mined_height: data.sh[i],
sats_sold: data.n[i],
txid_short: data.x[i], // first 8 + last 4 hex digits
// The rows of the other sats it sold, primary-first ({black_omega: 1}).
secondary: (data.f[i] & 16) ? pairs(data.w[w++]) : {},
// The sats it sold by exact type combination ([{types: ['uncommon', 'alpha'], sats: 1}]).
types: kind(data.kinds[data.k[i]])
};
});- Follow new sales: keep
seqand ask?after=<seq>(samerow/group) on the next block; append what comes back. Ifgenerationchanges (a reorg), reload fromafter=0. A cursor more than 5,000 sales behind the newest is answered whole: the answer'safteris then0, and replaces what you hold. One past the newest is answered as the newest (nothing new). - Secondary rows: a bundle is shown as its primary row (the primary-sat rule); a sale flagged 16 sold sats of other rows too, named by its item in
w. Withled=1, a row's view also lists the sales led by another row that it is a secondary row of, flagged 8 (with their own row ino), never in its median or market. - Exact types: a row's sales are not all alike (an alpha uncommon sold with a plain uncommon is an Uncommon sale).
knames the type combinations each sale sold, its own row's first, through the smallkindsandtype_setstables. - Addresses are not in the columns: ask Sale parties for the rows you show.
- The full txid is a redirect away:
/api/sales/{seq}/tx(to the transaction page;{txid, href}withaccept: application/json). - A sale's price is the marketplace's: on Magisat the listing price; elsewhere the seller's payment plus attributable marketplace fees. Its unit price is the premium (price less the sold output's value) per sat of its row.
To filter by mint year, mint day, birthday (month-day) or a rarer sub-category, newest first and paged, use Query sales; see Filtering & birthday sats.
Pending sales #
pending lists the selection's sales still in the mempool, newest first, under the same row, group, sub and led rules. Each has the market overview's sale fields (row, u, p, n, s, secondary, seller, buyer, …), placed among the rows exactly as a confirmed sale, plus key (txid:vin), the whole txid, vin, first_seen and fee_rate; seq and h are null. They are never in the columns, count or seq, and never in a median, volume or stat: once mined, the same sale (same txid and vin) arrives in the columns and the pending one is gone. See Pending.