Loading…
Loading…
Loading…
Developers
Public betaTrade on Croleo from your own programs. Market data is public; orders, balances and fills need an API key and a signed request. Spot only. API keys can never withdraw.
Public beta with test funds
BASE-QUOTE, for example BTC-USDT or ETH-USDC.Content-Type: application/json.Orders, balances and fills need an API key. Create one under Account, API keys: list the IP addresses it may be used from, confirm with your passkey and copy the secret, which is shown once. See also how API keys work.
timestamp + "\n" + METHOD + "\n" + path?query + "\n" + body: the timestamp, the method in capitals, the path with its query string exactly as you send it, and the raw body (empty for GET), joined by newlines (\n).1793323476006
GET
/api/orders/history?market=BTC-USDT&limit=50
1793323476006
POST
/api/orders
{"market":"BTC-USDT","side":"buy","price":"50000.00","quantity":"0.0010"}stale_timestamp. Keep your clock synced.invalid_signature.replayed_request. Sign each request once, with its own timestamp.ip_not_allowed.key_not_allowed. A request with a key never falls back to a browser session.Each reads CROLEO_KEY and CROLEO_SECRET (and CROLEO_BASE, to point it elsewhere) from the environment and prints your balances. The commented last line places a limit order.
#!/usr/bin/env bash
# Needs bash, curl and openssl.
# Run: CROLEO_KEY=ck_... CROLEO_SECRET=... bash croleo.sh
BASE=https://croleo.com
[ -n "$CROLEO_BASE" ] && BASE=$CROLEO_BASE
croleo() {
method=$1 path=$2 body=$3
ts=$(date +%s%3N) # Unix ms (GNU date)
case $ts in *N) ts=$(($(date +%s) * 1000)) ;; esac # macOS date: seconds * 1000
sig=$(printf '%s\n%s\n%s\n%s' "$ts" "$method" "$path" "$body" |
openssl dgst -sha256 -hmac "$CROLEO_SECRET" | awk '{print $NF}')
set -- -sS -X "$method" "$BASE$path" \
-H "X-Croleo-Key: $CROLEO_KEY" \
-H "X-Croleo-Timestamp: $ts" \
-H "X-Croleo-Signature: $sig"
[ -n "$body" ] && set -- "$@" -H "Content-Type: application/json" --data-raw "$body"
curl "$@"
echo
}
croleo GET /api/balances
# croleo POST /api/orders '{"market":"BTC-USDT","side":"buy","price":"50000.00","quantity":"0.0010"}'A key acts for your account, but only through these endpoints. Every key has the same access: there are no scopes to choose.
Keys can never withdraw or change your account
Withdrawals, deposit addresses, saved addresses, passkeys, sessions, API keys and every other account setting are out of reach. A key that calls them gets 403 key_not_allowed.
Revoking a key under Account, API keys stops it at once. Recovering your account revokes every key.
429 rate_limited, with retryAfter in the body and a Retry-After header, both in seconds.429 too_many_streams.maxOpenOrders in GET /api/markets); one more gets 409 too_many_open_orders.{ "error": "rate_limited", "retryAfter": 3 }Errors use the HTTP status and a JSON body with a stable code to match on.
{ "error": "insufficient_funds" }Public: no key and no signature. Send these requests without the X-Croleo headers.
GET/api/markets
Every asset with its decimals, and every market with its status (trading, halted or delisted), tick (price step), lot (quantity step), limits, maker and taker fees in basis points and price band.
No parameters.
{
"assets": [{ "code": "BTC", "decimals": 8 }, { "code": "USDT", "decimals": 6 }],
"markets": [
{
"symbol": "BTC-USDT",
"base": "BTC",
"quote": "USDT",
"status": "trading",
"tick": "0.01",
"lot": "0.0001",
"minQuantity": "0.0001",
"maxQuantity": "100.0000",
"maxPrice": "10000000.00",
"maxOpenOrders": 200,
"makerFeeBps": 10,
"takerFeeBps": 20,
"priceBandBps": 500
}
]
}GET/api/markets/{symbol}/book
The top 50 price levels per side, best first, as [price, quantity] pairs. For live changes, use the market stream.
{
"symbol": "BTC-USDT",
"bids": [["77209.22", "0.0037"], ["77070.49", "0.0042"]],
"asks": [["77487.43", "0.0042"], ["77626.91", "0.0053"]]
}GET/api/markets/{symbol}/trades
The latest trades, newest first, up to 100. side is the taker's side.
{
"symbol": "BTC-USDT",
"trades": [
{
"id": 13,
"price": "77284.47",
"quantity": "0.0005",
"side": "buy",
"time": 1793320210895
},
{
"id": 12,
"price": "77191.84",
"quantity": "0.0025",
"side": "buy",
"time": 1793320210895
}
]
}GET/api/markets/{symbol}/candles
Candles, oldest first; time is the start of the period. Periods without trades are left out. 1m, 5m and 15m cover the last 24 hours; 1h, 4h, 1d and 1w the last year (the newest 1,000 candles). Days start at 00:00 UTC, weeks on Monday.
{
"symbol": "BTC-USDT",
"interval": "1h",
"candles": [
{
"time": 1793318400000,
"open": "62803.91",
"high": "77284.47",
"low": "62803.91",
"close": "77284.47",
"volume": "0.0035",
"quoteVolume": "265.919648",
"trades": 7
}
]
}GET/api/tickers
Rolling 24-hour statistics for every market. open, high and low are null when nothing traded in 24 hours; last is the latest trade ever, or null. Change is (last − open) / open.
No parameters.
{
"tickers": [
{
"symbol": "BTC-USDT",
"last": "77284.47",
"open": "60000.00",
"high": "77284.47",
"low": "60000.00",
"volume": "0.0179",
"quoteVolume": "1130.159648",
"trades": 13
}
]
}Signed with an API key. Order ids are numbers, unique within a market.
POST/api/orders
Places a limit or market order and answers 201 with the order after matching, with the trades it made. status is open, filled or closed (closeReason unfilled, self_trade, or would_trade for a post_only order that would have traded). A market order's price is the worst price it could reach, capped by the price band.
{
"market": "BTC-USDT",
"side": "buy",
"type": "limit",
"price": "50000.00",
"quantity": "0.0010",
"timeInForce": "gtc"
}{
"order": {
"market": "BTC-USDT",
"id": 111,
"type": "limit",
"side": "buy",
"price": "50000.00",
"quantity": "0.0010",
"remaining": "0.0010",
"spend": null,
"timeInForce": "gtc",
"placedAt": 1793323476006,
"status": "open",
"closeReason": null,
"trades": []
}
}POST/api/orders/cancel
Cancels one open order and releases what it held. Ids are unique within a market, so send both.
{ "market": "BTC-USDT", "id": 111 }{
"order": {
"market": "BTC-USDT",
"id": 111,
"type": "limit",
"side": "buy",
"price": "50000.00",
"quantity": "0.0010",
"remaining": "0.0010",
"spend": null,
"timeInForce": "gtc",
"placedAt": 1793323476006,
"status": "closed",
"closeReason": "cancelled"
}
}POST/api/orders/cancel-all
Cancels all your open orders, or only one market's. The body may be left out (every market). cancelled is the number of orders cancelled.
{ "market": "BTC-USDT" }{ "cancelled": 2 }GET/api/orders
Your open orders in every market, with how much is still to fill (remaining).
No parameters.
{
"orders": [
{
"market": "BTC-USDT",
"id": 111,
"type": "limit",
"side": "buy",
"price": "50000.00",
"quantity": "0.0010",
"remaining": "0.0010",
"timeInForce": "gtc",
"placedAt": 1793323476006
}
]
}GET/api/orders/history
Your closed orders, most recently closed first, with what filled, its value and the fee. Pages end with next: pass it as before to get older rows; null means there are none.
{
"orders": [
{
"market": "BTC-USDT",
"id": 95,
"type": "limit",
"side": "buy",
"price": "74624.04",
"quantity": "0.0010",
"remaining": "0.0000",
"spend": null,
"timeInForce": "gtc",
"placedAt": 1793321026544,
"status": "filled",
"closeReason": null,
"filled": "0.0010",
"filledValue": "74.624040",
"fee": "0.149249",
"feeAsset": "USDT",
"closedAt": 1793321027591
}
],
"next": null
}Signed with an API key.
GET/api/balances
Every asset, with what is available and what is on hold (in open orders or pending withdrawals), in the asset's full decimals.
No parameters.
{
"balances": [
{ "asset": "BTC", "available": "0.01250000", "onHold": "0.00000000" },
{ "asset": "USDT", "available": "68175.000000", "onHold": "12000.000000" }
]
}GET/api/fills
Your side of each trade, newest first: role is maker or taker, and the fee is charged in the quote asset. Paged like order history.
{
"fills": [
{
"market": "BTC-USDT",
"order": 95,
"trade": 14,
"side": "buy",
"role": "taker",
"price": "74624.04",
"quantity": "0.0010",
"value": "74.624040",
"fee": "0.149249",
"feeAsset": "USDT",
"time": 1793321027591
}
],
"next": "1188-2"
}Each market streams its order book (top 50 levels per side), trades and 24-hour ticker as server-sent events. No key is needed: open one stream per market you follow and close it when you are done.
GET/api/markets/{symbol}/streamPublic
// In a browser (or Node.js with an EventSource package). No key needed.
const stream = new EventSource("https://croleo.com/api/markets/BTC-USDT/stream");
let seq = null;
stream.addEventListener("snapshot", (event) => {
const s = JSON.parse(event.data);
seq = s.seq;
// Replace your state with s.bids, s.asks, s.trades (newest first) and s.ticker.
});
stream.addEventListener("update", (event) => {
const u = JSON.parse(event.data);
if (u.seq !== seq + 1) {
stream.close(); // a gap: open a new stream, which starts with a snapshot
return;
}
seq = u.seq;
// Apply u.bids and u.asks (a quantity of 0 removes the level), then u.trades (oldest first).
});snapshot: the whole state (book, latest trades newest first, ticker and status). Always the first event; one can also arrive mid-stream for a client that fell behind, and then it replaces everything.update: what changed. bids and asks hold only the changed levels: replace the level at that price, and remove it when the quantity is zero. trades lists the new trades, oldest first. ticker is included only when there were trades.seq rises by exactly 1 with each update on a connection. On a gap, close the stream and open it again. After a server restart numbering continues from a higher number: always continue from the latest snapshot.GET /api/orders, GET /api/balances and GET /api/fills.{
"symbol": "BTC-USDT",
"seq": 922,
"status": "trading",
"bids": [["77209.22", "0.0037"]],
"asks": [["77487.43", "0.0042"]],
"trades": [
{
"id": 13,
"price": "77284.47",
"quantity": "0.0005",
"side": "buy",
"time": 1793320210895
}
],
"ticker": {
"last": "77284.47",
"open": "60000.00",
"high": "77284.47",
"low": "60000.00",
"volume": "0.0179",
"quoteVolume": "1130.159648",
"trades": 13
}
}{
"seq": 923,
"status": "trading",
"bids": [["77209.22", "0.0000"]],
"asks": [],
"trades": [
{
"id": 14,
"price": "77209.22",
"quantity": "0.0037",
"side": "sell",
"time": 1793320230512
}
],
"ticker": {
"last": "77209.22",
"open": "60000.00",
"high": "77284.47",
"low": "60000.00",
"volume": "0.0216",
"quoteVolume": "1415.833762",
"trades": 14
}
}No key can reach a withdrawal endpoint, and there is no setting that could allow one. Withdrawals are made only in the app, with a passkey check, a waiting period for new addresses and review of large amounts. A leaked key can, at worst, place or cancel orders within your own balance; revoke it at once under Account, API keys.
How withdrawals are protected