raresats.net API
Wallet tools API View as Markdown

Submit a transfer

POST/api/market/transfer/submit

Step 2: the PSBT from prepare with every input signed. The server checks that it is exactly the prepared transaction, verifies every signature, rechecks every UTXO against bitcoind, the rare-sat index and the ord indexer (changed, spent), runs the invariants with the real size and testmempoolaccept, cancels the active listings of the moved UTXOs (their signatures are deleted, so they can never sell) and broadcasts. Idempotent: the same body again answers the same txid.

Request body #

content-type: application/json

FieldTypeRequiredDescription
idstringyesFrom prepare.
psbtstringyesThe PSBT from prepare with every input signed, base64 (finalized or not).

Example request #

cURL
curl -X POST 'https://raresats.net/api/market/transfer/submit' \
  -H 'content-type: application/json' \
  -d '{
  "id": "6baec47455bf8df8",
  "psbt": "<the PSBT from prepare, every input signed, base64>"
}'
JavaScript
const response = await fetch('https://raresats.net/api/market/transfer/submit', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    "id": "6baec47455bf8df8",
    "psbt": "<the PSBT from prepare, every input signed, base64>"
  })
});
const data = await response.json();
Python
import requests

data = requests.post("https://raresats.net/api/market/transfer/submit", json={
    "id": "6baec47455bf8df8",
    "psbt": "<the PSBT from prepare, every input signed, base64>"
}).json()

Response 200 #

FieldTypeDescription
txidstringThe broadcast transaction.
transferobjectA wallet transfer: what is on chain or was chosen by its owner.
transfer.idstringThe prepared transfer's id.
transfer.txidstringIts transaction id (what /tx/{txid} shows).
transfer.kindstringOne of: extract, send, send_n, chop, consolidate, multisend, speedup, cancel.
transfer.statusstringpending in the mempool; confirmed in a block; replaced by a speed-up or cancel built here (replaced_by); conflicted: a block spent its inputs and it was provably not mined (its own outputs never appeared within six blocks); dropped: in no mempool and its inputs unspent (it may still resurface until voided); unknown: the broadcast's outcome is not known yet; rejected: bitcoind refused it, so it was never sent and cannot resurface.
transfer.created_atintegerUnix seconds.
transfer.updated_atintegerUnix seconds.
transfer.ownerobjectThe owner's addresses (one transfer's read).
transfer.owner.payment_addressstringPaid the fees.
transfer.owner.ordinals_addressstringThe ordinals address.
transfer.owner_rolestringThe queried address's part in it (a list read). One of: payment, ordinals, both.
transfer.destinationstring | nullWhere the moved sats went (null for a multisend).
transfer.fee_ratenumbersat/vB.
transfer.network_feeintegerSats.
transfer.tools_feeintegerThe flat raresats.net fee it paid, in sats.
transfer.vsizeintegerIts size in vbytes.
transfer.replacesstring | nullThe transfer it replaced (speed-up, cancel).
transfer.replaced_bystring | nullThe transaction that replaced it.
transfer.block_heightinteger | nullThe block it confirmed in.
transfer.outputsarray of objectIts outputs.
transfer.outputs[].indexintegervout.
transfer.outputs[].addressstring | nullWho it pays.
transfer.outputs[].valueintegerSats.
transfer.outputs[].rolestringdest, return, plain_return, tools_fee or change.
transfer.actionsobjectWhat its owner can do now.
transfer.actions.speedupbooleanIt is pending: a speed-up can replace it.
transfer.actions.cancelbooleanIt could still confirm (pending, unknown, dropped) and its chain of replacements pays someone else: a cancel (or a void) sends its sources back. It never hides a cancel the prepare would allow; the prepare decides with the mempool (409 nothing_to_cancel).
Response
{
  "txid": "a1edadb9e3b45097d272621ea8adcbee697281bd4d611462db134292d4e1cf9c",
  "transfer": {
    "id": "6baec47455bf8df8",
    "txid": "a1edadb9e3b45097d272621ea8adcbee697281bd4d611462db134292d4e1cf9c",
    "kind": "extract",
    "status": "pending",
    "created_at": 1791428236,
    "updated_at": 1791428236,
    "owner": {
      "payment_address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
      "ordinals_address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr"
    },
    "destination": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
    "fee_rate": 3,
    "network_fee": 1059,
    "tools_fee": 1000,
    "vsize": 352,
    "replaces": null,
    "replaced_by": null,
    "block_height": null,
    "outputs": [
      {
        "index": 0,
        "address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
        "value": 388,
        "role": "return"
      },
      {
        "index": 1,
        "address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
        "value": 330,
        "role": "dest"
      },
      {
        "index": 2,
        "address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
        "value": 9282,
        "role": "plain_return"
      },
      {
        "index": 3,
        "address": "bc1p4hefchfnn06cn2xmk6fq6xcw5ew260q2dptc4c35fcazgedmzlcqkxf057",
        "value": 1000,
        "role": "tools_fee"
      },
      {
        "index": 4,
        "address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
        "value": 22941,
        "role": "change"
      }
    ],
    "actions": {"speedup": true, "cancel": false}
  }
}

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
400bad_idid is not a prepared purchase or transfer id.
400bad_jsonThe body is not valid JSON.
400bad_psbtNot a base64 PSBT, or one that does not parse.
400bad_requestThe body is not a JSON object, or the engine refused a malformed request (e.g. a purchase submitted without its signed setup PSBT).
400, 422bad_signature400: signature is missing or too long. 422: a signature does not verify (cancel or registration message, listing or purchase input).
403cross_originA POST from a browser on another site (a foreign Origin, or sec-fetch-site: cross-site). Server-side clients send no Origin and are not affected.
404unknown_transferNo such transfer: an unknown or forgotten prepared id, or a txid the wallet tools did not build.
405method_not_allowedWrong method for the route; the allow header lists the right ones.
409changedThe UTXO's value is not what the PSBT says, or (wallet tools) a UTXO, its sats or the transaction being replaced changed since prepare: prepare again.
409config_changedThe marketplace settings changed since this purchase or transfer was prepared (prepare it again), or since this offer was made (it is now invalid, reason config_changed).
409conflictA listing or one of your UTXOs was spent meanwhile (lost race), or bitcoind would not replace a transaction already spending it (insufficient fee), or a transfer's inputs are spent by another transaction.
409expiredThe prepared purchase, offer or transfer expired, or the offer itself has: prepare it again.
409has_descendantsA transaction spends the transfer's outputs already: it can no longer be replaced.
409not_pendingThe transfer to speed up or cancel is not waiting in the mempool any more (confirmed, replaced).
409offer_overlapSince the prepare, a planned input started funding one of your offers, or a UTXO the server picked got an offer: prepare the transfer again.
409pendingThe UTXO's listing is being bought, or an offer of it accepted, right now.
409spentThe UTXO (or a listing's UTXO) is spent or does not exist.
413too_largeThe body is over 256 KiB.
415not_jsonA POST without content-type: application/json.
422fee_rate_too_lowfee_rate is below the current minimum (GET /api/market/fees).
422fee_too_highThe network fee would exceed the per-transaction cap (200,000 sats): lower the fee rate or move fewer.
422missing_signatureAn input that must be signed is not.
422psbt_mismatchThe signed PSBT is not the prepared transaction.
422rejectedbitcoind refused the transaction (reason).
422replacement_fee_too_lowThe replacement pays too little to replace the pending transaction (BIP 125): raise the fee rate to at least min_fee_rate.
422unsupported_scriptThe trade engine met a script type it does not support.
429rate_limitedOver 30 POSTs a minute from your IPv4 address or IPv6 /64 (in chat, counted per IPv4 address or IPv6 /64: 30 writes and 20 login nonces a minute, 20 sign-ins and 10 display-name changes an hour; one message every 3 s and 20 a minute per address); wait retry-after seconds. With the AI moderation on, at most 6 messages and names a minute are checked per address and per IPv4 address or IPv6 /48.
500internalA server bug. Report it.
500invariant_violatedA planned or finalized purchase or transfer broke an invariant (violations); nothing was broadcast.
503bitcoind_unavailablebitcoind is not available.
503broadcast_unknownThe broadcast's outcome is unknown: submit the same purchase or transfer again to settle it. Carries txid.
503engine_unavailableThe trade engine is not available.
503fork_behindThe rare-sat index has not caught up with bitcoind: try again in a minute.
503fork_unavailableThe rare-sat index is not available.
503market_unavailableThe marketplace is closed right now (its backends disagree on the network, or are unreachable).
503oracle_unavailableThe ord oracle is not available.
Error (404)
{"error": "no such transfer", "code": "unknown_transfer"}

Submitting is idempotent: the same {id, psbt} again answers the same txid. On 503 broadcast_unknown (or a lost answer), submit the same body again until it answers {txid} or a definite refusal: when bitcoind still does not have the transaction, the server checks it again and sends the same transaction once more (same txid, so nothing is sent twice). Once the transfer has settled, a resubmit answers definitely: {txid} when it confirmed, 409 conflict (with txid, status and replaced_by) when it was replaced or its inputs were spent by another transaction. Offers are checked again just before the broadcast: a planned input that started funding one of your offers since the prepare (unless you chose it knowing, after the spends_offer_funding warning), or a UTXO the server picked that got an offer, answers 409 offer_overlap: prepare again. Your listings on the moved UTXOs are cancelled just before the broadcast, so they can never sell after you signed a move of them.

Follow the transfer with Get a wallet transfer; speed it up or cancel it with Speed up or cancel a transfer.