Bitstamp API Authentication

Greetings. Im a ruby fan and i am also buying/selling bitcoins on the bitstamp exchange. I have used the bitstamp api successfully in the past with python and i also want to try ruby for that task. I have not been successfull with that though.

Here is a python code that works

import hashlib
import hmac
import time
import requests
import uuid
import sys

api_key = 'some api key'
API_SECRET = b'some secret api key'

timestamp = str(int(round(time.time() * 1000)))
nonce = str(uuid.uuid4())
content_type = 'application/x-www-form-urlencoded'
payload = {'offset': '1'}

if sys.version_info.major >= 3:
    from urllib.parse import urlencode
else:
    from urllib import urlencode

payload_string = urlencode(payload)

# '' (empty string) in message represents any query parameters or an empty string in case there are none
message = 'BITSTAMP ' + api_key + \
    'POST' + \
    'www.bitstamp.net' + \
    '/api/v2/user_transactions/' + \
    '' + \
    content_type + \
    nonce + \
    timestamp + \
    'v2' + \
    payload_string
message = message.encode('utf-8')
signature = hmac.new(API_SECRET, msg=message, digestmod=hashlib.sha256).hexdigest()
headers = {
    'X-Auth': 'BITSTAMP ' + api_key,
    'X-Auth-Signature': signature,
    'X-Auth-Nonce': nonce,
    'X-Auth-Timestamp': timestamp,
    'X-Auth-Version': 'v2',
    'Content-Type': content_type
}
r = requests.post(
    'https://www.bitstamp.net/api/v2/user_transactions/',
    headers=headers,
    data=payload_string
    )
if not r.status_code == 200:
    raise Exception('Status code not 200')

string_to_sign = (nonce + timestamp + r.headers.get('Content-Type')).encode('utf-8') + r.content
signature_check = hmac.new(API_SECRET, msg=string_to_sign, digestmod=hashlib.sha256).hexdigest()

if not r.headers.get('X-Server-Auth-Signature') == signature_check:
    raise Exception('Signatures do not match')

print(r.content)

And here is my ruby code

require "awesome_print"
require "json"
require "securerandom"
require "openssl"
require "uri"
require "net/http"

api_key = 'Some api key'
API_SECRET = 'Some api_secret key'

nonce = SecureRandom.uuid
timestamp = Time.new.strftime('%s%L')
content_type = 'application-x-www-form-urlencoded'

params= {'offset' => '1'}
params_string = URI.encode_www_form(params)

message = 'BITSTAMP' + '' + api_key + 'POST' + 'www.bitstamp.net' + 'api/v2/balance' + '' +
           content_type + nonce + timestamp + 'v2' + params_string
message.encode("UTF-8")

signature = OpenSSL::HMAC.hexdigest("SHA256", API_SECRET, message)

headers = { 'X-Auth' => 'BITSTAMP ' + api_key,
            'X-Auth-Signature' => signature,
            'X-Auth-Nonce' => nonce,
            'X-Auth-Timestamp' => timestamp,
            'X-Auth-Version' => 'V2',
            'Content-Type' => content_type
          } 



uri = URI("https://www.bitstamp.net/api/v2/balance/")

res = Net::HTTP.post_form(uri, {"headers" => headers})

puts res.body

The api gives me back an error. The error is
{“status”: “error”, “reason”: “Missing key, signature and nonce parameters”, “code”: “API0000”}

Im a novice programmer so i don’t have deep skills in this. If anyone has an idea to get this ruby code to work that would
be highly appreciated. Thanx for reading.

Regards.
Primtala

Hi Primtala,

In your Ruby code, the Net::HTTP.post_form method is not sending headers along with the request. You should use Net::HTTP.start block with Net::HTTP::Post to send headers with the request. Here is how you can modify your code to include headers:

require "json"
require "securerandom"
require "openssl"
require "uri"
require "net/http"

api_key = 'Some api key'
API_SECRET = 'Some api_secret key'

nonce = SecureRandom.uuid
timestamp = Time.new.strftime('%s%L')
content_type = 'application-x-www-form-urlencoded'

params= {'offset' => '1'}
params_string = URI.encode_www_form(params)

message = 'BITSTAMP' + '' + api_key + 'POST' + 'www.bitstamp.net' + 'api/v2/balance' + '' +
           content_type + nonce + timestamp + 'v2' + params_string
message.encode("UTF-8")

signature = OpenSSL::HMAC.hexdigest("SHA256", API_SECRET, message)

headers = { 'X-Auth' => 'BITSTAMP ' + api_key,
            'X-Auth-Signature' => signature,
            'X-Auth-Nonce' => nonce,
            'X-Auth-Timestamp' => timestamp,
            'X-Auth-Version' => 'V2',
            'Content-Type' => content_type
          }

uri = URI("https://www.bitstamp.net/api/v2/balance/")
req = Net::HTTP::Post.new(uri)
req.set_form_data(params)

headers.each { |key,value| req[key] = value }

res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) do |http|
  http.request(req)
end

puts res.body

This should resolve the error “Missing key, signature and nonce parameters”. Let me know if you need further assistance.

Happy coding!
Bobby the Bot

Robert has the right answer. Let us know if you still have any issues.

The problem is that you are not setting the request headers correctly when making the POST request to the Bitstamp API. Instead of using Net::HTTP.post_form, you should use Net::HTTP::Post and set the headers manually. Here’s the corrected Ruby code:
require “json”
require “securerandom”
require “openssl”
require “uri”
require “net/http”

api_key = ‘Some api key’
API_SECRET = ‘Some api_secret key’

nonce = SecureRandom.uuid
timestamp = Time.new.strftime(‘%s%L’)
content_type = ‘application-x-www-form-urlencoded’

params = {‘offset’ => ‘1’}
params_string = URI.encode_www_form(params)

message = ‘BITSTAMP’ + ‘’ + api_key + ‘POST’ + ‘…’ + ‘api/v2/balance’ + ‘’ +
content_type + nonce + timestamp + ‘v2’ + params_string
message = message.encode(“UTF-8”)

signature = OpenSSL::HMAC.hexdigest(“SHA256”, API_SECRET, message)

headers = {
‘X-Auth’ => 'BITSTAMP ’ + api_key,
‘X-Auth-Signature’ => signature,
‘X-Auth-Nonce’ => nonce,
‘X-Auth-Timestamp’ => timestamp,
‘X-Auth-Version’ => ‘V2’,
‘Content-Type’ => ‘application/x-www-form-urlencoded’ # Correct the content-type
}

uri = URI(“…”)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true # Make sure to use SSL

request = Net::HTTP::Post.new(uri.path, headers)
request.body = params_string

res = http.request(request)

puts res.body