raresats.net API
Trading API View as Markdown

Submit a purchase

POST/api/market/purchase/submit

Step 3 of buying: the PSBTs from prepare, your inputs signed. The server checks they are exactly the prepared transactions, verifies your signatures, adds the sellers' signatures, rechecks every rule, then testmempoolaccept and broadcasts (setup first). The listings become pending. Resubmitting the same purchase is idempotent: use it to settle broadcast_unknown.

Request body #

content-type: application/json

FieldTypeRequiredDescription
idstringyesThe prepared purchase id.
setup_psbtstring | nullnoThe signed setup PSBT, base64: required when prepare returned setup, absent otherwise.
purchase_psbtstringyesThe signed purchase PSBT, base64. Not finalized is fine; the transaction must be exactly the prepared one.

Example request #

cURL
curl -X POST 'https://raresats.net/api/market/purchase/submit' \
  -H 'content-type: application/json' \
  -d '{
  "id": "5c9e5c410688cd85",
  "purchase_psbt": "cHNidP8BAP3lAQIAAAAEV/MhdPD/0sIHo8hX35p9GQud7geCW9jXa3hoAejEjgoAAAAAAP3///+3Sw6TNUSithUu7/zVH0UQDemfGc8HWcjxkuB/9nd1LAAAAAAA/f///6nTU92uYg6gtb+FEVnT5JxNFJ1H13OjJCln4+xg+7YvAAAAAAD9////VHDf42dMx3y1IrYZcBnC/YiD1iNsIqK5yRDtpRMrGUYAAAAAAP3///8JsAQAAAAAAAAXqRQ/tulYEuV7tGkfmkpiiGKmGk92m4ciAgAAAAAAACJRIKYIafDbzx3GWcnOy6+AUBNeqejNxIcFPx3GiAlJ3GhMguwAAAAAAAAWABTAzrzWw9PKjHXcXsYuvlUzDvkQ4vUCAAAAAAAAIlEgrfKcXTOb9Ymo27aSDRsOplytPApoV4riNE46JGW7F/D1AgAAAAAAACJRIKgvKZRNZbhq5rXlzHXilOrWxZORoe3F4BbjSYxn/Hu7WAIAAAAAAAAXqRQ/tulYEuV7tGkfmkpiiGKmGk92m4dYAgAAAAAAABepFD+26VgS5Xu0aR+aSmKIYqYaT3abh3hIAQAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHAAAAAAAAAAAOagxyYXJlc2F0cy5uZXQAAAAAAAEBIFgCAAAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHIgIDmztpS4/FteB/sGnHg8rHVPXTjD4IvtGWDjH9sd2jXCRIMEUCIQDqT5JFB8nk2LvYxjpKXk87G12Tn0Jt7fslQf2gSIvJjwIgbg+BAy20UrXd2niDIeZbPLkgyz+NcnSSkChVICw0gV4BAQQWABT5kGeayv4lwnYVNztAvyJEbST/RAABASBYAgAAAAAAABepFD+26VgS5Xu0aR+aSmKIYqYaT3abhyICA5s7aUuPxbXgf7Bpx4PKx1T104w+CL7Rlg4x/bHdo1wkRzBEAiAbu6SfmqbPok5pJyyLQoLTWtu6xHYFUxdkVqMdge1hPwIgXVp1PF1DvIaRLu2QeT0Qv9t+5Dfe2BUaY/Y2QBv58+ABAQQWABT5kGeayv4lwnYVNztAvyJEbST/RAABAR8iAgAAAAAAABYAFMDOvNbD08qMddxexi6+VTMO+RDiAAEBIPBJAgAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHIgIDmztpS4/FteB/sGnHg8rHVPXTjD4IvtGWDjH9sd2jXCRHMEQCIC8ykJLr2bz7gpup8hfRujOnTV4UdPVuIsfzFgJGmMuCAiARSDj1rK63S1wPEKMrRPqnZ7u+rJDysVPRn7ym3iNFpQEBBBYAFPmQZ5rK/iXCdhU3O0C/IkRtJP9EAAAAAAAAAAAAAA=="
}'
JavaScript
const response = await fetch('https://raresats.net/api/market/purchase/submit', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    "id": "5c9e5c410688cd85",
    "purchase_psbt": "cHNidP8BAP3lAQIAAAAEV/MhdPD/0sIHo8hX35p9GQud7geCW9jXa3hoAejEjgoAAAAAAP3///+3Sw6TNUSithUu7/zVH0UQDemfGc8HWcjxkuB/9nd1LAAAAAAA/f///6nTU92uYg6gtb+FEVnT5JxNFJ1H13OjJCln4+xg+7YvAAAAAAD9////VHDf42dMx3y1IrYZcBnC/YiD1iNsIqK5yRDtpRMrGUYAAAAAAP3///8JsAQAAAAAAAAXqRQ/tulYEuV7tGkfmkpiiGKmGk92m4ciAgAAAAAAACJRIKYIafDbzx3GWcnOy6+AUBNeqejNxIcFPx3GiAlJ3GhMguwAAAAAAAAWABTAzrzWw9PKjHXcXsYuvlUzDvkQ4vUCAAAAAAAAIlEgrfKcXTOb9Ymo27aSDRsOplytPApoV4riNE46JGW7F/D1AgAAAAAAACJRIKgvKZRNZbhq5rXlzHXilOrWxZORoe3F4BbjSYxn/Hu7WAIAAAAAAAAXqRQ/tulYEuV7tGkfmkpiiGKmGk92m4dYAgAAAAAAABepFD+26VgS5Xu0aR+aSmKIYqYaT3abh3hIAQAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHAAAAAAAAAAAOagxyYXJlc2F0cy5uZXQAAAAAAAEBIFgCAAAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHIgIDmztpS4/FteB/sGnHg8rHVPXTjD4IvtGWDjH9sd2jXCRIMEUCIQDqT5JFB8nk2LvYxjpKXk87G12Tn0Jt7fslQf2gSIvJjwIgbg+BAy20UrXd2niDIeZbPLkgyz+NcnSSkChVICw0gV4BAQQWABT5kGeayv4lwnYVNztAvyJEbST/RAABASBYAgAAAAAAABepFD+26VgS5Xu0aR+aSmKIYqYaT3abhyICA5s7aUuPxbXgf7Bpx4PKx1T104w+CL7Rlg4x/bHdo1wkRzBEAiAbu6SfmqbPok5pJyyLQoLTWtu6xHYFUxdkVqMdge1hPwIgXVp1PF1DvIaRLu2QeT0Qv9t+5Dfe2BUaY/Y2QBv58+ABAQQWABT5kGeayv4lwnYVNztAvyJEbST/RAABAR8iAgAAAAAAABYAFMDOvNbD08qMddxexi6+VTMO+RDiAAEBIPBJAgAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHIgIDmztpS4/FteB/sGnHg8rHVPXTjD4IvtGWDjH9sd2jXCRHMEQCIC8ykJLr2bz7gpup8hfRujOnTV4UdPVuIsfzFgJGmMuCAiARSDj1rK63S1wPEKMrRPqnZ7u+rJDysVPRn7ym3iNFpQEBBBYAFPmQZ5rK/iXCdhU3O0C/IkRtJP9EAAAAAAAAAAAAAA=="
  })
});
const data = await response.json();
Python
import requests

data = requests.post("https://raresats.net/api/market/purchase/submit", json={
    "id": "5c9e5c410688cd85",
    "purchase_psbt": "cHNidP8BAP3lAQIAAAAEV/MhdPD/0sIHo8hX35p9GQud7geCW9jXa3hoAejEjgoAAAAAAP3///+3Sw6TNUSithUu7/zVH0UQDemfGc8HWcjxkuB/9nd1LAAAAAAA/f///6nTU92uYg6gtb+FEVnT5JxNFJ1H13OjJCln4+xg+7YvAAAAAAD9////VHDf42dMx3y1IrYZcBnC/YiD1iNsIqK5yRDtpRMrGUYAAAAAAP3///8JsAQAAAAAAAAXqRQ/tulYEuV7tGkfmkpiiGKmGk92m4ciAgAAAAAAACJRIKYIafDbzx3GWcnOy6+AUBNeqejNxIcFPx3GiAlJ3GhMguwAAAAAAAAWABTAzrzWw9PKjHXcXsYuvlUzDvkQ4vUCAAAAAAAAIlEgrfKcXTOb9Ymo27aSDRsOplytPApoV4riNE46JGW7F/D1AgAAAAAAACJRIKgvKZRNZbhq5rXlzHXilOrWxZORoe3F4BbjSYxn/Hu7WAIAAAAAAAAXqRQ/tulYEuV7tGkfmkpiiGKmGk92m4dYAgAAAAAAABepFD+26VgS5Xu0aR+aSmKIYqYaT3abh3hIAQAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHAAAAAAAAAAAOagxyYXJlc2F0cy5uZXQAAAAAAAEBIFgCAAAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHIgIDmztpS4/FteB/sGnHg8rHVPXTjD4IvtGWDjH9sd2jXCRIMEUCIQDqT5JFB8nk2LvYxjpKXk87G12Tn0Jt7fslQf2gSIvJjwIgbg+BAy20UrXd2niDIeZbPLkgyz+NcnSSkChVICw0gV4BAQQWABT5kGeayv4lwnYVNztAvyJEbST/RAABASBYAgAAAAAAABepFD+26VgS5Xu0aR+aSmKIYqYaT3abhyICA5s7aUuPxbXgf7Bpx4PKx1T104w+CL7Rlg4x/bHdo1wkRzBEAiAbu6SfmqbPok5pJyyLQoLTWtu6xHYFUxdkVqMdge1hPwIgXVp1PF1DvIaRLu2QeT0Qv9t+5Dfe2BUaY/Y2QBv58+ABAQQWABT5kGeayv4lwnYVNztAvyJEbST/RAABAR8iAgAAAAAAABYAFMDOvNbD08qMddxexi6+VTMO+RDiAAEBIPBJAgAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHIgIDmztpS4/FteB/sGnHg8rHVPXTjD4IvtGWDjH9sd2jXCRHMEQCIC8ykJLr2bz7gpup8hfRujOnTV4UdPVuIsfzFgJGmMuCAiARSDj1rK63S1wPEKMrRPqnZ7u+rJDysVPRn7ym3iNFpQEBBBYAFPmQZ5rK/iXCdhU3O0C/IkRtJP9EAAAAAAAAAAAAAA=="
}).json()

Response 200 #

FieldTypeDescription
txidstringThe purchase transaction, accepted by bitcoind and broadcast.
setup_txidstringThe setup transaction (broadcast first), when there was one.
Response
{
  "txid": "6897451c42f02de2594abfdd6a5d4fcf4794a62cbbd9fbec1ec983893ddc5df4"
}

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_purchaseNo such prepared purchase (it was dropped: your 11th prepared since, a busy server, or a restart): prepare it again.
405method_not_allowedWrong method for the route; the allow header lists the right ones.
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.
409listing_changedA listing's stored terms no longer match the plan: prepare it again.
409not_activeA listing (or offer) is not active: being bought (or accepted), sold, cancelled, moved, expired or invalid. Carries id and status.
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.
422bad_sighashA signature uses the wrong sighash type (listings: 0x83; purchases: DEFAULT or ALL).
422missing_signatureAn input that must be signed is not.
422psbt_mismatchThe signed PSBT is not the prepared transaction.
422rejectedbitcoind refused the transaction (reason).
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.
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_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 (409)
{
  "error": "the prepared purchase expired: prepare it again",
  "code": "expired"
}

When the answer is lost #

A submit that times out, or answers 503 broadcast_unknown, may still have gone through. Submit the same body again: it is idempotent. If bitcoind has the transaction, the answer is the same {txid}; if it was refused, you get that error; while it cannot be known yet, broadcast_unknown again. Meanwhile the listings stay reserved (pending) for you, and return to sale if the transaction never reaches the mempool.