# Extract rare sats

**Extracting** moves the rare sats of a UTXO into small outputs of their own, so each can be held, listed or sent alone (see [Extraction](https://raresats.net/types/extraction)). The wallet tools do it in one transaction you sign with your own wallet: the server plans and checks it, your wallet signs, the server verifies and broadcasts. The server never holds a key.

It is three steps: **prepare** (`kind: "extract"`), **sign** every input listed in `sign`, **submit**.

## Before you start

- Your wallet's **ordinals address** holds the rare sats; its **payment address** pays the fees. Xverse-style wallets have both; a single-address wallet (UniSat-style) uses one Taproot address for both. Taproot, native SegWit or nested SegWit payment addresses; not legacy.
- The fees come **only from plain bitcoin** of your payment address: confirmed UTXOs with no inscription, no runes and no rare sat the index tracks. No sat of the UTXOs you extract is ever paid as fee.
- Inscribed and rune UTXOs are never extracted (send them whole instead).
- Pick a fee rate from `GET /api/market/fees`.

## 1. Prepare

```json title="Request"
{
  "kind": "extract",
  "owner": {
    "payment_address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
    "payment_public_key": "039b3b694b8fc5b5e07fb069c783cac754f5d38c3e08bed1960e31fdb1dda35c24",
    "ordinals_address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
    "ordinals_public_key": "03cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115"
  },
  "items": [{ "outpoint": "2fb6…d3a9:0" }],
  "fee_rate": 3
}
```

- `items`: 1 to 50 UTXOs of yours. An item's `sats` (absolute sat numbers) picks some of its tracked sats; absent, every tracked sat is extracted ("extract all").
- `destination`: where the extracted outputs go; **your ordinals address** when absent. Unpicked tracked sats always go back to the address of the UTXO they came from.
- `plain_to`: where a slice holding no tracked sat goes: `"payment"` (default, your payment address) or `"source"`.

### The layout

Each picked sat (or each whole [block 9](https://raresats.net/types/block-9) range) gets an output of its own, at offset 0 where it can: **330 sats** of padding for a Taproot or native SegWit destination, **546** for a nested SegWit (P2SH) or legacy one. Sats too close together share an output (a `merged` warning, and `summary.layout[].merged`). The sats before, between and after them go back: to the UTXO's own address, or as plain slices to your payment address (`plain_to`). The padding and the fees come from your plain bitcoin.

With many items, the ones that cannot be extracted are skipped, not fatal: they are listed in `summary.skipped` with a reason (`already_extracted`, `split_chunk`, `inscribed`, `runes`, `unconfirmed`, `pending`, `untracked`, `nothing_to_separate`, `keep_in_destination`, `listed`, `offered`). A UTXO you are selling or that has offers is skipped by an "extract all"; name it in `items` yourself to move it (that cancels its listing).

### Split into chunks

A UTXO holding many sats of one type, say 200 uncommons, would need 200 outputs to separate each one, more than a transaction holds (100). **Split** cuts them into chunks instead, each chunk an output of its own:

```json title="Request"
{
  "kind": "extract",
  "owner": { "…": "as above" },
  "items": [{ "outpoint": "2fb6…d3a9:0" }],
  "split": { "type": "uncommon", "per_output": 10 },
  "fee_rate": 3
}
```

- `split.type`: the exact type combination to chunk (`uncommon`, `black+uncommon`; 1 to 8 lowercase types joined with `+`), matched as `send_n` matches it. The address API's `split_counts` says how many sats of each combination a UTXO can split. Every sat of that type counts, including each sat of a long tracked run of it (a run of `vintage` sats is that many sats). A whole range (`block9`, `450x`) is not split by sat: [chop](https://raresats.net/docs/prepare-transfer.md) it.
- `split.per_output`: sats of the type per output (1–2,000), in offset order; the last chunk holds the remainder (205 uncommons in tens: 20 chunks of 10 and one of 5). Or `split.chunks`: how many chunks (2–100), near-equal, the first ones one larger (200 in 7: 29, 29, 29, 29, 28, 28, 28). Exactly one of the two.
- `split.trim`: the sats of the type sit at scattered offsets, so each chunk is the span from its first sat of the type to its last. By default (`false`) the plain sats between two chunks stay with the earlier one: each chunk's output runs from its first sat to the next chunk, and other rare or exotic sats between two chunks ride along with it too when the destination is your own address (to another address, they stay home with the plain sats around them). With `true` the plain sats between chunks come back as plain change (to `plain_to`) when they make an output of their own (330 sats, 546 for a nested SegWit or legacy address); a smaller gap rides along with a chunk.
- A chunk worth more than about 10,000 sats per rare sat in it does not count as [extracted](https://raresats.net/types/extraction): the plan warns (`chunk_not_extracted`), and the site's dialog turns `trim` on once. An "extract all" over several UTXOs skips the chunks a split here made (skip code `split_chunk`), so it never undoes a split; name a chunk alone to extract it.
- The plain sats before the first chunk and after the last come back as for any extraction, and no tracked sat is ever in the fee.
- **Other rare sats** in the UTXO (another type, an exotic range, a whole block 9 range) are never cut and never sent to plain change: they stay home in an output back to the UTXO's address, or ride along with a chunk, which only your own address may receive (an external `destination` with one inside a chunk's span is refused with `keep_in_destination`). A tracked run that touches block 9 stays whole in an output of its own: its sats are never chunked. A whole range never rides along with a chunk: a chunk whose sats lie on both sides of one is cut around it into two outputs (a `chunk_cut` warning), and when the range sits too close to keep apart the split is refused (`keep_in_destination`).
- The items take no `sats`; with several items each UTXO is split on its own, and a UTXO that cannot make 2 chunks (too few sats of the type) is skipped (`summary.skipped`: `not_enough_sats` or `nothing_to_do`). At most 2,000 sats of the type per request, and per UTXO at most 2,000 of them and its other tracked runs and exotic intervals together (`too_many_items`); outputs past 100 (counted as laid out: chunks too close together share one) are refused before anything is built (`too_many_outputs`).
- Chunks too close together to separate (each output needs its padding) share an output: a `merged` warning, and `summary.layout[].chunk_outputs` below `chunks`. When they would all stay one output: `nothing_to_separate`. Fewer sats of the type than chunks: `not_enough_sats`. More chunks than outputs allow: `too_many_outputs`.

When the split sends back small outputs worth less than the fees, the plan warns (`fee_exceeds_freed`). The plan checks each chunk output against the FIFO flow of the transaction: it holds exactly its chunk's sats of the type, and no other output holds any ("Each output holds exactly the sats you asked for"), at prepare and again at submit. `summary.split` echoes the split as planned.

### The summary

Read it before you sign. Each output says what it holds and where it goes:

| `to` | Meaning |
| --- | --- |
| `ordinals` | Your ordinals address (the default destination). |
| `payment` | Your payment address (plain slices, change). |
| `source` | Back to the address the sats came from. |
| `external` | An address that is not yours. |
| `fee` | The raresats.net tools fee: one flat output. |

`checks` lists what the server verified on the unsigned transaction ("No rare sat is paid as fee", …); `network_fee + tools_fee = total_cost`.

## 2. Sign

Sign **every** input in `sign`, each with the key of its `address`, with your wallet's default sighash (SIGHASH_DEFAULT for Taproot, SIGHASH_ALL for SegWit v0). Don't finalize, don't broadcast.

- **Xverse:** `signPsbt` with `signInputs: {[ordinals address]: [...], [payment address]: [...]}` and `broadcast: false`: one prompt.
- **UniSat:** `signPsbt(hex, {autoFinalized: false, toSignInputs: [{index, address}]})`.

## 3. Submit

```json title="Request"
{ "id": "a3f0c2…", "psbt": "<the signed PSBT, base64>" }
```

The server checks that the signed transaction is exactly the prepared one, verifies every signature, rechecks the UTXOs against bitcoind, the rare-sat index and the ord indexer, runs `testmempoolaccept` and broadcasts. The answer is `{txid, transfer}`.

| Code | Status | What to do |
| --- | --- | --- |
| `changed`, `spent` | 409 | A UTXO moved or its sats changed since prepare: prepare again. |
| `expired` | 409 | Over 10 minutes: prepare again. |
| `config_changed` | 409 | The tools fee changed since prepare: prepare again and review the new summary. |
| `psbt_mismatch` | 422 | Your wallet changed the transaction: sign the PSBT as given. |
| `insufficient_funds` | 422 | Not enough plain bitcoin at your payment address. |
| `broadcast_unknown` | 503 | Submit the same body again; it answers `{txid}` once bitcoind has it. |

Then follow it with [Get a wallet transfer](https://raresats.net/docs/get-wallet-transfer.md), or [speed it up](https://raresats.net/docs/guide-speed-up.md).
