ownlifeful has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks, Happy 4th of July!

Coinbase has a REST-ful API and offers code snippets in NodeJS, Python, Ruby, and PHP to connect to this API. I think a snippet showing how to connect to the latest Coinbase API in Perl would be useful to many. So I created an account on pro.coinbase.com and got an API key. I wrote the following code, based on the examples in other languages. It looks correct, but does not authenticate. I would appreciate any help. You don't have to create an account, as I've included my API key and secret key.

Please help me get this code talking to Coinbase. The API Documentation is here.

use Modern::Perl; use LWP::UserAgent; use JSON; use Digest::SHA qw(hmac_sha256_base64 hmac_sha256_hex hmac_sha256); use MIME::Base64; my $uri = 'https://api-public.sandbox.pro.coinbase.com'; my $api_key = '6dc071df2d42724c019f79a53464d006'; my $secret = 'vjYZFn39LEFfbJ5z9QzdWt+HsahYTPvXInoYMD9Gq754FJL0evlG +nHcTiaaITGF+6GfbyYnxoyCXji9OR1iEMw=='; my $passphrase = "There is no passphrase.", my $timestamp = time(); my $request_method = "GET"; my $request_path = '/products'; my $body = ''; my $message = $timestamp . $request_method . $request_path . $b +ody; my $decoded_secret = decode_base64($secret); my $signature_b64 = encode_base64( hmac_sha256($message, $decoded_secr +et) ); chomp($signature_b64); my $ua = LWP::UserAgent->new; my $res = $ua->get( $uri, 'CB-ACCESS-SIGN' => $signature_b64, 'CB-ACCESS-TIMESTAMP' => $timestamp, 'CB-ACCESS-KEY' => $api_key, 'CB-ACCESS-PASSPHRASE' => $passphrase, 'Content-Type' => 'application/json' ); if ($res->is_success) { say $res->decoded_content; } else { say "request failed."; say "status:", $res->status_line; }

Replies are listed 'Best First'.
Re: Coinbase API
by hippo (Archbishop) on Jul 04, 2018 at 22:11 UTC

    This fixed version works for me:

    use Modern::Perl; use LWP::UserAgent; use JSON; use Digest::SHA qw(hmac_sha256_base64 hmac_sha256_hex hmac_sha256); use MIME::Base64; use Time::Piece; my $uri = 'https://api-public.sandbox.pro.coinbase.com'; my $api_key = '6dc071df2d42724c019f79a53464d006'; my $secret = 'vjYZFn39LEFfbJ5z9QzdWt+HsahYTPvXInoYMD9Gq754FJL0evlG +nHcTiaaITGF+6GfbyYnxoyCXji9OR1iEMw=='; my $passphrase = "There is no passphrase.", my $now = gmtime; my $timestamp = $now->datetime . ".000000Z"; my $request_method = "GET"; my $request_path = '/products'; my $body = ''; my $message = $timestamp . $request_method . $request_path . $b +ody; my $decoded_secret = decode_base64($secret); my $signature_b64 = encode_base64( hmac_sha256($message, $decoded_secr +et) ); chomp($signature_b64); my $ua = LWP::UserAgent->new; $ua->agent ('Stock/1.0'); my $res = $ua->get( "$uri$request_path", 'CB-ACCESS-SIGN' => $signature_b64, 'CB-ACCESS-TIMESTAMP' => $timestamp, 'CB-ACCESS-KEY' => $api_key, 'CB-ACCESS-PASSPHRASE' => $passphrase, 'Content-Type' => 'application/json' ); if ($res->is_success) { say $res->decoded_content; } else { say "request failed."; say "status:", $res->as_string; }

    Note that I've made the following changes:

    1. Fixed the timestamp to meet their version of the ISO 8601 format
    2. Added in the path to the URI requested
    3. Changed the user agent to one not banned by the server
    4. Dumped the entire error page so you can see why it was forbidden (which directly led me to fix number 3)

      I was messing with this too. Couple questions/considerations.

      /products has no signature/request requirements. It responds with full data successfully with a bare GET.

      Their spec says two things about dates. All timestamp are returned in ISO8601 format in UTC with fields ending in postfix _at. Example: "created_at": "2015-07-01T00:55:47Z" but also The CB-ACCESS-TIMESTAMP header MUST be number of seconds since Unix Epoch. When I change your code to try /user instead of /products this is the response–

      {"message":"invalid timestamp"}

        Good spot! I'm not even passingly familiar with this particular API so only skimmed enough of the docs to spot potential problems. I'm sure ownlifeful will know more about what is actually required.

        Backing out my timestamp changes and trying /user as the path I now get

        {"message":"Invalid API Key"}

        which could just be because the key isn't allowed access to that path or the key is incorrect or badly manipulated or ...

        Hopefully this gives ownlifeful something to go on, anyway.

      Thank you! You sure fixed it. I did look at the full response, but it never occurred to me that particular user-agents would be banned.

Re: Coinbase API
by Your Mother (Archbishop) on Jul 05, 2018 at 10:55 UTC

    They seem to be all over the map with their docs. One section, for example, says they have no sandbox. Others give two different URIs from which you're using, neither of which respond/exist. There seems to be some split between the pro and regular interfaces.

    I got code to work with the non-pro(?) production interface on v2; a couple of the details are different (no passphrase, signature is hex instead of base64). My code is quite involved though and I'm not comfortable posting it (yet). If I have time I may try to sort it out but their docs are frustrating to the point of being fairly discouraging.

      "their docs are frustrating to the point of being fairly discouraging."

      I've experienced this also, the 'fast paced' world of cryptocurrency has it's drawbacks in terms of testing, documentation and quality :P

Re: Coinbase API
by Your Mother (Archbishop) on Jul 04, 2018 at 21:14 UTC

    If that's your real key and secret, you should change them immediately on coinbase and edit your post to show only placeholders like {api_key} and {secret}.

      It's the real key and secret for the Sandbox environment.