Quickstart

Your first swap in 5 minutes

Three calls: quote → create → track. No SDK, no account for your users, and we never touch your keys.

Base URL  https://cryptoexchanger.co/api/v1

1. Get a key

Create an API key — pick Test first (simulated swaps, no real funds). You'll get a key like cx_test_…. Send it as a bearer token on any authenticated call.

2. Get a quote GET

Best rate across all connected liquidity, plus min/max, in one call. No key needed for quotes.

# BTC -> ETH for 0.05 BTC
curl "https://cryptoexchanger.co/api/v1/estimate?from=BTC&to=ETH&amount=0.05&flow=float"
{ "estimate": { "fromAmount": 0.05, "toAmount": 1.5564, "rate": 31.13,
    "minAmount": 0.0004, "maxAmount": null, "flow": "float" } }

flow=float is the live market rate on arrival (default). flow=fixed locks the rate for the deposit window.

Memo/tag wallets. Recipient metadata belongs in extraId; refund metadata belongs in refundExtraId on the send network. Supply both values on /estimate and /exchange when the respective wallets need them. Routes that cannot preserve a supplied refund tag are excluded; a compatible quote may be unavailable. XRP tag 0 is valid.

3. Create the order POST

Returns a deposit address to send the from coin to. The swapped coins are sent to address. Always include an Idempotency-Key so a retry never creates a second order.

curl -X POST https://cryptoexchanger.co/api/v1/exchange \
  -H "Authorization: Bearer cx_test_..." \
  -H "Idempotency-Key: order-001" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "BTC", "to": "ETH", "amount": 0.05,
    "address": "0xYourRecipientEthWallet",
    "refundAddress": "bc1qYourBtcRefund",
    "flow": "float"
  }'
{ "order": {
    "id": "AKCQ8", "status": "waiting",
    "payinAddress": "bc1q...send-exactly-0.05-BTC-here",
    "payinExtraId": null,
    "expectedFromAmount": 0.05, "expectedToAmount": 1.5564,
    "depositWindow": 3600, "createdAt": 1790026280 } }
Send exactly expectedFromAmount. If the coin needs a memo/tag, it comes back as payinExtraId — include it or funds can be lost. Deposit within depositWindow seconds; a funded order is never expired.

Retry the same logical request. Restart-safe reservations prevent another allocation for the same key. A 503 with retrySameKey:true means creation is pending/uncertain or its safety store is unavailable. Keep the same key and body, check the original order, and contact support if it cannot be recovered. Do not silently create a replacement order.

4. Send funds, then track GET

Send the deposit from your wallet, then poll until the status is terminal. (Or set a webhook and skip polling.)

curl https://cryptoexchanger.co/api/v1/order/AKCQ8 \
  -H "Authorization: Bearer cx_test_..."

Lifecycle: waiting → confirming → exchanging → sending → finished. Terminal-bad: failed, refunded, expired.

Poll loop — Python

import requests, time
H = {"Authorization": "Bearer cx_test_..."}
B = "https://cryptoexchanger.co/api/v1"

q = requests.get(f"{B}/estimate", params={"from":"BTC","to":"ETH","amount":0.05}).json()
o = requests.post(f"{B}/exchange", headers={**H, "Idempotency-Key":"order-001"},
    json={"from":"BTC","to":"ETH","amount":0.05,
          "address":"0xRecipient","refundAddress":"bc1qRefund"}).json()["order"]
# -> send o["expectedFromAmount"] to o["payinAddress"] from your wallet
while True:
    s = requests.get(f"{B}/order/{o['id']}", headers=H).json()["order"]["status"]
    if s in ("finished","failed","refunded","expired"): break
    time.sleep(15)

Node.js

const B = "https://cryptoexchanger.co/api/v1", KEY = "cx_test_...";
const order = await fetch(`${B}/exchange`, { method:"POST",
  headers:{ "Authorization":`Bearer ${KEY}`, "Idempotency-Key":"order-001",
            "Content-Type":"application/json" },
  body: JSON.stringify({ from:"BTC", to:"ETH", amount:0.05,
    address:"0xRecipient", refundAddress:"bc1qRefund" })
}).then(r => r.json());
Building against Test? The sandbox returns a simulated deposit address and auto-advances the order through its lifecycle so you can test polling without moving real coins. Switch to a cx_live_ key when you're ready.
← Overview Authentication →