Cryptora API

Read your spot balances and orders, and place and cancel spot orders, from your own programs. Base URL: https://cryptora.dev

Keys

Generate a key in Settings > API Access. You get an API key (cex_…) and a secret (cxs_…). The secret is shown once; keep it on your own server and never in a browser or app.

  • Read is always on: balances, open orders, order history and fills.
  • Trade adds placing and cancelling spot orders.
  • No key can withdraw or transfer funds.
  • With an IP list, the key only works from those addresses.
  • A password reset deletes every key on the account. Deleting a key stops it at once.

Signing a request

Every call except /api/v1/time carries three headers:

HeaderValue
X-WS-APIKEYYour API key
X-WS-TIMESTAMPThe current time in milliseconds since 1970 (UTC)
X-WS-SIGNATUREHMAC-SHA256 of the string below, keyed with your secret, as lowercase hex
X-WS-RECV-WINDOWOptional. How many milliseconds the request stays valid: 5000 by default, up to 60000

The string you sign is these four joined with nothing between them:

timestamp + METHOD + path with query + body

1790500000000GET/api/v1/orders?symbol=BTC-USDT
1790500000000POST/api/v1/orders{"symbol":"BTC-USDT","side":"BUY","type":"MARKET","amount":"0.001"}

Sign the exact body bytes you send. The timestamp must be within the window of the server clock (GET /api/v1/time), and each signature works once.

Examples

Node.js 18+

import crypto from 'node:crypto';

const KEY = process.env.WS_API_KEY;       // cex_...
const SECRET = process.env.WS_API_SECRET; // cxs_...

async function call(method, path, body) {
  const ts = Date.now().toString();
  const raw = body ? JSON.stringify(body) : '';
  const sig = crypto.createHmac('sha256', SECRET).update(ts + method + path + raw).digest('hex');
  const res = await fetch('https://cryptora.dev' + path, {
    method,
    headers: { 'X-WS-APIKEY': KEY, 'X-WS-TIMESTAMP': ts, 'X-WS-SIGNATURE': sig, 'Content-Type': 'application/json' },
    body: raw || undefined,
  });
  return res.json();
}

console.log(await call('GET', '/api/v1/account'));
console.log(await call('POST', '/api/v1/orders', { symbol: 'BTC-USDT', side: 'BUY', type: 'LIMIT', amount: '0.001', price: '50000' }));

Python

import hashlib, hmac, json, os, time, requests

KEY, SECRET = os.environ["WS_API_KEY"], os.environ["WS_API_SECRET"]

def call(method, path, body=None):
    ts = str(int(time.time() * 1000))
    raw = json.dumps(body, separators=(",", ":")) if body else ""
    sig = hmac.new(SECRET.encode(), (ts + method + path + raw).encode(), hashlib.sha256).hexdigest()
    headers = {"X-WS-APIKEY": KEY, "X-WS-TIMESTAMP": ts, "X-WS-SIGNATURE": sig, "Content-Type": "application/json"}
    return requests.request(method, "https://cryptora.dev" + path, headers=headers, data=raw or None).json()

print(call("GET", "/api/v1/orders?symbol=BTC-USDT"))

curl

TS=$(date +%s000)
PATH_Q='/api/v1/account'
SIG=$(printf '%s' "${TS}GET${PATH_Q}" | openssl dgst -sha256 -hmac "$WS_API_SECRET" | sed 's/^.* //')
curl -s "https://cryptora.dev${PATH_Q}" -H "X-WS-APIKEY: $WS_API_KEY" -H "X-WS-TIMESTAMP: $TS" -H "X-WS-SIGNATURE: $SIG"

Endpoints

Symbols are written BTC-USDT (BTC/USDT and btc_usdt work too). Responses are JSON with ok true or false.

Method and pathKey needsWhat it does
GET /api/v1/timenothingServer time, for the timestamp
GET /api/v1/accountReadSpot balances: available and locked per coin. ?currencies=BTC,USDT narrows it
GET /api/v1/ordersReadOpen spot orders. ?symbol= narrows it
GET /api/v1/orders/historyReadClosed spot orders. ?symbol= and ?page=
GET /api/v1/tradesReadYour fills. ?symbol= narrows it
POST /api/v1/ordersTradePlace a spot order (body below)
DELETE /api/v1/orders/<id>TradeCancel one of your open orders

POST /api/v1/orders body

FieldValue
symbolThe market, for example BTC-USDT
sideBUY or SELL
typeLIMIT or MARKET
amountQuantity in the base coin (BTC in BTC-USDT)
priceRequired for LIMIT, in the quote coin

Orders follow the same rules as the trade page: your verification level, balances, fees and limits all apply.

Errors

HTTPcodeMeaning
401MISSING_AUTHA header is missing
401TIMESTAMP_OUT_OF_WINDOWYour clock is off, or the request is too old
401INVALID_KEYUnknown or deleted key
401KEY_NOT_SIGNABLEA key made before signing existed: delete it and generate a new one
401INVALID_SIGNATUREThe signature does not match the string above
401REPLAYEDThis exact request was already sent
403IP_NOT_ALLOWEDThe call came from an address not on the key’s list
403PERMISSION_DENIEDThe key has no Trade permission
403ACCOUNT_RESTRICTEDThe account is locked or suspended
429RATE_LIMITEDToo many requests, see below

An order the trade rules refuse comes back with ok false and an error message, as on the trade page.

Limits

  • 600 requests a minute per key.
  • 100 new spot orders a minute per account, the same as the trade page.
  • Up to 10 active keys per account.