Transfer ETH to BTC via API in my own account

Description

I am attempting to use the Coinbase API to convert between ETH and BTC within the Coinbase account that I have. I am unable to successfully complete this conversion as I would expect via the API docs. I am receiving responses like this:

{}

This is incredibly easy to do via the mobile Coinbase app and online in the browser, however appears impossible based on my experience working with the API.

What I have tried

I have created an API key for the profile containing my ETH and BTC balances and grated it all permissions. I am able to successfully execute a GET request against the API and return info about my account. This aligns accurately with what is online and on the app. No issues there. Now, when I attempt a POST request as outlined in the documentation here I am unable to actually perform a successful transfer. On the app it is very easy to transfer between BTC/ETH or ETH/BTC but with the API there is no clear way to accomplish this.

Minimum Reproducible Example

I am using Python 3.9.2 with the following requirements installed:

certifi==2021.10.8
charset-normalizer==2.0.12
idna==3.3
requests==2.27.1
urllib3==1.26.9

and this being the minimum code example to reproduce the issue I am experiencing:

import base64
import hashlib
import hmac
import os
import time
from datetime import datetime

import requests
from requests.auth import AuthBase

class CoinbaseAuth(AuthBase):
    def __init__(self, api_key = None, secret_key = None):
        self.api_key = api_key or os.getenv("API_KEY")
        self.secret_key = secret_key or os.getenv("SECRET_KEY")

    def __call__(self, request):
        timestamp = str(int(time.time()))

        message = (
            timestamp
            + request.method
            + request.path_url
            + (request.body.decode("utf8") if request.body else "")
        )
        sk = self.secret_key.encode("utf8")
        hmac_signature = hmac.new(
            key=sk,
            msg=message.encode("utf8),
            digestmod=hashlib.sha256,
        )
        signature = hmac_signature.hexdigest()

        headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "CB-ACCESS-KEY": self.api_key,
            "CB-ACCESS-SIGN": signature,
            "CB-ACCESS-TIMESTAMP": timestamp,
            "CB-VERSION": str(datetime(2022, 1, 17).date()),
        }
        request.headers.update(headers)
        return request

auth = CoinbaseAuth(
    api_key='<my-api-key>',
    secret_key='<my-secret-key>',
)

accounts_response = requests.get("https://api.coinbase.com/v2/accounts", auth=auth)

accounts = accounts_response.json()

eth_wallet = {}
btc_wallet = {}
for account in accounts['data']:
    if account['currency']['code'] == 'BTC':
        btc_wallet = account
    elif account['currency']['code'] == 'ETH':
        eth_wallet = account

# I only have 1 profile, but one could iterate through to find the default
eth_account_id = eth_wallet.get("id", "ETH")
btc_account_id = btc_wallet.get("id", "BTC")



transfer_response = requests.post(
    f"https://api.coinbase.com/v2/accounts/{eth_account_id}/transactions/",
    auth=auth,
    json={
            "amount": "0.002".
            "currency": "ETH",
            "description": "Trading ETH to BTC from the API.",
            "idem": "testing123",
            "to": btc_account_id,
            "type": "transfer",
        },
)

print(f"Status Code: {transfer_response.status_code}")
print(f'Response Body: {transfer_response.content.decode("utf8")}')

and the above code snippet should output the following (because it does for me):

Status Code: 200
Response Body: {}

I have tried using the {"type": "send"} parameter as well instead of transfer, but that yields the following:

Status Code: 400
Response Body: {"errors":[{"id":"validation_error","message":"Please enter a valid email or ethereum address","field":"base"}]}

This response makes no sense, because I am literally feeding the account id values back into the API call that I received from the other API call, so they must be valid OR you mean different things by the account id than the id’s returned from a GET to /accounts and the id expected with a POST to /accounts/:account_id:/transactions/

NOTE

There is an incredibly similar issue over on the Exchange/API. I have tried to accomplish this simple task with both APIs and have not had success with either of them. Please advise what the correct way to do this would be, your documentation is incredibly unclear as to how one would accomplish this.

Hello @romanmc72, thank you for taking an interest in trying out Coinbase APIs. For the details regarding your concern, we will check on this for you with our team to see how we can best assist. We will get back to you once we have more information. Keep in touch!

1 Like