Last updated
Five JSON endpoints, no API key: list coins, pull live prices, get a signed quote, create a swap and track it to completion. It is the same API our own swap card calls, so if the site works, your integration works.
| Method | Path | Returns |
|---|---|---|
| GET | /api/coins | the full coin registry |
| GET | /api/ticker | USD prices for headline coins |
| POST | /api/quote | a signed, short-lived rate quote |
| POST | /api/order | create a swap from a quote |
| GET | /api/order/{id} | live order status + timeline |
The full coin registry, currently 136 assets. This is the source of truth for everything else: valid coin ids, per-coin minimums, confirmation counts, and the address pattern we use to sanity-check payout addresses. Cache it for minutes, not days; the list changes when we add coins.
curl https://coinvast.io/api/coins
{
"coins": [
{
"id": "xmr",
"ticker": "XMR",
"name": "Monero",
"network": "Monero",
"decimals": 8,
"confirmations": 10,
"minAmount": 0.05,
"addressPattern": "^[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}$",
"custody": "node",
"popular": true
},
{ "id": "usdt-trc20", "ticker": "USDT", "network": "Tron (TRC-20)", "...": "..." }
]
}USD prices and 24-hour change for a fixed set of headline coins (BTC, ETH, XMR, SOL, LTC, TON, DOGE, KAS). It exists for the price strip on our site. It is not a quote: use /api/quote for any number you intend to trade on. On upstream failure it returns an empty entries array with status 200 rather than an error.
curl https://coinvast.io/api/ticker
{
"entries": [
{ "id": "btc", "ticker": "BTC", "priceUsd": 106240.0, "change24hPct": 1.42 },
{ "id": "xmr", "ticker": "XMR", "priceUsd": 412.55, "change24hPct": -0.83 }
]
}A live quote for a pair and amount. Every quote carries a flat 2% spread over the mid-market rate, locked the moment you turn it into an order. Two modes control how long the quote stays valid before you commit: float (valid 30 seconds) and fixed (valid 60 seconds). The spread and the payout network fee are separate fields, never hidden in the rate.
curl -X POST https://coinvast.io/api/quote \
-H "Content-Type: application/json" \
-d '{ "from": "xmr", "to": "btc", "amountIn": 2.5, "mode": "float" }'{
"quote": {
"id": "eyJmcm9tIjoieG1yIi...<base64url payload>.<hex hmac>",
"from": "xmr",
"to": "btc",
"amountIn": 2.5,
"amountOut": 0.00965312,
"rate": 0.00386365,
"mode": "float",
"spreadPct": 2,
"networkFee": 0.00006,
"expiresAt": 1781234567890
}
}Turns an unexpired quote into a swap and returns a deposit address. The pair, amount and mode in the body must match the quote exactly; any drift gets a 400 telling you to refresh the rate. The deposit must arrive within 30 minutes or the order expires.
curl -X POST https://coinvast.io/api/order \
-H "Content-Type: application/json" \
-d '{
"quoteId": "eyJmcm9tIjoieG1yIi...<from the quote>",
"from": "xmr",
"to": "btc",
"amountIn": 2.5,
"mode": "float",
"payoutAddress": "bc1qw4nsga9w3h9jzx0nmtkfh4nv9vh87qj5y3z8ae",
"refundAddress": "48jWWbXZUd9b2iDVnYrqyTXi3FuKKZBRkVtJ1MYkdnRb...",
"email": "[email protected]"
}'{
"order": {
"id": "K7TQ2WXMNR",
"createdAt": 1781234567890,
"status": "AWAITING_DEPOSIT",
"quote": { "...": "the verified quote, echoed back" },
"payoutAddress": "bc1qw4nsga9w3h9jzx0nmtkfh4nv9vh87qj5y3z8ae",
"refundAddress": "48jWWbXZUd9b2iDVnYrqy...",
"depositAddress": "888tNkZrPN6JsEgekjMnAB...",
"timeline": [
{ "at": 1781234567890, "status": "AWAITING_DEPOSIT",
"note": "Swap created — waiting for your XMR deposit" }
],
"expiresAt": 1781236367890
}
}The live state of an order: current status, full timeline with timestamps and transaction hashes (depositTxid, payoutTxid appear once known). Expiry is applied lazily on read, so an abandoned order flips to EXPIRED the first time anyone fetches it past the window.
curl https://coinvast.io/api/order/K7TQ2WXMNR
{
"order": {
"id": "K7TQ2WXMNR",
"status": "SENDING",
"depositTxid": "5f2a9c...",
"payoutTxid": "b81d04...",
"timeline": [
{ "at": 1781234567890, "status": "AWAITING_DEPOSIT", "note": "..." },
{ "at": 1781234890123, "status": "CONFIRMING", "txid": "5f2a9c..." },
{ "at": 1781235241000, "status": "EXCHANGING" },
{ "at": 1781235290456, "status": "SENDING", "txid": "b81d04..." }
],
"...": "same shape as the create response"
}
}Unknown id: 404 with { "error": "Order not found" }. There is no list-orders endpoint by design; an order is only reachable by its id.
Polling advice: GET the order every 5 to 10 seconds while it is in flight. Stop on COMPLETED, EXPIRED, REFUNDED or REJECTED; those four are final and the order will never change again. The happy path is AWAITING_DEPOSIT → CONFIRMING → EXCHANGING → SENDING → COMPLETED, and the slowest hop is almost always the deposit chain's confirmations, not us.
The API is open because keys add friction without adding trust. A few requests keep it that way. Poll orders at 5 to 10 second intervals, not in a tight loop. Quote when a user actually asks for a rate; hammering /api/quote to build your own price feed is what /api/ticker and public price APIs are for.
Quotes are signed and they expire. Do not stockpile quoteIds, and do not try to edit one; the signature check will reject it. And never cache deposit addresses across orders. Each order gets its own deposit address, and sending a second payment to an old one is the slow, painful kind of support ticket.
If you run an exchange aggregator, a comparison site or a listing directory, this API is enough to integrate the full flow today: coins, quote, create, track. It is the exact same API our own swap card uses, not a separate partner surface, so it gets exercised on every swap on the site.
For anything beyond the public endpoints, email [email protected] with the subject "partner" and a sentence about what you are building. One honest roadmap note: per-order webhooks are not public yet. Today you poll GET /api/order/{id} for status; push notifications for integrators are planned but not shipped, and we would rather say so than document an endpoint that does not exist.
No API key · JSON everywhere · the same API our swap card runs on