# 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

```bash title="cURL"
curl 'https://raresats.net/api/sales?row=uncommon&years=2009&month=2017-12&blocks=1000-5000&luck=10'
```
```js title="JavaScript"
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();
```
```python title="Python"
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:

```js title="Decode"
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 `seq` and ask `?after=<seq>` (same `row`/`group`) on the next block; append what comes back. If `generation` changes (a reorg), reload from `after=0`. A cursor more than 5,000 sales behind the newest is answered whole: the answer's `after` is then `0`, 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](https://raresats.net/docs/concepts.md#rows-collections)); a sale flagged 16 sold sats of other rows too, named by its item in `w`. With `led=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 in `o`), 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). `k` names the type combinations each sale sold, its own row's first, through the small `kinds` and `type_sets` tables.
- **Addresses** are not in the columns: ask [Sale parties](https://raresats.net/docs/get-sales-parties.md) for the rows you show.
- **The full txid** is a redirect away: `/api/sales/{seq}/tx` (to the [transaction page](https://raresats.net/docs/get-tx.md); `{txid, href}` with `accept: 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](https://raresats.net/docs/get-sales-query.md); see [Filtering & birthday sats](https://raresats.net/docs/guide-filters.md).

## 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](https://raresats.net/docs/get-market.md)'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](https://raresats.net/docs/site-feeds.md#pending-the-mempool).
