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:
- Log in into your Bullish account
- Click on your account initials at the upper right corner and then click Settings
- Click API Keys and then click Add API Key
- Select the API Key type, either ECDSA or HMAC
- Enter a key name in the Key Name field
- Adding an IP whitelist is optional. Should an IP whitelist be added, login requests must be from within the IP whitelist range.
- 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
metadatastring to extract youruserIdlike 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 keyuserId- Bullish user id corresponding to the metadatasignature- signed JSON string encoding ofloginPayload, see the code sample for how to get itexpirationTime- epoch timestamp in seconds that is 5 minutes in the futurenonce- epoch timestamp in seconds; note this login API nonce has no connection to the orders API noncebiometricsUsed- set tofalsesessionKey- set tonull
{
"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 EPOCHBX-NONCE- client side incremented 64-bit unsigned integerBX-PUBLIC-KEY- Public key for the HMAC Key being used to generate the JWTBX-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-TIMESTAMPheader - 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 metadatasignature- signed JSON string encoding ofloginPayload, see the code sample for how to get itexpirationTime- epoch timestamp in seconds that is 5 minutes in the futurenonce- epoch timestamp in seconds; note this login API nonce has no connection to the orders API noncebiometricsUsed- set tofalsesessionKey- set tonull
{
"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
nullto represent the absence of a value - The fields must be specified and encoded in the order presented in this documentation
Find below <COMMAND> examples:
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
orderIdis390755251743358977
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-NONCEvalue - 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
lowerBoundis the start of day EPOCH timestamp in micro seconds - The nonce
upperBoundis 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-TIMESTAMPheader - 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
- Hash the above string using a SHA-256 hash function and sign the resulting hexdigest with your ECDSA
<PRIVATE_KEY>. - 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
- Hash the above string using a SHA-256 hash function and take the hexdigest.
- 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.