Allways Access / Developersv1Get your API key
Endpoints

POST/v1/exchanges

Creates and locks an exchange. One credit is spent at creation; see Credits.

Request

POST /v1/exchanges with a JSON body. Needs x-api-key.

NameRequiredRule
from, toyesSupported currency ids
fromAmountyesDecimal string within current bounds, both ends inclusive
fromAddressyesThe exact address of the wallet that will send, copied from the wallet tool (case-sensitive); refunds may land here. A deposit from any other address is unrecoverable
toAddressyesValid, non-burn address for to; make sure it can receive the asset
slippagenoPercent, 0 to 5; default 0.5. See Notes
minToAmountnoAbsolute net floor; overrides slippage
Requestcurl
curl -X POST "$API/v1/exchanges" \
  -H "x-api-key: $KEY" \
  -H "content-type: application/json" \
  -d '{
    "from": "sol",
    "to": "tao",
    "fromAmount": "0.5",
    "fromAddress": "9s4hVRU5nfPF9Er9Y5FexDjxNtvZSYmnVEujxHCX5Gsb",
    "toAddress": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
  }'

Response

201 with the exchange object. toAmount is final. The example below is abbreviated; every field is described on GET /v1/exchanges/{id}.

FieldMeaning
idUse it to poll and to report your deposit
statuspending at creation
toAmountWhat lands in your wallet, net of fee; does not move after the 201
depositAddress, validUntilNull here; filled when status becomes awaiting_deposit, so poll to get them
Responsejson
{
  "id": "5d1f3c2e-8a1b-4c7d-9e0f-123456789abc",
  "status": "pending",
  "from": "sol",
  "to": "tao",
  "fromAmount": "0.5",
  "toAmount": "3.912",
  "rate": "7.824",
  "depositAddress": null,
  "validUntil": null,
  "createdAt": 1756900000
}

Errors

HTTPCodeExtra fieldsWhen
400validationfieldA body field is missing or malformed, or an address is a few characters away from one this account used before (a typo)
402insufficient_balancebalance, cost, depositAddressBalance is zero; top up at depositAddress
403geo_blocked—Request from a blocked region
403sanctioned—An address failed sanctions screening
409seat_takenretryAfterNo rate inside your band right now, or fromAddress already holds a live exchange on that rate; retry after retryAfter seconds
409rate_below_floortoAmount, floorThe best net amount is under your minToAmount
422amount_too_lowminFromAmount, maxFromAmountfromAmount is under the minimum
422amount_too_highminFromAmount, maxFromAmountfromAmount is over the maximum
422no_liquidityminFromAmount, maxFromAmountNo liquidity for this pair right now
422reserve_rejectedreasonThe rate could not be reserved; reason says why
429rate_limitedretryAfterOver the limit; see Rate limits

Notes

  • toAmount is fixed at creation. slippage only decides which rates we may accept: the best rate available within this band of the current best. None available: 409 seat_taken (retry) or, with minToAmount, 409 rate_below_floor.
  • Poll every 3 seconds until awaiting_deposit; this usually takes a few seconds, at most about 30.
  • One credit is spent only on a 201; a request that fails with a 4xx spends nothing.
  • Subnet token source (from=sn<N>): depositAddress is a coldkey. Send one transfer_stake, not a transfer: origin netuid = destination netuid = N, exactly fromAmount, from one hotkey that holds all of it, signed by the fromAddress coldkey. No batch, no proxy, no --all. Anything else is not credited and the exchange lapses.
    • btcli: btcli stake transfer --wallet-name <wallet> --hotkey <hotkey holding fromAmount> --origin-netuid N --dest-netuid N --dest <depositAddress> --amount <fromAmount>
    • SDK: subtensor.transfer_stake(wallet=wallet, destination_coldkey_ss58=<depositAddress>, hotkey_ss58=<your hotkey>, origin_netuid=N, destination_netuid=N, amount=bt.Balance.from_tao(<fromAmount>, netuid=N))