Skip to main content

Authentication

The API uses bearer based authentication. A JWT token is valid for 24 hours only. To generate a JWT token see - Generate a JWT Token

Generate an API Key

A prerequisite to generate API keys is to have a Bullish account. To generate an API key follow these steps:

  1. Log in into your Bullish account
  2. Click on your account initials at the upper right corner and then click Settings
  3. Click API Keys and then click Add API Key
  4. Select the API Key type, either ECDSA or HMAC
  5. Enter a key name in the Key Name field
  6. Adding an IP whitelist is optional. Should an IP whitelist be added, login requests must be from within the IP whitelist range.
  7. Click Generate API Key

HMAC API Key Notes

  • A HMAC API Key is a shared secret key that is used for HMAC based signing of trading API requests
  • Always store your HMAC secret in a secure medium as they are used to sign your requests. Do not share your HMAC secret in any publicly accessible areas such as code repositories, client side code, or other vulnerable areas and make sure the keys are not shipped with your mobile or web apps.
  • You do not need the metadata string to extract your userId like you would a Bullish API Key
  • HMAC API Keys can only be used for Trading on Bullish; the JWT generated using an HMAC API Key is only valid for trading endpoints.

ECDSA API Key Notes

  • An ECDSA API key is a public/private key pair used for ECDSA based signing of trading and custody API requests
  • The private key is what you will use to sign your requests. Always store your private keys in a secure medium as they are used to sign your requests. Do not share your private keys in any publicly accessible areas such as code repositories, client side code, or other vulnerable areas and make sure the keys are not shipped with your mobile or web apps.
  • From here on the:
  • public key will be referred to as PUBLIC_KEY
  • private key will be referred to as PRIVATE_KEY
  • Key and signature format details:
  • Curve: ECDSA R1 (prime256v1/secp256r1/P-256)
  • Signature encoding: DER
  • Hashing Algorithm: SHA256
  • Key format: X.509 SubjectPublicKeyInfo format, PEM encoded

An ECDSA API key additionally has a metadata string associated with it which is displayed along side the key. You must base64 decode the metadata to extract your userId (example follows). You will need the userId in the next step.

echo eyJwdWJsaWNLZXkiOiJQVUJfUjFfNWNpVW52TW5rVThMOVBCWnZaa1BGcjhqdkRnUHpzcHhWNGlqOThIN1JqM1FSNzJyMkEiLCJhY2NvdW50SWQiOjIyMjAwMDAwMDAwMDAwNCwiY3JlZGVudGlhbElkIjoiMTAifQ== | base64 --decode
{"publicKey":"PUB_R1_5ciUnvMnkU8L9PBZvZkPFr8jvDgPzspxV4ij98H7Rj3QR72r2A","userId":"12345","accountId":"12345","credentialId":"10"}

Generate a JWT Token

Bullish API Key

Deprecated [more info]: Existing Bullish API key will no longer be usable as of 28 June, 2024

To generate/get a JWT token for a Bullish API Key you will need to perform the following request:

POST /trading-api/v2/users/login

  • Body
  • publicKey - Bullish account public key
  • userId - Bullish user id corresponding to the metadata
  • signature - signed JSON string encoding of loginPayload, see the code sample for how to get it
  • expirationTime - epoch timestamp in seconds that is 5 minutes in the future
  • nonce - epoch timestamp in seconds; note this login API nonce has no connection to the orders API nonce
  • biometricsUsed - set to false
  • sessionKey - set to null
{
"publicKey": "<PUBLIC_KEY>",
"signature": "<SIGNATURE>",
"loginPayload": {
"userId": "100008771"
"nonce": 1638776636,
"expirationTime": 1638776936,
"biometricsUsed": false,
"sessionKey": null
}
}
  • Response
{
"authorizer": "<AUTHORIZER>",
"token": "<JWT_TOKEN>"
}

See generate JWT for sample Python scripts.

HMAC API Key

To generate/get a JWT token for a HMAC API Key you will need to perform the following request:

GET /trading-api/v1/users/hmac/login

  • Response
{
"authorizer": "<AUTHORIZER>",
"token": "<JWT_TOKEN>"
}

You will need to provide a series of headers along with the request in order to successfully generate a JWT token.

  • BX-TIMESTAMP - number of milliseconds since EPOCH
  • BX-NONCE - client side incremented 64-bit unsigned integer
  • BX-PUBLIC-KEY - Public key for the HMAC Key being used to generate the JWT
  • BX-SIGNATURE - The signed message related to the login request. Outlined below

To sign the login request for an HMAC API Key login we will need to construct a string that concatenates the following fields:

  • timestamp - value provided for the BX-TIMESTAMP header
  • nonce - value provided for the BX-NONCE
  • request method - GET
  • request path - /trading-api/v1/users/hmac/login

See generate JWT for a sample Python script.

ECDSA API Key

To generate/get a JWT token for a ECDSA API Key you will need to perform the following request:

POST /trading-api/v2/users/login

  • Body
  • publicKey - ECDSA public key; new line characters should be Unix encoded (\n, not \r\n or \r)
  • userId - Bullish user id corresponding to the metadata
  • signature - signed JSON string encoding of loginPayload, see the code sample for how to get it
  • expirationTime - epoch timestamp in seconds that is 5 minutes in the future
  • nonce - epoch timestamp in seconds; note this login API nonce has no connection to the orders API nonce
  • biometricsUsed - set to false
  • sessionKey - set to null
{
"publicKey": "<PUBLIC_KEY>",
"signature": "<SIGNATURE>",
"loginPayload": {
"userId": "100008771"
"nonce": 1638776636,
"expirationTime": 1638776936,
"biometricsUsed": false,
"sessionKey": null
}
}
  • Response
{
"authorizer": "<AUTHORIZER>",
"token": "<JWT_TOKEN>"
}

See generate JWT for a sample Python script.

Logout Using a JWT Token

Users can better manage their sessions by logging out of unused sessions. This can be done by calling the GET /trading-api/v1/users/logout endpoint with the JWT Token in the header - see Add Authenticated Request Header.

Add Authenticated Request Header

Each authenticated request must include a Authorization header:

  • Authorization: Bearer <JWT_TOKEN>

The JWT is valid for 24 hours.

Construct the Command You Want to Send

Each authenticated request contains a <COMMAND> to be executed by the API. A <COMMAND> has the following properties:

  • A <COMMAND> is JSON encoded
  • Every field in the JSON payload must have a value. Use null to represent the absence of a value
  • The fields must be specified and encoded in the order presented in this documentation

Find below <COMMAND> examples:

  1. Create Spot Order
  2. Cancel Order

Create Spot Order Example

To create a Spot buy limit order:

  • for the BTCUSD market
  • at a price of 55071.5000
  • for a quantity of 1.87000000
  • with a time-in-force of GTC (good till canceled)

The COMMAND would be constructed like below:

{
"timestamp": "<TIMESTAMP>",
"nonce": "<NONCE>",
"authorizer": "<AUTHORIZER>",
"command": {
"commandType": "V2CreateOrder",
"handle": null,
"symbol": "BTCUSD",
"type": "LMT",
"side": "BUY",
"price": "55071.5000",
"stopPrice": null,
"quantity": "1.87000000",
"timeInForce": "GTC",
"allowMargin": false,
"tradingAccountId": "111234567890"
}
}

Cancel Order Example

To cancel a buy limit order:

  • for the BTCUSD market
  • where the orderId is 390755251743358977

The COMMAND would be constructed like below:

{
"timestamp": "<TIMESTAMP>",
"nonce": "<NONCE>",
"authorizer": "<AUTHORIZER>",
"command": {
"commandType": "V2CancelOrder",
"orderId": "390755251743358977",
"handle": null,
"symbol": "BTCUSD",
"tradingAccountId": "111234567890"
}
}

Construct the BX-NONCE Header

The header BX-NONCE value is a unique client side 64-bit unsigned integer. It has the following characteristics:

  • Each request the client sends should have incrementing BX-NONCE value
  • To prevent a client to send the max value of a 64-bit unsigned integer and thus immediately exhaust all unique nonces the exchange will only accept a nonce within a specified range
  • The lower and upper bounds of the current nonce range are specified by nonce endpoint e.g. GET /nonce
  • The nonce range is updated daily
  • The nonce lowerBound is the start of day EPOCH timestamp in micro seconds
  • The nonce upperBound is the end of day EPOCH timestamp in micro seconds

Construct the BX-SIGNATURE Header

The following signing formats demonstrates how to obtain the BX-SIGNATURE header.

Signing Format

This signing format works with /trading-api/v2/orders, /trading-api/v2/amm-instructions, /trading-api/v2/command and Custody APIs. This signing format has the following benefits:

  • Null fields need not be included in the HTTP request body
  • Fields need not be strictly ordered in the HTTP request body

Construct a string that concatenates the following fields:

  • timestamp - value provided for the BX-TIMESTAMP header
  • nonce - value provided for the BX-NONCE
  • request method - e.g. POST
  • request path - e.g. /trading-api/v2/orders
  • request JSON string, removing any spaces and newline characters.

Note that the same request JSON string used in signing must be sent as the HTTP request body.

How to Sign - ECDSA API Key

  1. Hash the above string using a SHA-256 hash function and sign the resulting hexdigest with your ECDSA <PRIVATE_KEY>.
  2. DER encode the signature, and base64 encode the DER encoded signature.

See sign a request with ECDSA for a sample Python script.

How to Sign - HMAC API Key

  1. Hash the above string using a SHA-256 hash function and take the hexdigest.
  2. Sign the hexdigest from step 2 with your HMAC Secret Key.

See sign a request with HMAC for a sample Python script.

Send the HTTP Authenticated Request

See create an order for a sample Python script.