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:
| Header | Value |
|---|---|
X-WS-APIKEY | Your API key |
X-WS-TIMESTAMP | The current time in milliseconds since 1970 (UTC) |
X-WS-SIGNATURE | HMAC-SHA256 of the string below, keyed with your secret, as lowercase hex |
X-WS-RECV-WINDOW | Optional. 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 path | Key needs | What it does |
|---|---|---|
GET /api/v1/time | nothing | Server time, for the timestamp |
GET /api/v1/account | Read | Spot balances: available and locked per coin. ?currencies=BTC,USDT narrows it |
GET /api/v1/orders | Read | Open spot orders. ?symbol= narrows it |
GET /api/v1/orders/history | Read | Closed spot orders. ?symbol= and ?page= |
GET /api/v1/trades | Read | Your fills. ?symbol= narrows it |
POST /api/v1/orders | Trade | Place a spot order (body below) |
DELETE /api/v1/orders/<id> | Trade | Cancel one of your open orders |
POST /api/v1/orders body
| Field | Value |
|---|---|
symbol | The market, for example BTC-USDT |
side | BUY or SELL |
type | LIMIT or MARKET |
amount | Quantity in the base coin (BTC in BTC-USDT) |
price | Required 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
| HTTP | code | Meaning |
|---|---|---|
| 401 | MISSING_AUTH | A header is missing |
| 401 | TIMESTAMP_OUT_OF_WINDOW | Your clock is off, or the request is too old |
| 401 | INVALID_KEY | Unknown or deleted key |
| 401 | KEY_NOT_SIGNABLE | A key made before signing existed: delete it and generate a new one |
| 401 | INVALID_SIGNATURE | The signature does not match the string above |
| 401 | REPLAYED | This exact request was already sent |
| 403 | IP_NOT_ALLOWED | The call came from an address not on the key’s list |
| 403 | PERMISSION_DENIED | The key has no Trade permission |
| 403 | ACCOUNT_RESTRICTED | The account is locked or suspended |
| 429 | RATE_LIMITED | Too 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.