Skip to content
Xinxi Wang edited this page Apr 20, 2022 · 23 revisions

We provide a set of easy to use REST APIs and Websocket APIs for you to trade cryptos. You can set up an account with us within 5 minutes using your email and phone number and then try our API with the FREE Litecoin immediately deposited into your newly created account. Should you have any question regarding our API, feel free to talk to us at our Telegram group.

REST API

Connection

Endpoint: https://api.coinut.com

Authentication

Our REST APIs use HMAC-SHA256 to authenticate users. To authenticate, simply put your username and signature in the HTTP header part:

X-USER: <your username> 
X-SIGNATURE: HMAC-SHA256(<your api key>, payload)

Your API key can be found on the Settings page, and you should never disclose it to anyone else. To verify your HMAC-SHA256 function, check this example

HMAC-SHA256('b45e65ff-d4c4-433c-b032-093423b1aaa4', '{"nonce":3062542,"request":"user_balance"}') = 'd1893b047c4f2f39a229eff4342c1a5c3717cddea01fdfd996199a9bb95f58ea'

For Python2.7 or Python 3, you can use the following sample code to obtain the above signature.

import hmac
import hashlib
signature = hmac.new(key='b45e65ff-d4c4-433c-b032-093423b1aaa4'.encode('utf-8'), msg='{"nonce":3062542,"request":"user_balance"}'.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()

The payloads for APIs that require authentication are JSON dictionaries. For your security, you should put a nonce field in the dictionary with a monotonically increasing integer value to avoid a replay attack.

Our REST APIs and Websocket APIs are mostly the same. The documentation for Websocket APIs below also applies to REST APIs. Due to the HTTP connection constraints, REST APIs do not support incremental updates. Therefore, all subscribe fields in the Websocket APIs have no effect on REST APIs. If you need incremental updates, use Websocket instead.

Our code library for REST API can be found here.

Websocket API

Connection

Establish a connection

  • Websocket endpoint: wss://wsapi.coinut.com

NodeJS

If you are using NodeJS, you can install the websocket package through the command npm install websocket, and use the following code to connect to our WebSocket server.

var W3CWebSocket = require('websocket').w3cwebsocket;
var client = new W3CWebSocket('wss://wsapi.coinut.com');

Python

Using python, you can connect to our WebSocket server using the websocket-client package like the following. Please use the command sudo pip install websocket-client or git to install the newest version. Older version may give you a SSLV3_ALERT_HANDSHAKE_FAILURE error.

import websocket
ws = websocket.WebSocket()
ws.connect("wss://wsapi.coinut.com")

Heartbeats

Heartbeats can be used to maintain a connection or check the status of a connection.

Request

{"request":"hb","nonce":3637472}

Reply

{
   "nonce":3637472,
   "reply":"hb",
   "status":[
      "OK"
   ]
}

Sample code

  • NodeJS
var W3CWebSocket = require('websocket').w3cwebsocket;
var client = new W3CWebSocket('wss://wsapi.coinut.com');

client.onmessage = function(e) {
    console.log(e.data);
};

client.onopen = function () {
    client.send('{"request":"hb","nonce":3637}')
}
  • Python
import websocket
ws = websocket.WebSocket()
ws.connect("wss://wsapi.coinut.com")
ws.send('{"request":"hb","nonce":3637}')
print(ws.recv())

Nonce

Each request should include a unique nonce within the range of [1, 16777215], which will be then included in the replies for that request. Therefore, nonce can be used to track the replies for a particular request.

Public APIs

Get spot trading instruments

Request

{"request": "inst_list", "sec_type": "SPOT", "nonce": 39116}

Response

{
   "SPOT":{
      "LTCBTC":[
         {
            "base":"LTC",
            "inst_id":1,
            "decimal_places":5,
            "quote":"BTC"
         }
      ],
      "ETHBTC":[
         {
            "quote":"BTC",
            "base":"ETH",
            "decimal_places":5,
            "inst_id":2
         }
      ]
   },
   "nonce":39116,
   "reply":"inst_list",
   "status":[
      "OK"
   ]
}
  • inst_id, the id of the instrument, will not change over time.
  • decimal_places is the number of decimal places of the price.

Get realtime ticks

Request

{"request":"inst_tick","inst_id":1, "subscribe": true, "nonce":61514}
  • subscribe is only needed when you need updates of new ticks.
  • once you start to subscribe the ticks for one instrument, use this request with "subscribe": "false" to unsubscribe.

Reply

{
  "highest_buy": "750.58100000",
  "inst_id": 1,
  "last": "752.00000000",
  "lowest_sell": "752.00000000",
  "reply": "inst_tick",
  "timestamp": 1481355058109705,
  "trans_id": 170064,
  "volume": "0.07650000",
  "volume24": "56.07650000"
}
  • volume is the total volume since the launch of the exchange
  • volume24 gives the trading volume of the last 24 hours

Get orderbooks in realtime

Request

{"request":"inst_order_book","inst_id":1, "subscribe": true, "nonce":704114}
  • inst_id is required
  • subscribe is optional. It's needed only if you need incremental updates.
  • To unsubscribe, use this request with subscribe set as false.

Response

The server will immediately return a snapshot of the current orderbook as the following:

{
  "buy":
   [ { "count": 7, "price": "750.00000000", "qty": "0.07000000" },
     { "count": 1, "price": "751.00000000", "qty": "0.01000000" },
     { "count": 1, "price": "751.34500000", "qty": "0.01000000" } ],
  "sell":
   [ { "count": 6, "price": "750.58100000", "qty": "0.06000000" },
     { "count": 1, "price": "750.58200000", "qty": "0.01000000" },
     { "count": 1, "price": "750.58300000", "qty": "0.01000000" } ],
  "inst_id": 1,
  "nonce": 704114,
  "total_buy": "67.52345000",
  "total_sell": "0.08000000",
  "reply": "inst_order_book",
  "status": [ "OK" ]
}

After that, whenever the orderbook changes, the server will push incremental updates (if subscribe is set as true in the request):

{ "count": 7,
  "inst_id": 1,
  "price": "750.58100000",
  "qty": "0.07000000",
  "total_buy": "120.06412000",
  "reply": "inst_order_book_update",
  "side": "BUY",
  "trans_id": 169384
}

To use the incremental update, first find an entry in the original orderbook with the same price field, and then replace that entry with this new update if qty is not 0. If qty is 0, the original entry needs to be deleted. If no such entry is found, insert this update into the original orderbook.

Instead of keeping track of the full orderbook, we can also use aggregated ones.

{
   "request":"inst_order_book",
   "inst_id":1,
   "decimal_places":2,
   "top_n":10,
   "subscribe":true,
   "nonce":704114
}
  • top_n can be any of 10, 50, 100, and 200. It indicates how many entries of the buy/sell side of the orderbook should be returned.
  • decimal_places indicates how many decimal places shall be used for prices.
  • incremental updates are also adjusted according to the top_n and decimal_places.

Get market trades

Request

{"request":"inst_trade", "subscribe": true, "inst_id":1,"nonce":450319}
  • subscribe is only needed if you need incremental updates.

Response

A snapshot will be returned immediately from the server.

{
  "nonce": 450319,
  "reply": "inst_trade",
  "status": [
    "OK"
  ],
  "trades": [
    {
      "price": "750.00000000",
      "qty": "0.01000000",
      "side": "BUY",
      "timestamp": 1481193563288963,
      "trans_id": 169514
    },
    {
      "price": "750.00000000",
      "qty": "0.01000000",
      "side": "BUY",
      "timestamp": 1481193345279104,
      "trans_id": 169510
    },
    {
      "price": "750.00000000",
      "qty": "0.01000000",
      "side": "BUY",
      "timestamp": 1481193333272230,
      "trans_id": 169506
    },
    {
      "price": "750.00000000",
      "qty": "0.01000000",
      "side": "BUY",
      "timestamp": 1481193007342874,
      "trans_id": 169502
    }]
}

And updates will come after that (Some updates may come before the snapshot. You may check the trans_id field for the chronological order of them):

{
  "inst_id": 1,
  "price": "750.58300000",
  "reply": "inst_trade_update",
  "side": "BUY",
  "timestamp": 0,
  "trans_id": 169478
}

Private APIs

There are two login methods. One is through username and password; the other is through the API key.

Login with username and password

Request

{
   "request":"login",
   "username":"test",
   "password":"Abcdefg12345",
   "nonce":829055
}

If you enabled two-factor authentication for your account, you will need to proivde an OTP field as well like the following:

{
   "request":"login",
   "username":"test",
   "password":"Abcdefg12345",
   "otp": "545848",
   "nonce":829055
}

Reply

{
   "api_key":"b46e658f-d4c4-433c-b032-093423b1aaa4",
   "country":"NA",
   "email":"tester@test.com",
   "failed_times":0,
   "lang":"en_US",
   "nonce":829055,
   "otp_enabled":false,
   "products_enabled":[
      "SPOT",
      "FUTURE",
      "BINARY_OPTION",
      "OPTION"
   ],
   "reply":"login",
   "session_id":"f8833081-af69-4266-904d-eea088cdcc52",
   "status":[
      "OK"
   ],
   "timezone":"Asia/Singapore",
   "unverified_email":"",
   "username":"test"
}

The session_id field can be used to login again if your connection dropped and needs to reconnect. This avoids user interaction if your two-factor authentication is enabled. To achieve that, just use the following request. Please notice that session_id only last one hour after you disconnect.

{
   "request":"login",
   "username":"test",
   "session_id":"f8833081-af69-4266-904d-eea088cdcc52",
   "nonce":829055
}

Login with API key

This method does not need to reveal your password and also avoid two-factor authentication. To login through the API key, simply concatenate your username, the current unix timestamp, and request nonce into a message as the following, and then calculate the HMAC-SHA256 digest as the following. You should make sure our computer's clock is reasonably correct.

HMAC-SHA256(YOUR_API_KEY, username|timestamp|nonce)

For example, if the username = "test", timestamp = 1542603878, and nonce = 67130554, concatenate them as the following:

test|1542603878|67130554

Then calculate the HMAC-SHA256 digest (suppose the API key is "ec9dd76b-71da-4ab9-bb65-1a19d1184b84", you can find yours on Settings)

HMAC-SHA256("ec9dd76b-71da-4ab9-bb65-1a19d1184b84", "test|1542603878|67130554")

which should give you the following result:

208ade71dc150118feebef664e2262358079d3b445971dedf0eb1d6de3cc2e04

And then make a login request immediately. Make sure the timestamp, nonce, and username in the login request are the same as those for computing the HMAC-SHA256 value. For this example, the login request should be:

{
  "request": "login",
  "username": "test",
  "nonce": 67130554,
  "hmac_sha256": "208ade71dc150118feebef664e2262358079d3b445971dedf0eb1d6de3cc2e04",
  "timestamp": 1542603878
}

Get Account Balance

Request

{
  "nonce": 306254,
  "request": "user_balance"
}

Reply

{
  "nonce": 306254,
  "status": [
    "OK"
  ],
  "BTC": "192.46630415",
  "LTC": "6000.00000000",
  "ETC": "800.00000000",
  "ETH": "496.99938000",
  "floating_pl": "0.00000000",
  "initial_margin": "0.00000000",
  "realized_pl": "0.00000000",
  "maintenance_margin": "0.00000000",
  "equity": "192.46630415",
  "reply": "user_balance",
  "trans_id": 15159032
}
  • If you only trade spots, floating_pl, initial_margin, realized_pl, maintenance_margin, and equity can be ignored.

Submit an order

Request

{
   "request":"new_order",
   "nonce": 956475,
   "inst_id":1,
   "price":"750.581",
   "qty":"0.01",
   "client_ord_id":1345,
   "side":"SELL"
}
  • inst_id is the id of the instrument returned by the inst_list API.
  • price is the price for a limit order. Market orders do not need this field.
  • qty is the quantity you that want to buy or sell.
  • client_ord_id can be any integer within the range of [1, 16777215]. It can be used by the client to identify the order
  • side BUY/SELL

Response

There are three kinds of responses: accepted, filled, and rejected.

(i) Accepted

If an order cannot be fully filled immediately but may be able to be filled later, the order will be inserted into the exchange's orderbook, and a reply with order_accepted will be returned to the user. This only applies to a limit order. A market order will never receive an order_accepted reply.

   {
      "nonce":956475,
      "status":[
         "OK"
      ],
      "order_id":1,
      "open_qty": "0.01",
      "inst_id":1,
      "qty":"0.01",
      "client_ord_id": 1345,
      "order_price":"750.581",
      "reply":"order_accepted",
      "side":"SELL",
      "trans_id":127303
   }
(ii) Filled

If an order is partially or fully filled, order_filled replies will be returned. The format is like the following:

  {
    "commission": {
      "amount": "0.00799000",
      "currency": "USD"
    },
    "fill_price": "799.00000000",
    "fill_qty": "0.01000000",
    "nonce": 956475,
    "order": {
      "client_ord_id": 12345,
      "inst_id": 2,
      "open_qty": "0.00000000",
      "order_id": 721923,
      "price": "748.00000000",
      "qty": "0.01000000",
      "side": "SELL",
      "timestamp": 1482903034617491
    },
    "reply": "order_filled",
    "status": [
      "OK"
    ],
    "timestamp": 1482903034617491,
    "trans_id": 20859252
  }

One order may be filled at different price levels. And each fill will generate a single order_fill reply. Unfilled market orders will be rejected immediately. Unfilled limit orders will remain in the orderbook.

(iii) Rejected

An order may be rejected because of various reasons. For example, an order that requires too much balance to open will be rejected with a NOT_ENOUGH_BALANCE error; an unfilled market order will be rejected with a NOT_ENOUGH_LIQUIDITY error; an order with invalid price will be rejected with an INVALID_PRICE error. The following gives a complete list of possible errors:

UNKNOWN_ERROR
INVALID_INSTRUMENT
INST_EXPIRED
INVALID_PRICE
INVALID_QUANTITY
NOT_ENOUGH_BALANCE
NOT_ENOUGH_LIQUIDTY
INVALID_CLIENT_ORD_ID
TRADE_DISABLED

The format of order_reject replies is as the following.

{
    "nonce": 275825,
    "status": [
        "OK"
    ],
    "order_id": 7171,
    "open_qty": "100000.00000000",
    "price": "750.60000000",
    "inst_id": 2,
    "reasons": [
        "NOT_ENOUGH_BALANCE"
    ],
    "client_ord_id": 4,
    "timestamp": 1482080535098689,
    "reply": "order_rejected",
    "qty": "100000.00000000",
    "side": "BUY",
    "trans_id": 3282993
}

Submit orders in batch mode

Liquidity providers may need to submit multiple orders at once. This API enables that.

Request

{
   "request":"new_orders",
   "nonce":621701,
   "orders":[
      {
         "inst_id":1,
         "price":"750.581",
         "qty":"0.01",
         "client_ord_id":1345,
         "side":"SELL"
      },
      {
         "inst_id":1,
         "price":"750.321",
         "qty":"0.01",
         "client_ord_id":50001346,
         "side":"BUY"
      }
   ]
}
  • orders array shall include all orders' information. The maximum number of orders per batch should not exceed 1000.

Response

[
  {
    "nonce": 621701,
    "status": [
      "OK"
    ],
    "order_id": 331,
    "open_qty": "0.01000000",
    "price": "750.58100000",
    "inst_id": 1,
    "client_ord_id": 1345,
    "timestamp": 1490713990542441,
    "reply": "order_accepted",
    "qty": "0.01000000",
    "side": "SELL",
    "trans_id": 15155495
  },
  {
    "nonce": 621701,
    "status": [
      "OK"
    ],
    "order_id": 332,
    "open_qty": "0.01000000",
    "price": "750.32100000",
    "inst_id": 1,
    "client_ord_id": 50001346,
    "timestamp": 1490713990542441,
    "reply": "order_accepted",
    "qty": "0.01000000",
    "side": "BUY",
    "trans_id": 15155497
  }
]
  • Orders are executed one by one following the order in the request.
  • The response is an array containing each order's result.

Get open orders

Request

{"request": "user_open_orders", "nonce": 1234, "inst_id": 1}
  • inst_id specifies the instrument of the open orders that you want to get.

Response

{
    "nonce": 1234,
    "reply": "user_open_orders",
    "status": [
        "OK"
    ],
    "orders": [
        {
            "order_id": 35,
            "open_qty": "0.01000000",
            "price": "750.58200000",
            "inst_id": 1,
            "client_ord_id": 4,
            "timestamp": 1481138766081720,
            "qty": "0.01000000",
            "side": "BUY"
        },
        {
            "order_id": 30,
            "open_qty": "0.01000000",
            "price": "750.58100000",
            "inst_id": 1,
            "client_ord_id": 5,
            "timestamp": 1481137697919617,
            "qty": "0.01000000",
            "side": "BUY"
        }
    ]
}

Accepted orders will also become part of the user's open orders. Therefore, to incrementally update the user's open orders list, first use this user_open_orders request, and then listen to order_accepted messages. There is no need to subscribe or unsubscribe order_accepted messages. They are always delivered to all websocket connections belonging to the user.

Cancel an order

Request

{"request":"cancel_order","inst_id":1,"order_id":39,"nonce":547201}
  • inst_id and order_id are all required.

Response

If successful, the result is similar to following

  {
    "nonce": 547201,
    "reply": "cancel_order",
    "order_id": 1,
    "client_ord_id": 13556,
    "status": [
      "OK"
    ]
  }

Cancel orders in batch mode

Request

{
   "request":"cancel_orders",
   "entries":[
      {
         "inst_id":1,
         "order_id":332      
      },
      {
         "inst_id":1,
         "order_id":329
      }
   ],
   "nonce":547201
}
  • The maximum number of orders can be canceled in one request is 1000.

Response

{
  "nonce": 547201,
  "reply": "cancel_orders",
  "status": [
    "OK"
  ],
  "results": [
    {
      "order_id": 329,
      "status": "OK",
      "inst_id": 1,
      "client_ord_id": 13561
    },
    {
      "order_id": 332,
      "status": "OK",
      "inst_id": 1,
      "client_ord_id": 13562
    }
  ],
  "trans_id": 15166063
}
  • The results field contains each order's cancel request result.

Get your history trades

Request

{"request":"trade_history","inst_id":1,"start":1,"limit":2,"nonce":326181}
  • start and limit are for pagination and are optional. The default value for start is 0, and limit 100.

Reply

{
  "nonce": 326181,
  "reply": "trade_history",
  "status": [
    "OK"
  ],
  "trades": [
    {
      "commission": {
        "amount": "0.00000100",
        "currency": "BTC"
      },
      "order": {
        "client_ord_id": 297125564,
        "inst_id": 1,
        "open_qty": "0.00000000",
        "order_id": 721327,
        "price": "1.00000000",
        "qty": "0.00100000",
        "side": "SELL",
        "timestamp": 1482490337560987
      },
      "fill_price": "1.00000000",
      "fill_qty": "0.00100000",
      "timestamp": 1482490337560987,
      "trans_id": 10020695
    },
    {
      "commission": {
        "amount": "0.00000100",
        "currency": "BTC"
      },
      "order": {
        "client_ord_id": 297118937,
        "inst_id": 1,
        "open_qty": "0.00000000",
        "order_id": 721326,
        "price": "1.00000000",
        "qty": "0.00100000",
        "side": "SELL",
        "timestamp": 1482490330557949
      },
      "fill_price": "1.00000000",
      "fill_qty": "0.00100000",
      "timestamp": 1482490330557949,
      "trans_id": 10020514
    }
  ]
}

order_filled messages can be used as incremental updates of history trades.

API code library

Check our API code library.

Join this telegram to discuss our API.