NAV Navbar
Crypto.com Exchange Broker Program API

javascript python c# java

Introduction

Welcome to the Crypto.com Exchange Broker Program API reference documentation.

Broker can manage orders for their customers by using API Key. Participate in broker program to earn

customer authorise screen

Customer API Key can be generated as these steps:

customer authorise screen

figure 1 Customer authorization screen

Callback payload sample

{
  "id": -1,
  "method": "private/broker/broker-access-token-created",
  "code": "0",
  "detail_code": "0",
  "result": {
    "broker_reference_id": "cdc-authorization-1234567890123",
    "broker_access_token": "78164676-f5f0-43d0-8e7a-e0a3244bd4d9"
  }
}

Broker onboarding for fast API

Register your user account and provide the following info for broker application.

curl -l [callback] -H 'content-type: application/json' -d '{"id": -1,"method": "private/broker/broker-access-token-created","code": "0","detail_code": "0","result": {"broker_reference_id": "[brokerReferenceId]","broker_access_token": "[accessToken]"    }}'

You will receive broker id on successful onboarding. It will be used for customer authorization.

Use the following url for customer authorization page redirection

https://{Frontend_Route_Endpoint}/exchange/authorize_broker?broker_id={broker_id}&broker_reference_id={broker_reference_id}&redirect_uri={redirect_uri}

Name Description
endpoint
  • Production - crypto.com
  • Integration test - xsta-www.3ona.co
broker_id broker id from onboarding
broker_reference_id a reference for receiving access token
redirect_uri redirect destination after access token generated

Change Logs

Breaking Change Schedule

Effective from 2023-05-11 (UAT), TBD (PROD)

Method Breaking Change
- -

Generating the API Key

Before sending any requests, you need to generate a new API key.

This can be done in the Exchange website under User Center - API.

After generating the key, there are two things you need to carefully note down:

API Key and Secret are randomly generated by the system and can not be modified. Default settings will be set to "Can Read" only, and you have the option of adding or removing certain permissions for your API Key via Web UI.

You can optionally specify a whitelist of IP addresses when generating the API Key.

If specified, the API can only be used from the whitelisted IP addresses.

Rate Limits

REST API

For authenticated calls, rate limits are per API method, per API key:

Method Limit
private/create-fast-api-key 30 requests per 100ms

Frontend Route Endpoint

UAT

https://xsta2-www.3ona.co/

Production

https://crypto.com/

REST API Root Endpoint

UAT Sandbox

REST API

https://uat-api.3ona.co/v2/{method}

Production (Effective: TBD)

https://api.crypto.com/v2/{method}

Common API Reference

Most of the APIs for REST and Websockets are shared, and follow the same request format and response, allowing users to easily switch between the two methods.

The Applies To section under each API allows you to see which platform supports the API.

Naming Conventions

Request Format

The following information applies to both REST API and websockets commands:

Name Type Required Description
id long N Request Identifier
Range: 0 to 9,223,372,036,854,775,807
Response message will contain the same id
method string Y The method to be invoked
params object N Parameters for the methods
api_key string Depends API key. See Digital Signature section
sig string Depends Digital signature. See Digital Signature section
nonce long Y Current timestamp (milliseconds since the Unix epoch)

Digital Signature

const crypto = require("crypto-js");

const signRequest = (request_body, api_key, secret) => {
  const { id, method, params, nonce } = request_body;

  function isObject(obj) { return obj !== undefined && obj !== null && obj.constructor == Object; }
  function isArray(obj) { return obj !== undefined && obj !== null && obj.constructor == Array; }
  function arrayToString(obj) { return obj.reduce((a,b) => { return a + (isObject(b) ? objectToString(b) : (isArray(b) ? arrayToString(b) : b)); }, ""); }
  function objectToString(obj) { return (obj == null ? "" : Object.keys(obj).sort().reduce((a, b) => { return a + b + (isArray(obj[b]) ? arrayToString(obj[b]) : (isObject(obj[b]) ? objectToString(obj[b]) : obj[b])); }, "")); }

  const paramsString = objectToString(params);

  console.log(paramsString);

  const sigPayload = method + id + api_key + paramsString + nonce;
  request_body.sig = crypto.HmacSHA256(sigPayload, secret).toString(crypto.enc.Hex);
};

const apiKey = "token"; /* User API Key */
const apiSecret = "secretKey"; /* User API Secret */

let request = {
  id: 11,
  method: "private/get-order-detail",
  api_key: API_KEY,
  params: {
    order_id: 53287421324
  },
  nonce: 1587846358253,
};

const requestBody = JSON.stringify(signRequest(request, apiKey, apiSecret)));
import hmac
import hashlib
import time

API_KEY = "API_KEY"
SECRET_KEY = "SECRET_KEY"

req = {
    "id": 14,
    "method": "private/create-order-list",
    "api_key": API_KEY,
    "params": {
        "contingency_type": "LIST",
        "order_list": [
            {
                "instrument_name": "ONE_USDT",
                "side": "BUY",
                "type": "LIMIT",
                "price": "0.24",
                "quantity": "1.0"
            },
            {
                "instrument_name": "ONE_USDT",
                "side": "BUY",
                "type": "STOP_LIMIT",
                "price": "0.27",
                "quantity": "1.0",
                "trigger_price": "0.26"
            }
        ]
    },
    "nonce": int(time.time() * 1000)
}

# First ensure the params are alphabetically sorted by key
param_str = ""

MAX_LEVEL = 3


def params_to_str(obj, level):
    if level >= MAX_LEVEL:
        return str(obj)

    return_str = ""
    for key in sorted(obj):
        return_str += key
        if obj[key] is None:
            return_str += 'null'
        elif isinstance(obj[key], list):
            for subObj in obj[key]:
                return_str += params_to_str(subObj, ++level)
        else:
            return_str += str(obj[key])
    return return_str


if "params" in req:
    param_str = params_to_str(req['params'], 0)

payload_str = req['method'] + str(req['id']) + req['api_key'] + param_str + str(req['nonce'])

req['sig'] = hmac.new(
    bytes(str(SECRET_KEY), 'utf-8'),
    msg=bytes(payload_str, 'utf-8'),
    digestmod=hashlib.sha256
).hexdigest()

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Net.WebSockets;

private const string API_KEY = "YOUR_API_KEY";
private const string API_SECRET = "YOUR_API_SECRET";

private static string GetSign (Dictionary Request)
{
    Dictionary Params = Request.Params;

    // Ensure the params are alphabetically sorted by key
    string ParamString = string.Join("", Params.Keys.OrderBy(key => key).Select(key => key + Params[key]));

    string SigPayload = Request.method + Request.id + API_KEY + ParamString + Request.nonce;

    var hash = new HMACSHA256(API_SECRET);
    var ComputedHash = hash.ComputeHash(SigPayload);
    return ToHex(ComputedHash, false);
}
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Map;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiRequestJson {
  private Long id;
  private String method;
  private Map<String, Object> params;
  private String sig;

  @JsonProperty("api_key")
  private String apiKey;

  private Long nonce;
}

//------------

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;

public class SigningUtil {

  private static final String HMAC_SHA256 = "HmacSHA256";
  private static final int MAX_LEVEL = 3;

  public static boolean verifySignature(ApiRequestJson apiRequestJson, String secret) {
    try {
      return genSignature(apiRequestJson, secret).equalsIgnoreCase(apiRequestJson.getSig());
    } catch (Exception e) {
      return false;
    }
  }

  @SuppressWarnings("unchecked")
  public static String getParamString(final Object paramObject) {
    StringBuilder sb = new StringBuilder();
    appendParamString(sb, paramObject, 0);
    return sb.toString();
  }


  @SuppressWarnings("unchecked")
  private static void appendParamString(final StringBuilder paramsStringBuilder, final Object paramObject, final int level) {
    if (level >= MAX_LEVEL) {
      paramsStringBuilder.append(paramObject.toString());
      return;
    }

    if (paramObject instanceof Map) {
      TreeMap<String, Object> params = new TreeMap<>((Map) paramObject);
      for (Map.Entry<String, Object> entry : params.entrySet()) {
        if (entry.getValue() instanceof Double) {
          paramsStringBuilder
              .append(entry.getKey())
              .append((new BigDecimal(entry.getValue().toString()))
                  .stripTrailingZeros()
                  .toPlainString());
        } else if (entry.getValue() instanceof List || entry.getValue() instanceof Map) {
          paramsStringBuilder
              .append(entry.getKey());
          appendParamString(paramsStringBuilder, entry.getValue(), level + 1);
        } else {
          paramsStringBuilder
              .append(entry.getKey())
              .append(entry.getValue());
        }
      }
    } else if (paramObject instanceof List) {
      List list = (List) paramObject;
      for (Object o : list) {
        appendParamString(paramsStringBuilder, o, level + 1);
      }
    } else {
      paramsStringBuilder.append(paramObject.toString());
    }
  }

  public static String genSignature(ApiRequestJson apiRequestJson, String secret)
      throws NoSuchAlgorithmException, InvalidKeyException {
    final byte[] byteKey = secret.getBytes(StandardCharsets.UTF_8);
    Mac mac = Mac.getInstance(HMAC_SHA256);
    SecretKeySpec keySpec = new SecretKeySpec(byteKey, HMAC_SHA256);
    mac.init(keySpec);

    String paramsString = "";

    if (apiRequestJson.getParams() != null) {
      paramsString += getParamString(apiRequestJson.getParams());
    }

    String sigPayload =
        apiRequestJson.getMethod()
            + apiRequestJson.getId()
            + apiRequestJson.getApiKey()
            + paramsString
            + (apiRequestJson.getNonce() == null ? "" : apiRequestJson.getNonce());

    byte[] macData = mac.doFinal(sigPayload.getBytes(StandardCharsets.UTF_8));

    return Hex.encodeHexString(macData);
  }

  public static ApiRequestJson sign(ApiRequestJson apiRequestJson, String secret)
      throws InvalidKeyException, NoSuchAlgorithmException {
    apiRequestJson.setSig(genSignature(apiRequestJson, secret));

    return apiRequestJson;
  }

  public static void main(String[] argv) throws InvalidKeyException, NoSuchAlgorithmException {
    ApiRequestJson apiRequestJson = ApiRequestJson.builder()
            .id(11L)
            .apiKey("token")
            .method("public/auth")
            .nonce(1589594102779L)
            .build();

    System.out.println(genSignature(apiRequestJson, "secretKey"));

    System.out.println(sign(apiRequestJson, "secretKey"));

  }
}

For REST API, only the private methods require a Digital Signature (as "sig") and API key (as "api_key") to be passed in. These private endpoints are only accessible by authenticated users.

The authentication is based on the pairing of the API Key, along with the HMAC-SHA256 hash of the request parameters using the API Secret as the cryptographic key.

The algorithm for generating the HMAC-SHA256 signature is as follows:

Digital Signature Issues

Based on the language, and the way data-types are handled, the digital signature may be incorrectly calculated for certain values.

One known case is if parameters contain numbers, and the value passed in is a whole numbers that still has decimal suffixes, e.g. 8000.000 instead of 8000.

When the JSON request is sent to the server, the client (when parsing as JSON) may cast the number 8000.000 as 8000 automatically, which is what the server would see.

However, the digital signature may have been calculated using 8000.000 in the payload, which would result in a digital signature mismatch.

If this scenario happens in your case, there are three options:

Response Format

Name Type Description
id long Original request identifier
method string Method invoked
result object Result object
code int 0 for success, see below for full list
message string (optional) For server or error messages
original string (optional) Original request as a string, for error cases
detail_code string Detail Response Code. Please refer to Exchange v1 API for the list of values.
detail_message string Detail Message (if any).

Response and Reason Codes

These codes are shared by both the response, and the reason field for rejected orders.

Code HTTP Status Message Code Description
0 200 -- Success
10000 200 PARTIAL_SUCCESS Batch request processed successfully and some requests succeeded (E.g. Some orders created when creating a list of orders)

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10001 500 SYS_ERROR Malformed request, (E.g. not using application/json for REST)

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10002 401 UNAUTHORIZED Not authenticated, or key/signature incorrect

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10003 401 IP_ILLEGAL IP address not whitelisted

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10004 400 BAD_REQUEST Missing required fields

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10005 401 USER_TIER_INVALID Disallowed based on user tier

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10006 429 TOO_MANY_REQUESTS Requests have exceeded rate limits

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10007 400 INVALID_NONCE Nonce value differs by more than 30 seconds from server

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10008 400 METHOD_NOT_FOUND Invalid method specified

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10009 400 INVALID_DATE_RANGE Invalid date range

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
10010 200 FAIL Batch request processed successfully but all requests failed (E.g. No orders created when creating a list of orders)

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
20001 400 DUPLICATE_RECORD Duplicated record

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
20002 400 NEGATIVE_BALANCE Insufficient balance

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30003 400 SYMBOL_NOT_FOUND Invalid instrument_name specified

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30004 400 SIDE_NOT_SUPPORTED Invalid side specified

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30005 400 ORDERTYPE_NOT_SUPPORTED Invalid type specified

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30006 400 MIN_PRICE_VIOLATED Price is lower than the minimum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30007 400 MAX_PRICE_VIOLATED Price is higher than the maximum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30008 400 MIN_QUANTITY_VIOLATED Quantity is lower than the minimum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30009 400 MAX_QUANTITY_VIOLATED Quantity is higher than the maximum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30010 400 MISSING_ARGUMENT Required argument is blank or missing

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30013 400 INVALID_PRICE_PRECISION Too many decimal places for Price

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30014 400 INVALID_QUANTITY_PRECISION Too many decimal places for Quantity

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30015 400 REJECTION_FOR_EXEC_INST_POST_ONLY The order is sent with "exec_inst=POST_ONLY", meanwhile, it can not be placed as resting active maker order

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30016 400 MIN_NOTIONAL_VIOLATED The notional amount is less than the minimum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30017 400 MAX_NOTIONAL_VIOLATED The notional amount exceeds the maximum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30023 400 MIN_AMOUNT_VIOLATED Amount is lower than the minimum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30024 400 MAX_AMOUNT_VIOLATED Amount is higher than the maximum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
30025 400 AMOUNT_PRECISION_OVERFLOW Amount precision exceeds the maximum

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
40001 400 MG_INVALID_ACCOUNT_STATUS Operation has failed due to your account's status. Please try again later.

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
40002 400 MG_TRANSFER_ACTIVE_LOAN Transfer has failed due to holding an active loan. Please repay your loan and try again later.

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
40003 400 MG_INVALID_LOAN_CURRENCY Currency is not same as loan currency of active loan

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
40004 400 MG_INVALID_REPAY_AMOUNT Only supporting full repayment of all margin loans

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
40005 400 MG_NO_ACTIVE_LOAN No active loan

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
40006 400 MG_BLOCKED_BORROW Borrow has been suspended. Please try again later.

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
40007 400 MG_BLOCKED_NEW_ORDER Placing new order has been suspended. Please try again later.

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
50001 400 DW_CREDIT_LINE_NOT_MAINTAINED Please ensure your credit line is maintained and try again later.

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
5000008 400 SYSTEM_BUSY System is currently busy, please try later.

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
5000012 400 CURRENCY_CLOSED Currency is closed

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
5000013 400 BAD_PARAMETER Bad parameter(s)

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.
5000808 403 WITHDRAWAL_FORBIDDEN_
TEMPORARILY_UNAVAILABLE
Withdrawal temporarily unavailable

Please refer to detail_code and detail_message for additional information. Please refer to Exchange v1 API for the list of values.

Websocket Termination Codes

Code Description
1000 Normal disconnection by server, usually when the heartbeat isn't handled properly
1006 Abnormal disconnection
1013 Server restarting -- try again later

Fast API

Allow broker to create API key on behalf of customer. Customer does not need to take action to create and share API key.

private/broker/create-fast-api-key

Create API Key on behalf of the user with access token

Request Params

Request Sample

{
  "id":"1",
  "method": "private/broker/create-fast-api-key",
  "params": {
    "broker_access_token":"dddd1234-cafe-deee-9399-123443210000",
    "broker_id":"00111234-cafe-deee-9399-123443210000",
    "broker_reference_id":"69991111-cafe-deee-9399-123443210000",
    "whitelist_ips":"8.8.8.8"
  },
  "nonce": 1680012321987
}
Name Type Required Description
broker_access_token string Y access token received from user authorization
broker_id string Y broker id
broker_reference_id string Y reference id of the user authorization
whitelist_ips string N IP whitelist for using the API KEY

Applies To

REST

REST Method

POST

Response Attributes

Response Sample

{
 "id": "11",
 "method": "private/broker/create-fast-api-key",
 "code": 0,
 "result": {
    "ts": 1683013329206,
    "api_key": "eUF7eDrkkM2jwfsnDqNATW",
    "secret_key": "Xcow878VrkH6dSSkiKQyMh",
    "enabled_trading": true,
    "enabled_withdrawal": false
  }
}
Name Type Description
ts long Timestamp
api_key string API Key
secret_key string API key secret
enabled_trading boolean Trading enabled
enabled_withdrawal boolean Trading enabled

Websocket Heartbeats

Heartbeat Example

{
  "id": 1587523073344,
  "method": "public/heartbeat",
  "code": 0
}

For websocket connections, the system will send a heartbeat message to the client every 30 seconds.

The client must respond back with the public/respond-heartbeat method, using the same matching id, within 5 seconds, or the connection will break.

public/respond-heartbeat

Request Sample

{
  "id": 1587523073344,
  "method": "public/respond-heartbeat"
}

Responds to the server-initiated websocket heartbeat

Request Params

None

Applies To

Websocket (Fast API)

Common Issues

INVALID_NONCE On All Requests

The nonce should be the UTC Unix timestamp in milliseconds.

If this has been carefully checked, then the issue occurs when the system clock of the client machine is greater than 1 second in the future, or 30 seconds in the past.

Usually, re-syncing with the NTP time server on the client machine will correct the issue.

If the issue persists, you can try deliberately subtracting 5 seconds from the nonce to force it to be 5 seconds in the past, which is still within the 30-second past tolerance.