raresats.net API
Offers API View as Markdown

Prepare an offer

POST/api/market/offers/prepare

Step 1 of making an offer on a UTXO, listed or not: checks the UTXO (tracked sats, no inscription or rune, unspent and confirmed, not yours), picks plain cardinal UTXOs of your payment address and returns the offer transaction to sign: the seller's UTXO at input 0 (unsigned), your funding after it; the UTXO's sats to your ordinals address (output 0), price to the address that holds it (output 1), the platform fee (output 2), your change, the marker. The plan is held 10 minutes.

Request body #

content-type: application/json

FieldTypeRequiredDescription
outpointstringyesThe UTXO to bid on, txid:vout: any UTXO holding tracked sats, unspent and confirmed, with no inscription or rune, not your own. Listed or not.
priceintegeryesWhat the seller receives, in sats: above the UTXO's value, at least 546. You pay it plus the platform fee and the network fee.
bidderobjectyesThe bidder's wallet: Xverse-style wallets have two addresses, UniSat-style one (use it for both).
bidder.payment_addressstringyesFunds the offer and gets the change. Taproot, native SegWit or nested SegWit.
bidder.payment_public_keystringyesIts public key, hex (Taproot: the internal key, x-only or compressed; SegWit: compressed).
bidder.ordinals_addressstringyesReceives the UTXO. Taproot, native SegWit or nested SegWit. May equal payment_address.
bidder.ordinals_public_keystringyesIts public key, hex (Taproot: the internal key, x-only or compressed; SegWit: compressed). Required: the ordinals address can cancel the offer and lists it as made.
fee_ratenumberyesThe network fee rate in sat/vB (see GET /api/market/fees), fixed when you sign. (max 5000)
expires_ininteger | "never" | nullnoHow long it can be accepted, in seconds: 86400 (1 day), 259200 (3 days), 604800 (7 days, the default when absent) or 2592000 (30 days); null or "never" for no expiry.

Example request #

cURL
curl -X POST 'https://raresats.net/api/market/offers/prepare' \
  -H 'content-type: application/json' \
  -d '{
  "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0",
  "price": 110000,
  "bidder": {
    "payment_address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
    "payment_public_key": "0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145",
    "ordinals_address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
    "ordinals_public_key": "0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145"
  },
  "fee_rate": 3,
  "expires_in": 604800
}'
JavaScript
const response = await fetch('https://raresats.net/api/market/offers/prepare', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0",
    "price": 110000,
    "bidder": {
      "payment_address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
      "payment_public_key": "0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145",
      "ordinals_address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
      "ordinals_public_key": "0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145"
    },
    "fee_rate": 3,
    "expires_in": 604800
  })
});
const data = await response.json();
Python
import requests

data = requests.post("https://raresats.net/api/market/offers/prepare", json={
    "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0",
    "price": 110000,
    "bidder": {
        "payment_address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
        "payment_public_key": "0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145",
        "ordinals_address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
        "ordinals_public_key": "0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145"
    },
    "fee_rate": 3,
    "expires_in": 604800
}).json()

Response 200 #

FieldTypeDescription
idstringThe prepared offer's id: submit with it.
psbtstringThe whole offer transaction, base64. Input 0 is the seller's UTXO, unsigned (the seller signs it to accept).
signarray of objectYour inputs (1 and up): sign exactly these, with your wallet's default sighash.
sign[].indexintegerThe input to sign.
sign[].addressstringThe address whose key signs it.
sign[].sighashintegerThe sighash type to sign with, when it is not the default: 131 (0x83, SIGHASH_SINGLE|ANYONECANPAY) for listings. Absent on purchase inputs: sign with the default (SIGHASH_DEFAULT for Taproot, SIGHASH_ALL for SegWit v0).
summaryobjectWhat the offer costs and pays.
summary.outpointstringThe UTXO bid on.
summary.valueintegerIts value in sats.
summary.priceintegerWhat the seller receives.
summary.platform_feeinteger2.5% of price, at least 546 sats.
summary.network_feeintegerThe network fee in sats.
summary.totalintegerWhat you pay: price + platform_fee + network_fee.
summary.vsizeintegerEstimated virtual size.
summary.fee_ratenumbersat/vB.
summary.changeintegerYour change output in sats, 0 when none.
summary.unit_pricenumber(price - value) / count.
summary.rowstringThe UTXO's own row.
summary.countintegerSats of row in the UTXO.
expires_atinteger | nullWhen the offer will expire, unix seconds; null: no expiry.
submit_byintegerSubmit before this (unix seconds, 10 minutes from now), or prepare again.
Response
{
  "id": "dbf866a1e7e209e7",
  "psbt": "cHNidP8BAP0fAQIAAAACfxBUfmD8FgvLWC+rhhI7HaW1ncmmlqpbSg8h3N/usyQAAAAAAP3///9Bihr4H2G1iZuygx3BHzwLSWfyqpoJAv/W0tpPJYLJAQAAAAAA/f///wUQJwAAAAAAACJRIKgvKZRNZbhq5rXlzHXilOrWxZORoe3F4BbjSYxn/Hu7sK0BAAAAAAAiUSCmCGnw288dxlnJzsuvgFATXqnozcSHBT8dxogJSdxoTL4KAAAAAAAAIlEgrfKcXTOb9Ymo27aSDRsOplytPApoV4riNE46JGW7F/AvJxUAAAAAACJRIKgvKZRNZbhq5rXlzHXilOrWxZORoe3F4BbjSYxn/Hu7AAAAAAAAAAAOagxyYXJlc2F0cy5uZXQAAAAAAAEBKxAnAAAAAAAAIlEgpghp8NvPHcZZyc7Lr4BQE16p6M3EhwU/HcaICUncaEwAAQErYOMWAAAAAAAiUSCoLymUTWW4aua15cx14pTq1sWTkaHtxeAW40mMZ/x7uwEXIIPf6FoxUdJRcpDaRh/igVWR72nysYos5j8BaXqLMTFFAAAAAAAA",
  "sign": [
    {
      "index": 1,
      "address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh"
    }
  ],
  "summary": {
    "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0",
    "value": 10000,
    "price": 110000,
    "platform_fee": 2750,
    "network_fee": 963,
    "total": 113713,
    "vsize": 321,
    "fee_rate": 3,
    "change": 1386287,
    "unit_price": 100000,
    "row": "decasat_alpha",
    "count": 1
  },
  "expires_at": 1791571812,
  "submit_by": 1790967612
}

Errors #

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

StatusCodeMeaning
400bad_addressAn address is not a mainnet address (or not one at all).
400bad_bidderThe offer's bidder is missing, not an object, or lacks ordinals_public_key.
400bad_expiryexpires_in is not 86400, 259200, 604800 or 2592000 (seconds), null or "never".
400bad_fee_ratefee_rate is not a positive number up to 5000.
400bad_jsonThe body is not valid JSON.
400bad_outpointNot an outpoint (txid:vout).
400bad_priceA price is not a whole number of sats.
400bad_public_keyA public key is not hex, or not a valid secp256k1 key of the right size.
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).
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.
405method_not_allowedWrong method for the route; the allow header lists the right ones.
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.
409unconfirmedThe UTXO is not confirmed yet.
409unindexedThe ord indexer does not know the UTXO yet.
413too_largeThe body is over 256 KiB.
415not_jsonA POST without content-type: application/json.
422bad_structureThe listing PSBT is not the expected layout (pairing, version 2, locktime 0, one seller, sequences), or a purchase would pay the fee address.
422dust_outputA bought UTXO is below the dust limit of your ordinals address type: use a Taproot or native SegWit ordinals address.
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.
422inscribedThe UTXO carries an inscription: only rare sats are listed; the wallet tools only send it whole.
422insufficient_fundsThe payment address's plain UTXOs cannot pay prices, fee, padding and network fee (or a transfer's fees). Carries available, prices, platform_fee.
422key_mismatchThe public key does not control the address.
422legacy_addressA legacy (P2PKH, 1…) address: use Taproot or SegWit.
422no_rowThe UTXO holds no sat of a market row.
422own_utxoThe UTXO is held by one of the bidder's own addresses.
422price_too_highThe price is above 21,000,000 BTC.
422price_too_lowThe price is at most the UTXO's own value, below 546 sats, or below the payout output's dust limit.
422pubkey_mismatchThe trade engine found the public key is not the address's key.
422runesThe UTXO carries runes (the wallet tools only send it whole).
422unsupported_addressAn address type the marketplace does not trade (only Taproot, native SegWit and nested SegWit).
422unsupported_scriptThe trade engine met a script type it does not support.
422untrackedThe UTXO holds no rare sat the index tracks (send it whole instead).
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.
500invariant_violatedA planned or finalized purchase or transfer broke an invariant (violations); nothing was broadcast.
503bitcoind_unavailablebitcoind is not available.
503busyToo many purchases, offers or transfers are being prepared: try again in a minute. Each client (IPv4 address or IPv6 /64) keeps its 10 latest prepared purchases, offers and transfers; an older one answers unknown_purchase / unknown_offer / unknown_transfer.
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.
503indexer_behindThe ord indexer is catching up: try again shortly.
503market_unavailableThe marketplace is closed right now (its backends disagree on the network, or are unreachable).
503oracle_unavailableThe ord oracle is not available.
Error (422)
{
  "error": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0 is held by your own address: make offers on other people's UTXOs",
  "code": "own_utxo",
  "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0"
}

Then sign the PSBT, exactly the inputs in sign, and submit before submit_by. The whole flow: Make, accept and cancel offers.

Expiry #

expires_in is 1, 3, 7 or 30 days in seconds (7 days when absent), or null / "never" for an offer with no expiry. The server stops accepting an offer once it expires and deletes your signatures. One with no expiry can be accepted at its price at any time until you cancel it, it is accepted, or it becomes invalid (the UTXO moves or your funding is spent). Its fee rate is fixed now: the seller can speed up a slow confirmation by spending their output 1 (CPFP).