Submit a listing
POST/api/market/listings
Step 3 of listing: the PSBT from POST /api/market/listings/prepare, every input signed SIGHASH_SINGLE|ANYONECANPAY (0x83). Everything is checked again against the chain and every signature verified before anything is stored. A new listing of an outpoint already listed replaces the old one (whose signature is deleted).
Request body #
content-type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
psbt | string | yes | The listing PSBT, every input signed with SIGHASH_SINGLE|ANYONECANPAY (0x83). Base64. Partially signed or finalized inputs are both accepted. |
Example request #
curl -X POST 'https://raresats.net/api/market/listings' \
-H 'content-type: application/json' \
-d '{
"psbt": "cHNidP8BAF4CAAAAAWroye3nyLiwpcNzDMmz2aV449rKtugMlDF04UqR1gmLAAAAAAD9////AfhvAQAAAAAAIlEgqC8plE1luGrmteXMdeKU6tbFk5Gh7cXgFuNJjGf8e7sAAAAAAAEBK4gYAAAAAAAAIlEgqC8plE1luGrmteXMdeKU6tbFk5Gh7cXgFuNJjGf8e7sBAwSDAAAAARNBTRvH6V6M7UvHRHSt9rxehNgXWjFx339UPLBrpakZUHtJWYdSEjJcKWGz0mMAzAntUr45lEpONbwi2QRK4vGr6IMBFyCD3+haMVHSUXKQ2kYf4oFVke9p8rGKLOY/AWl6izExRQAA"
}'const response = await fetch('https://raresats.net/api/market/listings', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
"psbt": "cHNidP8BAF4CAAAAAWroye3nyLiwpcNzDMmz2aV449rKtugMlDF04UqR1gmLAAAAAAD9////AfhvAQAAAAAAIlEgqC8plE1luGrmteXMdeKU6tbFk5Gh7cXgFuNJjGf8e7sAAAAAAAEBK4gYAAAAAAAAIlEgqC8plE1luGrmteXMdeKU6tbFk5Gh7cXgFuNJjGf8e7sBAwSDAAAAARNBTRvH6V6M7UvHRHSt9rxehNgXWjFx339UPLBrpakZUHtJWYdSEjJcKWGz0mMAzAntUr45lEpONbwi2QRK4vGr6IMBFyCD3+haMVHSUXKQ2kYf4oFVke9p8rGKLOY/AWl6izExRQAA"
})
});
const data = await response.json();import requests
data = requests.post("https://raresats.net/api/market/listings", json={
"psbt": "cHNidP8BAF4CAAAAAWroye3nyLiwpcNzDMmz2aV449rKtugMlDF04UqR1gmLAAAAAAD9////AfhvAQAAAAAAIlEgqC8plE1luGrmteXMdeKU6tbFk5Gh7cXgFuNJjGf8e7sAAAAAAAEBK4gYAAAAAAAAIlEgqC8plE1luGrmteXMdeKU6tbFk5Gh7cXgFuNJjGf8e7sBAwSDAAAAARNBTRvH6V6M7UvHRHSt9rxehNgXWjFx339UPLBrpakZUHtJWYdSEjJcKWGz0mMAzAntUr45lEpONbwi2QRK4vGr6IMBFyCD3+haMVHSUXKQ2kYf4oFVke9p8rGKLOY/AWl6izExRQAA"
}).json()Response 200 #
| Field | Type | Description |
|---|---|---|
listings | array of Listing | The new listings, active, in input order. |
warning | string | Present when a listing replaced here had a public signature (see exposed). |
exposed | array of string | Ids of replaced listings whose signature a broadcast purchase made public: their old price can still be taken until the UTXO moves. |
{
"listings": [
{
"id": "3324d08569806818",
"outpoint": "8b09d6914ae17431940ce8b6cadae378a5d9b3c90c73c3a5b0b8c8e7edc9e86a:0",
"value": 6280,
"price": 94200,
"unit_price": 14,
"row": "block9",
"count": 6280,
"rows": {"block9": 6280, "block9_1st_tx": 6280},
"types": [
{
"types": ["block9", "nakamoto", "vintage"],
"count": 6280,
"sats": [45100023043, 45100023044, 45100023045]
}
],
"sats": 6280,
"ranges": [[45100023043, 45100029322]],
"years": {"2009": 6280},
"seller_address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
"payout_address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
"status": "active",
"created_at": 1790952985,
"updated_at": 1790952985,
"txid": null,
"signature_public": false,
"sold_via": null,
"offer_id": null,
"best_offer": null
}
]
}Errors #
{error, code}; see Errors for every code.
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_items | items is not a list of 1-50 UTXOs (or the PSBT lists more), or an item's sats is not null or a list of sat numbers. |
| 400 | bad_json | The body is not valid JSON. |
| 400 | bad_price | A price is not a whole number of sats. |
| 400 | bad_psbt | Not a base64 PSBT, or one that does not parse. |
| 400 | bad_request | The body is not a JSON object, or the engine refused a malformed request (e.g. a purchase submitted without its signed setup PSBT). |
| 400, 422 | bad_signature | 400: signature is missing or too long. 422: a signature does not verify (cancel or registration message, listing or purchase input). |
| 400 | duplicate_outpoint | The same UTXO twice in one listing. |
| 403 | cross_origin | A 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. |
| 405 | method_not_allowed | Wrong method for the route; the allow header lists the right ones. |
| 409 | changed | The 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. |
| 409 | pending | The UTXO's listing is being bought, or an offer of it accepted, right now. |
| 409 | spent | The UTXO (or a listing's UTXO) is spent or does not exist. |
| 409 | unconfirmed | The UTXO is not confirmed yet. |
| 409 | unindexed | The ord indexer does not know the UTXO yet. |
| 409 | utxo_mismatch | The PSBT's amount or script of a UTXO is not the chain's. |
| 413 | too_large | The body is over 256 KiB. |
| 415 | not_json | A POST without content-type: application/json. |
| 422 | bad_payout | A listed input is paired with an output that pays no address, or the payout address is the marketplace's fee address. |
| 422 | bad_sequence | A listed input is not signed with sequence 0xfffffffd. |
| 422 | bad_sighash | A signature uses the wrong sighash type (listings: 0x83; purchases: DEFAULT or ALL). |
| 422 | bad_structure | The listing PSBT is not the expected layout (pairing, version 2, locktime 0, one seller, sequences), or a purchase would pay the fee address. |
| 422 | inscribed | The UTXO carries an inscription: only rare sats are listed; the wallet tools only send it whole. |
| 422 | legacy_address | A legacy (P2PKH, 1…) address: use Taproot or SegWit. |
| 422 | missing_signature | An input that must be signed is not. |
| 422 | no_row | The UTXO holds no sat of a market row. |
| 422 | not_owner | The UTXO does not belong to the seller address, or (wallet tools) to the owner's addresses, or the transfer was made by another wallet. |
| 422 | price_too_high | The price is above 21,000,000 BTC. |
| 422 | price_too_low | The price is at most the UTXO's own value, below 546 sats, or below the payout output's dust limit. |
| 422 | runes | The UTXO carries runes (the wallet tools only send it whole). |
| 422 | too_many_items | Too many UTXOs in a listing, or listings in a purchase (max_bulk); for a wallet transfer, too many rare sats (and the runs between them) to lay out in one UTXO: choose fewer. |
| 422 | unsupported_address | An address type the marketplace does not trade (only Taproot, native SegWit and nested SegWit). |
| 422 | unsupported_script | The trade engine met a script type it does not support. |
| 422 | untracked | The UTXO holds no rare sat the index tracks (send it whole instead). |
| 429 | rate_limited | Over 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. |
| 503 | bitcoind_unavailable | bitcoind is not available. |
| 503 | engine_unavailable | The trade engine is not available. |
| 503 | fork_behind | The rare-sat index has not caught up with bitcoind: try again in a minute. |
| 503 | fork_unavailable | The rare-sat index is not available. |
| 503 | indexer_behind | The ord indexer is catching up: try again shortly. |
| 503 | market_unavailable | The marketplace is closed right now (its backends disagree on the network, or are unreachable). |
| 503 | oracle_unavailable | The ord oracle is not available. |
{
"error": "Listing signature (input 0): the signature does not verify",
"code": "bad_signature"
}- Every rule of prepare is checked again, and each signature is verified against the chain's amount and script of its UTXO: Schnorr over the BIP341 sighash for Taproot (65 bytes, ending in
0x83), low-S ECDSA over the BIP143 sighash for SegWit v0 (DER, ending in0x83). - Listing a UTXO that is already listed replaces the old listing: it becomes
cancelledand its signature is deleted. That is how a price is changed. warningandexposedappear when a replaced listing's signature was already public (see Cancel listings).