Authentication

To use an API key with the Bitcoin Suisse REST API, certain information has to be transmitted in the headers of each request.

At the bottom of this page you can find example code in C#, Python and Java.

Request headers

When you created your API Key, you also got an API Secret (see API Keys). The API Secret is used to create a signature for each request. This ensures that your request was not tampered with when it reaches the Bitcoin Suisse servers.

The following information must be provided in the HTTP headers of each request.

NameHeader exampleDescription
API keyX-Auth: BTCS api-key"BTCS" followed by your API key (with a space in between).
NonceX-Auth-Nonce: 11223344556677889900Random string with a length of exactly 20 chars. Only a-z/A-Z/0-9 characters allowed. Needs to be unique for each request.
TimestampX-Auth-Timestamp: 2023‐09‐15T12:16:44:01ZTimestamp of the request. Must follow ISO 8601 "Date and time in UTC" format. Must be in the range of +/- 10s of the current server time.
VersionX-Auth-Version: v1Which version of the authentication mechanism should be used. Currently only v1 is supported.
SignatureX-Auth-Signature: signatureSignature of the request payload to verify that the content of the request has not been changed. Has to be calculated for every request.
Customer numbercustomer-number: BTCS-CUS-123456The customer to act as, optional on some of the endpoints. To find out which customers your API Key can access, use the Customer Management endpoint.

Example request:

Copy
Copied
curl --request GET \
  --url https://api.bitcoinsuisse.com/trading/api/v3/Accounts \
  --header 'X-Auth: BTCS put-your-api-key-here' \
  --header 'X-Auth-Nonce: 12345678901234567898' \
  --header 'X-Auth-Signature: put-the-base64-encoded-request-signature-here' \
  --header 'X-Auth-Timestamp: 2021-03-26 11:33:52.9100000 +00:00' \
  --header 'X-Auth-Version: v1' \
  --header 'accept: */*' \
  --header 'client-number: BTCS-CUS-123456'

Signature calculation

The signature has to be calculated for each request. It is calculated as follows:

  1. Concatenate the following information in this exact order to get the signature message (string):
    1. "BTCS": String, no spaces, no quotation marks
    2. apiKey: Your Bitcoin Suisse API Key (not the API secret)
    3. host: Host name without schema, e.g.: api.bitcoinsuisse.com (no trailing /)
    4. path: Path of the request, e.g.: /auth/api/v1/Customers
    5. queryString: The query string including the ?, e.g.: ?param=123
    6. contentType The Content-Type header of the HTTP request, e.g.: application/json
    7. nonce: Alphanumeric (a-z/A-Z/0-9) string with a length of exactly 20 chars, must be unique for each request
    8. timestamp: Timestamp of the request, must be in the range of +/- 10s of the current server time
    9. version: Version of the authentication mechanism, currently v1
    10. requestBody: The body of the HTTP request as string
  2. Initialize a HMAC/SHA512 algorithm, using the API secret as cryptographic key
    • Use ASCII encoding when converting the API Secret to bytes
  3. Using the previously initialized HMAC/SHA512 algorithm, hash the signature message
    • Use UTF8 encoding when converting the signature message to bytes
  4. Finally, use base64 encoding to encode the hash

C# Example

This example shows how to calculate the signature, add the required headers to a request and send it. It will print the response to the console.

Copy
Copied
using var client = new System.Net.Http.HttpClient();

var apiKey = ""; // TODO: Your API Key
var secret = ""; // TODO: Your API Secret
var customer = ""; // TODO: Your customer number (e.g. BTCS-CUS-000000)

var host = "sandbox-api.btcsqa.net";

// simple GET request, doesn't have query params, content type or request body
var path = "/auth/api/v1/Customers";
var queryString = string.Empty;
var contentType = string.Empty;
var nonce = Guid.NewGuid().ToString("N")[..20]; // must be unique for each request
var timestamp = System.DateTime.UtcNow.ToString("o"); // must be ISO8601 format
var version = "v1";
var requestBody = string.Empty;

var signatureMessage = "BTCS" +
	apiKey +
	host +
	path +
	queryString +
	contentType +
	nonce +
	timestamp +
	version +
	requestBody;

var keyBytes = Encoding.ASCII.GetBytes(secret);
var hmacsha512 = new System.Security.Cryptography.HMACSHA512(keyBytes);
var hashBytes = hmacsha512.ComputeHash(Encoding.UTF8.GetBytes(signatureMessage));
var signature =  Convert.ToBase64String(hashBytes);

//client.DefaultRequestHeaders.Add("customer-number", customer); // needed for most requests, see API specifications
client.DefaultRequestHeaders.Add("X-Auth", "BTCS " + apiKey);
client.DefaultRequestHeaders.Add("X-Auth-Nonce", nonce);
client.DefaultRequestHeaders.Add("X-Auth-Timestamp", timestamp);
client.DefaultRequestHeaders.Add("X-Auth-Version", version);
client.DefaultRequestHeaders.Add("X-Auth-Signature", signature);
var request = await client.GetAsync($"https://{host}{path}{queryString}");
var response = await request.Content.ReadAsStringAsync();

Console.WriteLine(request.StatusCode);
Console.WriteLine(response);

Python Example

This example shows how to calculate the signature, add the required headers to a request and send it. It will print the response to the console.

Save this code into a file (e.g. main.py), fill in your credentials (apiKey, secret, customerNumber) and then run it using python .\main.py. Make sure to use python3.
Copy
Copied
import requests
import random
from datetime import datetime
import hashlib
import hmac
import base64

apiKey = "" # TODO: Your API Key
secret = "" # TODO: Your API Secret
customerNumber = "" # TODO: Your Customer Number (e.g. BTCS-CUS-123456)

timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
print("Timestamp: " + str(timestamp))

nonce = str(random.randrange(10_000_000_000_000_000_000, 100_000_000_000_000_000_000))
print("Nonce: " + str(nonce))

version = "v1"
schema = "https://"
url = "sandbox-api.btcsqa.net/trading/api/v3/Accounts"
queryString = ""
contentType = ""
payload = ""

message = "BTCS" + apiKey + url + queryString + contentType + nonce + timestamp + version + payload
print("Signature message: " + message)
signature = hmac.new(bytes(secret, 'ASCII'), bytes(message, 'utf-8'), hashlib.sha512)
signature = base64.b64encode(signature.digest()).decode()
print("Signature: " + signature)

headers = {
    "X-Auth": "BTCS " + apiKey,
    "X-Auth-Signature": signature,
    "X-Auth-Nonce": nonce,
    "X-Auth-Timestamp": timestamp,
    "X-Auth-Version": version,
    "accept": "*/*",
    "content-type": contentType,
    "customer-number": customerNumber
}

print("Request: " + schema + url)
response = requests.request("GET", schema + url, data=payload, headers=headers)

print(response, response.text)

Java Example

This example shows how to calculate the signature, add the required headers to a request and send it. It will print the response to the console.

This example relies on the Apache Commons Codec package.

Copy
Copied
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Random;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;

public class Program {

    public static void main(String[] args) {
        String customerNumber   = ""; // TODO: Your customer number (e.g. BTCS-CUS-000000)
        String query          = "";
        String path           = "/trading/api/account/getaccountstatement";
        String requestContent = "{\"messageType\":\"GetAccountStatement\","
                              + "\"accountNumber\":\"BTCS-ACC-BTC-795185\","
                              + "\"dateFrom\":\"2021-04-01T00:00:00Z\","
                              + "\"dateTo\":\"2021-05-04T00:00:00+02:00\"}";
        // sendRequest("POST", customerNumber, path, query, requestContent);

        // The Java HttpRequest struggles to handle empty content with content-length = 0
        // so better to send an empty json object
        path           = "/trading/api/instrument/getinstruments";
        requestContent = "{}";
        sendRequest("POST", customerNumber, path, query, requestContent);

        path           = "/auth/api/v1/apikey/getassociatedclients";
        requestContent = "";
        sendRequest("GET", "", path, query, requestContent);
    }

    static final String ApiKeyHeaderName         = "X-Auth";
    static final String ApiKeyHeaderParamName    = "BTCS";
    static final String SignatureHeaderName      = "X-Auth-Signature";
    static final String NonceHeaderName          = "X-Auth-Nonce";
    static final String TimeStampHeaderName      = "X-Auth-TimeStamp";
    static final String VersionHeaderName        = "X-Auth-Version";
    static final String Version                  = "v1";
    static final String CustomerNumberHeaderName = "customer-number";
    static final char[] ValidChars =
                        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
                        .toCharArray();
    static final Random random = new Random();

    public static String generate20CharsNonce() {
        // must be exactly 20 chars long, and unique from call to call
        int len = 20;
        StringBuilder sb = new StringBuilder(len);
        for (int i=0; i<len; i++) {
            char c = ValidChars[random.nextInt(ValidChars.length)];
            sb.append(c);
        }
        return sb.toString();
    }

    public static void sendRequest(
        String httpMethod,
        String customerNumber,
        String path,
        String query,
        String requestContent
    ) {
        String apiKey         = ""; // TODO: Your API Key
        String secret         = ""; // TODO: Your API Secret
        String host           = "sandbox-api.btcsqa.net";
        String contentType    = "application/json";
        String nonce          = generate20CharsNonce(); // must be exactly 20 chars long, and unique from call to call
        String version        = "v1";
        DateTimeFormatter dtf = DateTimeFormatter
                                    .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS")
                                    .withZone(ZoneId.from(ZoneOffset.UTC));
        Instant utcNow        = Instant.now();
        String timestamp      = dtf.format(utcNow) + "Z";

        String message = "BTCS"
                       + apiKey
                       + host
                       + path
                       + query
                       + contentType
                       + nonce
                       + timestamp
                       + version
                       + requestContent;

        System.out.println(message);

        byte[] key = secret.getBytes();
        HmacUtils hm256 = new HmacUtils(HmacAlgorithms.HMAC_SHA_512, key);
        byte[] rawHmac = hm256.hmac(message);
        String signature = new String(Base64.encodeBase64(rawHmac));

        System.out.println(signature);

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest.Builder rb = HttpRequest.newBuilder()
            .uri(URI.create("https://" + host + path + query))
            .header("X-Auth",           "BTCS " + apiKey)
            .header("X-Auth-Signature", signature)
            .header("X-Auth-Nonce",     nonce)
            .header("X-Auth-TimeStamp", timestamp)
            .header("X-Auth-Version",   version)
            .header("Content-Type",     contentType);
        if (!customerNumber.isEmpty())
            rb = rb.header("client-number", customerNumber);
        if (httpMethod.equals("GET"))
            rb = rb.GET();
        else if (httpMethod.equals("POST"))
            rb = rb.POST(HttpRequest.BodyPublishers.ofString(requestContent, StandardCharsets.UTF_8));
        HttpRequest request = rb.build();
        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() != 200)
                throw new RuntimeException("Status code: " + response.statusCode() + "\r\n" + response.body());

            System.out.println(response.body());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}