API Authentication

We offer two authentication options for the Coinbase API:

Method Best for
API Key + Secret Use to access your own Coinbase account via the API
OAuth2 Use to access the account of an existing Coinbase user who grants your application partial or full access.

Note: Users should never share API key + secret with anyone else.

API Key + Secret

Access your own account via the API

Getting started

First, create a new API key. Then, visit your account settings to verify the actions and account information you wish to be accessible via the API. See the permissions page to find a list of permissions and respective actions and information.

Note: All requests will need to include a application/json Content-Type header and go over SSL. The base URL is https://api.coinbase.com/v1/



GET /api/v1/account/balance HTTP/1.1

Accept: */*

User-Agent: Ruby




Connection: close

Host: coinbase.com


    "currency": "BTC"

The ACCESS_KEY header is simply your API key. The ACCESS_SIGNATURE header is a HMAC-SHA256 hash of the nonce concatentated with the full URL and body of the HTTP request, encoded using your API secret. The nonce is a positive integer number that must increase with every request you make. The nonce parameter can be included in one of three ways:

  • As a ACCESS_NONCE header in your requests
    • Example: ACCESS_NONCE: 1234
  • As a GET parameter in the URL
    • Example: https://api.coinbase.com/v1/account/balance?nonce=1234
  • As a root-level parameter in your JSON POST requests

This scheme is generally known as “HMAC authentication” (see wikipedia article). Here are some examples of code, making simple API calls:

import os
import hashlib
import hmac
import urllib2
import time
import json

#This code sample demonstrates a GET and a POST call to the Coinbase API
#Before implementation, set environmental variables with the names API_KEY and API_SECRET.

def make_request(url, body=None):
  opener = urllib2.build_opener()
  nonce = int(time.time() * 1e6)
  message = str(nonce) + url + ('' if body is None else body)
  signature = hmac.new(os.environ['API_SECRET'], message, hashlib.sha256).hexdigest()

  headers = {'ACCESS_KEY' : os.environ['API_KEY'],
             'ACCESS_SIGNATURE': signature,
             'ACCESS_NONCE': nonce,
             'Accept': 'application/json'}

  # If we are passing data, a POST request is made. Note that content_type is specified as json.
  if body:
    headers.update({'Content-Type': 'application/json'})
    req = urllib2.Request(url, data=body, headers=headers)

  # If body is nil, a GET request is made.
    req = urllib2.Request(url, headers=headers)

    return opener.open(req)
  except urllib2.HTTPError as e:
    print e
    return e

# Example of a GET request, where body is nil.
print make_request('https://api.coinbase.com/v1/account/balance').read()

# Example of a POST request.

# Required parameters for POST /api/v1/buttons
button_params = {
  'button': {
    'name' : 'test',
    'price_string' : '1.23',
    'price_currency_iso' : 'USD'

#POST /api/v1/buttons
print make_request('https://api.coinbase.com/v1/buttons', body=json.dumps(button_params)).read()
require 'openssl'
require 'rest_client'
require 'json'

# This code sample demonstrates a GET and a POST call to the Coinbase API
# Before implementation, set environmental variables with the names API_KEY and API_SECRET.


def make_request(url, body=nil)
  nonce = (Time.now.to_f * 1e6).to_i
  message = nonce.to_s + url + body.to_s
  signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), API_SECRET, message)
  headers = {"ACCESS_KEY" => API_KEY,
             "ACCESS_SIGNATURE" => signature,
             "ACCESS_NONCE" => nonce}
    # If body is nil, a GET request is made.
    if body.nil?
      RestClient.get(url, headers.merge({:accept => :json}))
      # If we are passing data, a POST request is made. Note that content_type is specified as json.
      RestClient.post(url, body, headers.merge({:content_type => :json, :accept => :json}))
  rescue => e
    puts e

# Example of a GET request, where body is nil.
puts make_request('https://api.coinbase.com/v1/account/balance')

# Example of a POST request.

# Required parameters for POST /api/v1/buttons
request = {
  :button => {
    :name => 'test',
    :price_string => '1.23',
    :price_currency_iso => 'USD',
    :variable_price => true

# POST /api/v1/buttons
puts make_request('https://api.coinbase.com/v1/buttons', request.to_json)
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Hex;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

public class CoinbaseExample {

    static String API_KEY = "API_KEY";

    static String API_SECRET = "API_SECRET";

    public static String getHttp(String url, String body)
            throws InvalidKeyException, NoSuchAlgorithmException,
            ClientProtocolException, IOException {

        String nonce = String.valueOf(System.currentTimeMillis());
        String message = nonce + url + (body != null ? body : "");

        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(API_SECRET.getBytes(), "HmacSHA256"));
        String signature = new String(Hex.encodeHex(mac.doFinal(message.getBytes())));

        HttpRequestBase request;
        if (body == null || body.length() == 0)
            request = new HttpGet(url);
        else {
            HttpPost post = new HttpPost(url);
            post.setEntity(new StringEntity(body));
            request = post;
        request.setHeader("ACCESS_KEY", API_KEY);
        request.setHeader("ACCESS_SIGNATURE", signature);
        request.setHeader("ACCESS_NONCE", nonce);

        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpResponse response = httpClient.execute(request);

        HttpEntity entity = response.getEntity();
        if (entity != null)
            return EntityUtils.toString(entity);
        return null;

    public static void main(String[] args) throws Exception {
        System.out.println(getHttp("https://api.coinbase.com/v1/account/balance", null));

Out-of-order Processing

Sometimes network traffic can cause near-simultaneous API calls to arrive out of order. Since the nonce must always be increasing, an API call with a higher nonce may arrive earlier than a separate call with a lower nonce if they are initiated very quickly together. When this happens, the 2nd call will be dropped as invalid. To address this, a separate expire parameter can be included, specifying a unix timestamp after which the call will not process.

When the expire parameter is included, the nonce is ignored. We recommend setting the expire parameter to no greater than 15 minutes in the future to prevent replay attempts.


GET https://coinbase.com/api/v1/account/balance?expire=1406139548

Create a new API Key (sign in required)

OAuth2 Authentication

Use this if your app needs to access other users’ accounts.

The OAuth2 protocol allows a Coinbase user to grant your application full or partial access to his/her account, without sharing the account’s API key or login credentials. It is a slightly more complex integration than the API Key authentication method, but is more flexible. OAuth2 works well for web applications, as well as desktop and mobile apps. There are good OAuth2 client libraries in most languages due to its widespread use by companies like Google and Facebook.


To get started you should create an OAuth2 application and obtain a client_id and client_secret. You will also be prompted to set a redirect_uri which is a url on your website. If you are developing a desktop or mobile application, see the section below on what redirect_uri to use. Note that, for security reasons, all redirect_uris will need to use SSL (i.e. begin with https://). URIs without SSL will be rejected as invalid.

OAuth2 authentication flow

OAuth2 requires the user to authorize your app on a browser page after logging into their account. To authenticate a user with OAuth2:

  • Use the client_id and client_secret you obtained during registration to generate an authorize_url and redirect the user to this url. You must include scope and optionally meta parameters to request a specific set of permissions.

  • If the user authorizes your app, they will be returned to the redirect_uri you set during registration (with a code param in the url).

  • Use the code param in the url to generate an access_token

  • Use this access_token to make API calls on the user’s behalf.

To secure your OAuth2 implementation, you should always use state parameter to prevent cross-site request forgery attempts.

You can define multiple valid redirect URIs in your application’s settings for different environments. You can optionally omit the redirect URI from /authorize url and the first URI will be used by default.


The following paths are used for requesting tokens (using a code, email and password, or refresh token):

GET    https://www.coinbase.com/oauth/authorize     display authorization form
POST   https://api.coinbase.com/oauth/token         endpoint for requesting the token, include a grant
                                                type of (authorization code, resource owner
                                                credentials, client credentials, or refresh token)

Here is a sample flow that your app might go through:

# Redirect the user to this page

# If the user accepts, they will be redirected to:

# Initiate a POST request to get the access token

# Response containing the 'access_token'
    "access_token": "...",
    "refresh_token": "...",
    "token_type": "bearer",
    "expire_in": 7200,
    "scope": "universal"

# Now you can use the 'access_token' to initiate authenticated requests

# Response
    "amount": "50.00000000",
    "currency": "BTC"

Access Tokens and Refresh Tokens

Coinbase uses an optional security feature of OAuth2 called refresh tokens. When you first authenticate, your app will be given an access_token and refresh_token. The access token is used to authenticate all your requests, but expires in two hours. Once an access token has expired, you will need to use the refresh token to obtain a new access token and refresh token. The refresh token never expires but it can only be exchanged once for a new set of access and refresh tokens. If you try to make a call with an expired access token, 401 response will be returned.

This complicates the integration, but provides an additional layer of security since a compromised access token is automatically revoked after two hours.

To get a new access token, you’ll need to do a POST request to /oauth/token like before, but this time include your refresh_token and change the grant_type to refresh_token. Also, note that the code parameter will not be needed in this request.

POST https://api.coinbase.com/oauth/token?

The expected result is a response containing the access token, as before:


If you are using an OAuth2 library that supports refresh tokens, you won’t need to worry about this as the library will most likely take care of this for you.

Revoking an access token

Access tokens can be revoked manually if you want to disconnect your application’s access to user’s account. Revoking can also be used to implement logging out feature. You’ll need to supply the current access token to do so:

POST https://api.coinbase.com/oauth/revoke?

State Variable

To help protect against cross-site request forgery (CSRF), it is recommended to included a state GET parameter during the OAuth2 authorization process. Verifying that this variable matches upon receipt of an authorization code will mitigate any CSRF attempts.

An example of a request with state is as follows:


Once user has authorized your application, the same state param is passed back to redirect url with code param. You can read more about it here.

Developing Mobile And Desktop Apps

If you’re developing an installed application, you may not have a redirect url. In these cases you can set the redirect url to this special value: urn:ietf:wg:oauth:2.0:oob. This tells our servers to render a blank page with the authorization code in the url and title of the page, which can be parsed by your application.

This uses the same pattern that Google follows in their documentation. Here’s a great guide with sample code for both iOS and Android.

Both sign up and login inside OAuth2 flow require that the user opens a link from their email when email or new device has to be confirmed. To make it easier to guide user back to the native mobile application, a special verifications deeplink can be specified as part of your OAuth application’s advanced settings. This link will be opened automatically on successful verification.

Security Best Practices

Storing Credentials Securely

You should take great care to ensure your credentials are stored securely. If someone obtains your api_secret or an access_token with the send permission, they will be able to send all the bitcoin out of your account.

You should avoid storing API keys in your code base (which gets added to version control). Best practice is to store them in environment variables and you can learn more it here. Separating credentials from your code base and database is a good practice.

API key access is turned off by default on all accounts. So if you decide to do an API key integration, you will need to enable it first and take the necessary steps from then on to store it securely. You can always regenerate your API key (or disable it) if you feel it has been compromised.

OAuth2 access tokens and refresh tokens should be stored encrypted with encryption key stored in environment variables. To increase the security of your OAuth2 implementation, you should always implement state parameter (read more), request moderate send limits and implement 2FA authentication.

Validating SSL Certificates

It is also very important that your application validates our SSL certificate when it connects over https. This helps prevent a man in the middle attack. If you are using a client library, this may be turned on by default, but you should confirm this. If you see a setting to ‘verify SSL’ you should always ensure it is set to true.

Additional Security for API Keys

If you need enhanced security with your API Keys, we recommend that you whitelist IP addresses that are permitted to make requests with a particular API Key.

You can specify IP addresses to whitelist when creating a new API Key or editing an existing one.