Sending HMAC SHA256 API signature in node.js

Hi All,

This is from my project “binusbotnodejs”, where I am experimenting with automating cryptocurrency trades on the Binance.us exchange.

I seem to be able to pull data from public endpoints without issue, and I was able to create a Google apps script in a Google Sheet that works with pulling from signed endpoints as well. But that script uses the Google Utilities service, which as far as I know is not an option outside of their proprietary applications.

I think that the “crypto” module in node.js is the right tool for the job in Glitch, but I’m a little stuck. I get an error {“code”:-2014,“msg”:“API-key format invalid.”} no matter what I do, even though the request is in the exact same format as what my Google apps script is sending. The only difference may be the hash, but if it’s an invalid signature I would expect a different error, to that effect.

I also tried several timestamp configurations from subtracting 24 hours to adding 24 hours, in case there is a time zone offset or something else that could be the cause. But the error is always the same, so that is not a likely culprit.

I have pasted the relevant code from my server.js file below. Any ideas? Thanks so much!

const fetch = require(“node-fetch”);
const crypto = require(“crypto”);
//pull keys from the env file, KEY is public key, SKEY is secret key
const key = process.env.BIN_US_API_KEY;
const secret = process.env.BIN_US_API_SKEY;

acctinfoendpoint(0);

async function getFetch(callstop, url) {
if (callstop == 1) {
return “API call stop”;
} else {
var response = await fetch(url);
var jsontext = await response.text();
return jsontext;
}
}

async function acctinfoendpoint(callstop) {
// callstop is a parameter that is set to 1 when setting up code to test, to prevent spamming the API
if (callstop == 1) {
return “API call stop”;
} else {

// getFetch function returns the json data from an API endpoint
// this endpoint returns the current server time in UNIX format; through trial and error I found that I need to subtract 30 seconds from the current time in order for the API to return a result using the Google apps script Utilities service
var timestamp =
JSON.parse(
await getFetch(callstop, “https://api.binance.us/api/v3/time”)
)[“serverTime”] - 30000;

//two parameters needed for this endpoint, per the Binance.us API docs at https://github.com/binance-us/binance-official-api-docs/blob/master/rest-api.md#account-information-user_data
var string = “recvWindow=60000&timestamp=” + timestamp;

//creating hash from combination of secret key and query string, per API docs
var sKey = crypto
.createHmac(“sha256”, secret)
.update(string)
.digest(“hex”);
//show the string in the console
console.log(string);
//show the hash in the console
console.log(sKey);
var params = {
method: “get”,
headers: { “X-MBX-APIKEY”: key },
muteHttpExceptions: true
};

// put the endpoint, string and signature together to send the signed request to the API
var endpointurl =
"https://api.binance.us/api/v3/account?" + string + “&signature=” + sKey;
//show the full API URL in the console
console.log(endpointurl);
var acctinfojson = await getFetch(callstop, endpointurl);
// show the json data returned in the console
console.log(acctinfojson);
}
}

Have you tried url encoding the key before you send it via a query parameter/string? You can url encode the key using encodeURIComponent(sKey).
Also see this MDN page for more information about encodeURIComponent.

I tried this a few different ways, enclosing the signature vs. the string vs. the whole URL, but still get the same error. I’ll keep working at it. But thanks for the tip!

1 Like